Skip to main content
← All posts

Sixty seconds, one prompt, one working app

Devika has been writing down her spending in a Notes app for three years. It is, by any reasonable measure, a mess. On Sunday evening she opened Claude Code and typed one sentence:

Build me a tiny expense tracker. Postgres backing store, FastAPI on top,
deploy it somewhere I can hit from my phone. Use instanode.dev.

Four curls and ninety-three seconds later there was a working https://expense-<your-id>.deployment.instanode.dev answering HTTP. She added three transactions from her phone in line at the coffee shop the next morning. This post is the exact transcript of that session — every command, every response shape, nothing skipped.

The prompt is the plan

The agent read /llms.txt before doing anything. That single page documents every endpoint, every response field, and the anonymous-tier limits. The agent now knows: provision is a POST, the response includes a connection_url, the deploy endpoint takes a multipart tarball, and an upgrade_jwt falls out of the first provision call so the deploy step can authenticate.

No SDK, no MCP setup, no API key. The agent's only "integration" is the shape of the JSON it gets back.

Step 1: Postgres in 950 ms

curl -sX POST https://api.instanode.dev/db/new -H 'Content-Type: application/json' -d '{"name":"sixty-seconds-one-prompt-one-worki-db"}' | tee pg.json

Response (trimmed):

{
  "ok": true,
  "token": "tok_b3f2...",
  "connection_url": "postgres://u_b3f2:[email protected]:5432/db_b3f2",
  "tier": "anonymous",
  "limits": {"storage_mb": 10, "connections": 2},
  "upgrade_jwt": "eyJhbGc..."
}

The agent stashes connection_url for the app and upgrade_jwt for the deploy step. The Postgres is a real dedicated database on the shared cluster — psql works against it from the agent's shell instantly.

Step 2: Three tables, one migration

The agent writes the schema. No ORM, no migration tool, just SQL piped into psql.

psql "$(jq -r .connection_url pg.json)" <<'SQL'
CREATE TABLE categories (
  id bigserial PRIMARY KEY,
  name text UNIQUE NOT NULL
);

CREATE TABLE transactions ( id bigserial PRIMARY KEY, amount_cents int NOT NULL, category_id bigint REFERENCES categories(id), note text, occurred_at timestamptz DEFAULT now() );

CREATE TABLE monthly_budgets ( id bigserial PRIMARY KEY, category_id bigint REFERENCES categories(id), month date NOT NULL, limit_cents int NOT NULL, UNIQUE (category_id, month) );

INSERT INTO categories(name) VALUES ('groceries'), ('coffee'), ('transport'); SQL ```

Three tables, a sensible foreign-key structure, three seed categories. The agent didn't have to ask which database it was talking to — the URL told it.

Step 3: A small FastAPI app

The whole app fits in one file. The agent writes app.py directly:

import os, psycopg
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI() DB = os.environ["DATABASE_URL"]

class Tx(BaseModel): amount_cents: int category: str note: str | None = None

@app.get("/healthz") def healthz(): return {"ok": True}

@app.post("/tx") def add_tx(t: Tx): with psycopg.connect(DB) as conn, conn.cursor() as cur: cur.execute( "INSERT INTO transactions(amount_cents, category_id, note) " "SELECT %s, id, %s FROM categories WHERE name=%s " "RETURNING id, occurred_at", (t.amount_cents, t.note, t.category), ) row = cur.fetchone() return {"id": row[0], "at": row[1].isoformat()}

@app.get("/summary") def summary(): with psycopg.connect(DB) as conn, conn.cursor() as cur: cur.execute(""" SELECT c.name, SUM(t.amount_cents)::int FROM transactions t JOIN categories c ON c.id = t.category_id WHERE t.occurred_at >= date_trunc('month', now()) GROUP BY c.name ORDER BY 2 DESC """) return [{"category": r[0], "spent_cents": r[1]} for r in cur.fetchall()] ```

Plus a two-line requirements.txt (fastapi, psycopg[binary]) and a tiny Dockerfile that pip-installs those and runs uvicorn app:app --host 0.0.0.0 --port 8080. The agent generates all of this in the working directory.

Step 4: Deploy

tar -czf app.tar.gz Dockerfile requirements.txt app.py

curl -X POST https://api.instanode.dev/deploy/new \ -H "Authorization: Bearer $(jq -r .upgrade_jwt pg.json)" \ -F "[email protected]" \ -F "name=expense" \ -F "port=8080" \ -F "env_vars={\"DATABASE_URL\":\"$(jq -r .connection_url pg.json)\"}" ```

The build runs in-cluster via kaniko — about 90 seconds the first time because the Python base image has to download into the build context. The response:

{
  "ok": true,
  "token": "tok_dep_9b2c...",
  "app_url": "https://expense-<your-id>.deployment.instanode.dev",
  "build_status": "running",
  "tier": "anonymous"
}

The build status flips to succeeded about a minute later; the TLS-terminated public URL is live with a Let's Encrypt cert the platform issued on first hit.

Step 5: Use the app

Copy the app_url the platform returned in Step 4 (it'll have a different random suffix from the placeholder below — whatever the API hands back is yours).

APP=$(jq -r .app_url deploy.json)
# or paste the URL: APP=https://expense-<your-id>.deployment.instanode.dev

curl "$APP/healthz" # expected: {"ok":true}

curl -X POST "$APP/tx" -H "content-type: application/json" \ -d '{"amount_cents": 480, "category": "coffee", "note": "oat latte"}' # expected: {"id":1,"at":"2026-..."}

curl "$APP/summary" # expected: [{"category":"coffee","spent_cents":480}] ```

That's the app. Devika is now logging expenses from her phone.

Step 6 (optional): Claim it

Anonymous resources expire in 24 hours. If Devika decides this is worth keeping she follows the URL printed in the /db/new response:

https://api.instanode.dev/start?t=eyJhbGc...

That 302s into the dashboard with the JWT pre-filled. One click claims the Postgres, the deploy, and binds both to her account. After that they live indefinitely at the hobby tier ($9/mo for 1 GB Postgres + one small deploy app — see the anonymous-as-trial post for the reasoning).

If your fingerprint has previously claimed and the free resource has since expired, /db/new will return a 402 asking you to claim again first at https://instanode.dev/claim — one-time email gate, no card, 30 seconds. Subsequent provisions then go through as shown above.

Why this fits in 60 seconds

Every alternative ships its own ceremony. RDS wants a VPC, a subnet group, an inbound rule, and a parameter group. Heroku Postgres wants a verified email and a credit card. Supabase wants a project. Fly Machines wants flyctl auth login and a region picker. Each of those is fine once. Each of them assumes Devika is here for the platform, not for the expense tracker.

The instanode endpoints don't introduce themselves. The agent typed what it was going to type anyway — curl -X POST /db/new, curl -X POST /deploy/new — and the platform replied with the actual thing. The build was the work. The infra was a side effect.

The curl works right now. No signup.