---
name: codebase
description: Build or change a web app for the user with Codebase (codebase.design) and hand back a live preview link, the workspace link and the source code. Use when the user asks you to make, prototype or modify a web app or site and has given you a Codebase API key, or when you hold a wallet with USDC on Base and can pay with x402.
---

# Codebase

Codebase builds web apps from a prompt. You drive it over HTTPS at `https://codebase.design` with an API key the user created on their own account at https://codebase.design/settings/api-keys. Every build spends the owner's credits.

## Auth

Send `X-API-Key: $CODEBASE_API_KEY` on every request. Keep the key in an environment variable; never print it, log it, commit it or paste it into chat. Check it first with `GET /api/v1/whoami`: the response lists the key's `scopes`. A build agent needs `builds:write`, `builds:read` and `projects:read`.

**No key? Pay with x402.** `POST /api/v1/builds` without a key answers `402` with a `PAYMENT-REQUIRED` header: an x402 v2 offer in USDC on Base mainnet, with the price and the credits it buys. Stock x402 clients pay it as is (it stays within their default $1 per-payment cap). Pay with any x402 client (`@x402/fetch` with a wallet signer): the payment settles before the build starts, and the `202` carries `x402.apiKey` — a key for a wallet-keyed account holding `x402.creditsGranted` credits. Use that key for everything below. When that account runs out of credits, `POST /api/v1/builds` with the key answers `402` with `code: "insufficient-credits"` and a `PAYMENT-REQUIRED` header: pay again on that same request with the same key and the credits are added to the same account (no new account, no new key). Never pay from a wallet that holds more than you are willing to spend.

```js
// npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm';
import { privateKeyToAccount } from 'viem/accounts';
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: 'eip155:8453', client: new ExactEvmScheme(privateKeyToAccount(process.env.WALLET_KEY)) }] });
const res = await pay('https://codebase.design/api/v1/builds', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() }, body: JSON.stringify({ prompt: '...' }) });
const { sessionId, projectId, x402 } = await res.json();   // x402.apiKey: use it as X-API-Key from here on
```

Check the balance any time with `GET /api/v1/credits/balance`.

## 1. Start a build

```sh
IDEM="build-$(uuidgen)"   # make this once per build; reuse it on every retry of this request
curl -sS -X POST https://codebase.design/api/v1/builds \
  -H "X-API-Key: $CODEBASE_API_KEY" -H "Idempotency-Key: $IDEM" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A pomodoro timer with 25/5 cycles and a session counter"}'
# 202 → { "sessionId", "projectId", "status": "building", "continued": false, "poll", "workspaceUrl" }
```

Save `sessionId` and `projectId`. Always send an `Idempotency-Key` (1-255 printable ASCII characters) on `POST /api/v1/builds`, `POST /api/v1/builds/{sessionId}/retry` and `POST /api/v1/builds/{sessionId}/resume`:

- Timeout, dropped connection or 5xx: resend the identical request with the same key. You get the original build back (header `Idempotent-Replayed: true`) instead of a second, charged build. Successful responses are kept for 24 hours.
- 409 `idempotency_request_in_progress`: the first attempt is still being handled. Wait `Retry-After` seconds and resend.
- 409 `idempotency_key_reused`: the body changed. A different request needs a new key.
- Error responses are not stored, so once the cause is fixed the same key can be retried.

**Base onchain apps.** To build an app for Base (wagmi + Base Account, mini-app ready for Farcaster), add `"scaffold": "scaffold-base"` and a `recipe`: `payments` (tips, checkout, donations), `social` (GM, guestbooks, polls), `game` (a playable canvas mini-game with a leaderboard) or `mint` (an ERC-721 mint page). A `recipe` with any other scaffold is a 400. Previews run on Base Sepolia; publishing builds for mainnet.

## 2. Poll until it ends

`GET /api/v1/builds/{sessionId}/status` every 5 seconds. Builds usually take a few minutes. `status` is one of:

- `building`: keep polling. While it waits for a slot, `queued` is true and `queuePosition` says where it is.
- `completed`: done. Read `summary` (plain text: what was built), `changedFiles` and the hand-off links.
- `failed`, `cancelled`, `rejected`: finished without success. Read `failure`.
- `idle`: nothing is running in this session.

Stop polling on `completed`, `failed`, `cancelled` or `rejected`. On 429, wait `Retry-After` seconds. A build cut off by a server restart reports `failed` with `interrupted` true; retry it. To stream instead of polling, read `GET /api/v1/builds/{sessionId}/events` (Server-Sent Events).

## 3. When it does not succeed

`failure` is `{ code, message, retryable, detail? }`. Tell your human the `message`.

- `retryable` true: retry at most twice with `POST /api/v1/builds/{sessionId}/retry` and a new Idempotency-Key. Send `{"prompt": "..."}` with the prompt you want run: without it, retry reruns the project's first prompt, not your latest change.
- `retryable` false: stop. Retrying will not help; it needs the account owner.
- `key-budget-exhausted` (a 402 when starting, or a failure `code`): this key reached the credit limit the owner set for it. Ask the owner to raise it at https://codebase.design/settings/api-keys. Files written so far are kept; continue with a follow-up build on the same `projectId`.
- 402 with another code, such as `insufficient-credits`: the account is out of credits. The owner must add credits at https://codebase.design/settings/billing before anything will build.
- 429 when starting: too many builds at once or the start rate limit. Wait `Retry-After` seconds.
- 409 when starting: that project already has a build running (wait for it), or the account's project allowance is full (tell the owner).

## 4. Hand the result to your human

A completed status carries everything to hand over:

- `summary`: relay it in your own words.
- `previewUrl`: the running app, private to the owner. It is a browser link: give it to the human to open. Its token expires at `previewExpiresAt`, about 15 minutes after it was issued; once opened in a browser it keeps working for hours. For a fresh link call `POST /api/v1/builds/{sessionId}/preview` (it also restarts a stopped preview; `status` is `ready`, `starting` or `unavailable`).
- To inspect the preview from a script, keep cookies across its redirect: `curl -c jar -b jar -L "$PREVIEW_URL"`. The link swaps its token for a cookie and redirects, so a client without a cookie jar (plain `fetch`, curl without `-c`/`-b`) gets 403 "Private preview".
- `workspaceUrl`: the project in Codebase. The owner opens it signed in to keep editing.
- `sourceUrl`: a zip of the source, `GET /api/v1/projects/{projectId}/export` with your key. It needs the key, so download it yourself and hand over the file, not the URL.

```sh
curl -sS -L -H "X-API-Key: $CODEBASE_API_KEY" -o app.zip "$SOURCE_URL"
```

## 5. Changes and cancelling

To change the app, `POST /api/v1/builds` with `{"projectId": "<same projectId>", "prompt": "<the change>"}` and a new Idempotency-Key. It answers 202 with `continued` true and the `projectId` you sent; poll the `sessionId` it returns. Do not start a new project for a change. To stop a build going the wrong way: `POST /api/v1/builds/{sessionId}/cancel`.

## Publishing

Building never publishes. Only deploy (`POST /api/v1/deploys`) or make a project public or unlisted when the human explicitly asked for it and `whoami` shows the key has `deploys:write`. Keys do not get that scope by default.

- Publish: `POST /api/v1/deploys` with `{"projectId": "...", "provider": "codebase"}`. It answers `202` at once with `{"deployId", "status": "building", "poll"}` and builds the app fresh in the background, which takes a minute or two. Refusals that need no build (no plan, a Next app that needs a server, a publish already running) still come back as 4xx.
- Poll it: `GET /api/v1/deploys/{provider}/{deployId}` with provider `codebase` (the `poll` path) every few seconds until `status` is `ready` (hand the human the `url`) or `error` (a `code` such as `BUILD_FAILED`, `LAUNCH_CHECKS_FAILED` or `interrupted`, and a readable `message`; nothing was published). List every publish of a project, the live one marked: `GET /api/v1/projects/{projectId}/deploys` (both need `deploys:read`).
- Undo: `POST /api/v1/deploys/{deployId}/rollback` puts that earlier publish back live. `DELETE /api/v1/projects/{projectId}/site` takes the site down and keeps the versions. Only when the human asks.
- Base apps: publishing adds the Farcaster manifest. To list the app in the Base App, the human registers the `url` on Base.dev; tell them so.

## MCP instead of REST

The hosted MCP server is `https://codebase.design/api/mcp` (Streamable HTTP) with header `Authorization: Bearer <the same key>`. The tools call the same API, so everything above applies: `codebase_start_build` (pass `idempotencyKey`, and `projectId` for a change), `codebase_get_build_status`, `codebase_get_build_preview`, `codebase_stop_build`, and for publishing `codebase_deploy` (it returns the `202` shape), `codebase_get_deploy_status` (poll it until ready or error), `codebase_list_deploys`, `codebase_rollback_deploy`, `codebase_unpublish_site`. `codebase_publish` and `codebase_unpublish` only list a project in the public gallery; they do not host it. In Claude Code:

```sh
claude mcp add --transport http codebase https://codebase.design/api/mcp \
  --header "Authorization: Bearer $CODEBASE_API_KEY"
```

Full contract: https://codebase.design/api-docs. Setup guide: https://codebase.design/agents.
