<>
{}
Build73
Home

Build73 Project API

REST + hosted MCP for Cursor and other agents. Project-scoped credentials only.

Build73 Project API

Project-scoped automation API for Cursor and other agents. Same capabilities are exposed as:

  • RESThttps://api.build73.com/api/v1/projects/{projectId}/…
  • Hosted MCPhttps://api.build73.com/api/mcp/projects/{projectId}

Both call the same ProjectAutomationGateway. Credentials never allow deleting the project, managing members/share links, or billing/subscription.

ArtifactLocation
OpenAPI (static)`docs/project-api-openapi.json`
OpenAPI (live)GET https://api.build73.com/api/v1/openapi.json
Agent bundle`docs/project-api-agent-bundle/`
Tool/REST parityapps/api/src/modules/project-api/project-api.manifest.ts

Feature flag: project_external_api. Kill switch: PROJECT_EXTERNAL_API_ENABLED=false.


Authentication

All Project API requests require:

Authorization: Bearer <token>

Recommended: Project API key (Cursor + REST)

Format: b73_proj_<keyId>_<secret>

1. Open your project in the Build73 editor → Share → API & agents. 2. Create API key — the full key is shown once. 3. Copy the ready MCP config (includes project URL + key) into Cursor Settings → MCP or .cursor/mcp.json. 4. In Cursor chat: *"Use Build73 MCP for this project. Start with project_status."*

The key is bound to one projectId. Default expiry 30 days, max 90 days. Caps at EDITOR role; scopes narrow access further.

MCP config (what the editor generates after key creation):

{
  "mcpServers": {
    "build73-xxxxxxxx": {
      "url": "https://api.build73.com/api/mcp/projects/YOUR_PROJECT_ID",
      "headers": {
        "Authorization": "Bearer b73_proj_…"
      }
    }
  }
}

REST check:

curl -H "Authorization: Bearer b73_proj_…" \
  "https://api.build73.com/api/v1/projects/YOUR_PROJECT_ID/status"

Create a key programmatically (owner JWT session, not a project key):

curl -X POST "https://api.build73.com/api/projects/{projectId}/api-access/keys" \
  -H "Authorization: Bearer <EDITOR_JWT>" \
  -H "Content-Type: application/json" \
  -d '{"name":"ci-agent","scopes":["project:read","files:write","assets:write","versions:write"],"expiresInDays":30}'

Response includes fullKey once. Never commit it.

Alternative: OAuth 2.1 + PKCE (Cursor, no API key)

If you prefer browser login instead of storing a key, use OAuth MCP config (no Authorization header):

{
  "mcpServers": {
    "build73": {
      "url": "https://api.build73.com/api/mcp/projects/YOUR_PROJECT_ID"
    }
  }
}

Cursor discovers OAuth and opens consent on first use.

EndpointURL
MCP resourcehttps://api.build73.com/api/mcp/projects/{projectId}
Protected resource metadatahttps://api.build73.com/api/.well-known/oauth-protected-resource/mcp/projects/{projectId}
Authorization server metadatahttps://api.build73.com/api/project-api/oauth/.well-known/oauth-authorization-server
Authorizehttps://api.build73.com/api/project-api/oauth/authorize
Tokenhttps://api.build73.com/api/project-api/oauth/token
Revokehttps://api.build73.com/api/project-api/oauth/revoke
  • Public client id for Cursor: cursor-mcp
  • Grant: authorization code + PKCE S256 only
  • Access token prefix: b73_pat_… (short-lived, ~1h)
  • Refresh token prefix: b73_prt_… (rotating)
  • Tokens are project-bound; resource / project_id selects the project at consent time

Scopes

Scopes narrow the underlying project ACL; they never widen it. project:read is always implied. Effective permission = min(project role, credential scopes).

ScopeAllows
project:readStatus, files, assets, versions, jobs (read)
files:writeCreate / update / delete code files
assets:writeReplace asset metadata (not pipeline-owned keys)
ai:generateAI generation (charges credits)
versions:writeCreate / restore versions
build:runExport / build jobs and cancel (charges credits)

Default scopes on new keys (if omitted): project:read, files:write, assets:write, versions:write.

VIEWER project members cannot hold write scopes even if requested.

Never granted via Project API: delete project, manage members/links, billing, subscription, ownership transfer.


REST endpoints

Base: https://api.build73.com/api/v1/projects/{projectId}

MethodPathScopeNotes
GET/statusproject:readPlan, credits, capabilities, prices — call first
GET/filesproject:readList; ?path=&cursor=&limit=
GET/files?q=project:readContent search (q ≥ 2 chars)
GET/files/{fileId}project:readContent + contentRevision
POST/filesfiles:writeBody { path, content }
PUT/files/{fileId}files:writeBody { content, comment?, expectedRevision? }
DELETE/files/{fileId}files:writePrefer checkpoint first
GET/assetsproject:read`?type=image\audio\video\model3d\font\other`
PUT/assets/{assetId}/metaassets:write{ name?, metadata?, expectedRevision? }
GET/versionsproject:read?cursor=&limit=&search=
POST/versionsversions:writeCheckpoint { name, description? }
POST/versions/{versionId}/restoreversions:write{ backupName? }
POST/exportbuild:run202 + job; { format?, note? }
GET/jobs/{jobId}project:readPoll job + credits
POST/jobs/{jobId}/cancelbuild:runCancel non-terminal job

MCP tool names (parity): project_status, files_list, files_get, files_search, files_write, files_create, files_delete, assets_list, assets_update_meta, versions_list, versions_create, versions_restore, export_start, jobs_get, jobs_cancel.

MCP resources: build73://project/status, build73://project/capabilities, build73://project/billing.


Idempotency

Every mutating REST call requires header:

Idempotency-Key: <client-unique-string>

Rules:

  • Max length 255
  • Scoped per credential (api_key:… / oauth:…)
  • Same key + same body → replay stored success response
  • Same key + different body → 409 conflict
  • In-flight duplicate → 409 conflict
  • Failed attempts do not consume the key (safe to retry)
  • TTL ≈ 24 hours

MCP mutations use the same gateway semantics. The tool layer derives a stable key from the JSON-RPC request id. Paid ai_generate_image and ai_generate_audio calls additionally require an idempotencyKey argument: generate one key per intended asset and reuse it when retrying after a timeout.


Optimistic concurrency (expectedRevision)

Project content carries a monotonic contentRevision.

  • GET /files/{fileId} returns contentRevision
  • PUT file / asset meta may include expectedRevision
  • If the project changed since you read it → `409 conflict`

Recommended flow:

1. GET /status (and file reads) → note contentRevision 2. POST /versions checkpoint before large edits 3. Mutate with expectedRevision 4. On 409, re-read and reconcile


Jobs

Heavy work (export) returns HTTP 202 with a job object:

{
  "id": "…",
  "projectId": "…",
  "type": "export",
  "status": "queued",
  "progress": 0,
  "result": null,
  "error": null,
  "credits": { "held": 0, "charged": 0, "refunded": 0 },
  "createdAt": "…",
  "updatedAt": "…",
  "finishedAt": null
}

Statuses: queuedrunningcompleted | failed | cancelled.

Poll GET /jobs/{jobId}. Cancel with POST /jobs/{jobId}/cancel (requires Idempotency-Key).


Credits & status

GET /status (MCP: project_status) returns everything an agent needs to budget:

  • project — id, name, contentRevision, counts
  • credential — type, role, scopes
  • plan — tier, name, status (no payment methods / customer ids)
  • credits — total / free / purchased / bonus / subscription
  • capabilities — booleans for read/write/AI/build after scopes + feature flags
  • actionPrices — credit cost hints for version create, export, AI actions, etc.
  • rateLimits — e.g. 5000 req / 60s (exports also plan-limited)

AI / export operations charge the key/grant owner’s credits. Check credits and actionPrices before expensive calls. Job responses include credits.held|charged|refunded.


Errors

Stable envelope on all /api/v1 routes:

{
  "error": {
    "code": "conflict",
    "message": "…",
    "status": 409,
    "requestId": "optional-from-X-Request-Id",
    "details": {}
  }
}
HTTPcode
400invalid_request
401unauthorized
403forbidden
404not_found
409conflict
413payload_too_large
422unprocessable_entity
429rate_limited
500internal_error
503service_unavailable

Pass X-Request-Id to correlate with audit logs. Unauthorized MCP/REST challenges include WWW-Authenticate with protected-resource metadata for OAuth discovery.


Examples

curl — status

export BUILD73_PROJECT_TOKEN='b73_proj_…'
export PROJECT_ID='your-project-uuid'
export API='https://api.build73.com'

curl -sS -H "Authorization: Bearer $BUILD73_PROJECT_TOKEN" \
  "$API/api/v1/projects/$PROJECT_ID/status" | jq .

curl — checkpoint + write file

IDEM=$(uuidgen)

curl -sS -X POST -H "Authorization: Bearer $BUILD73_PROJECT_TOKEN" \
  -H "Idempotency-Key: $IDEM-checkpoint" \
  -H "Content-Type: application/json" \
  -d '{"name":"agent-checkpoint","description":"before edit"}' \
  "$API/api/v1/projects/$PROJECT_ID/versions"

REV=$(curl -sS -H "Authorization: Bearer $BUILD73_PROJECT_TOKEN" \
  "$API/api/v1/projects/$PROJECT_ID/files/$FILE_ID" | jq -r .contentRevision)

curl -sS -X PUT -H "Authorization: Bearer $BUILD73_PROJECT_TOKEN" \
  -H "Idempotency-Key: $IDEM-write" \
  -H "Content-Type: application/json" \
  -d "{\"content\":\"console.log('hi')\\n\",\"expectedRevision\":$REV}" \
  "$API/api/v1/projects/$PROJECT_ID/files/$FILE_ID"

TypeScript — thin client

const API = 'https://api.build73.com';
const token = process.env.BUILD73_PROJECT_TOKEN!;
const projectId = process.env.BUILD73_PROJECT_ID!;

async function projectApi<T>(
  method: string,
  path: string,
  body?: unknown,
  idempotencyKey?: string,
): Promise<T> {
  const headers: Record<string, string> = {
    Authorization: `Bearer ${token}`,
    Accept: 'application/json',
  };
  if (body !== undefined) headers['Content-Type'] = 'application/json';
  if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;

  const res = await fetch(`${API}/api/v1/projects/${projectId}${path}`, {
    method,
    headers,
    body: body === undefined ? undefined : JSON.stringify(body),
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(`${res.status} ${data?.error?.code}: ${data?.error?.message}`);
  }
  return data as T;
}

const status = await projectApi<any>('GET', '/status');
console.log(status.credits, status.capabilities);

await projectApi('POST', '/versions', { name: 'agent-checkpoint' }, crypto.randomUUID());

const file = await projectApi<any>('GET', `/files/${fileId}`);
await projectApi(
  'PUT',
  `/files/${fileId}`,
  { content: file.content.replace('foo', 'bar'), expectedRevision: file.contentRevision },
  crypto.randomUUID(),
);

Owner management API

JWT (editor session) + OWNER only. Not reachable with a project API key.

MethodPath
GET/api/projects/{projectId}/api-access/keys
POST/api/projects/{projectId}/api-access/keys
DELETE/api/projects/{projectId}/api-access/keys/{id}
GET/api/projects/{projectId}/api-access/oauth-grants
DELETE/api/projects/{projectId}/api-access/oauth-grants/{id}
GET/api/projects/{projectId}/api-access/audit
GET/api/projects/{projectId}/api-access/connection-info

connection-info returns MCP URL, REST base, OpenAPI URL, OAuth discovery, and sample mcp.json without secrets.


Security notes

  • Store keys/tokens in env or a secret manager; never in git, screenshots, or chat logs.
  • Prefer least-privilege scopes; omit ai:generate / build:run unless needed.
  • Rotate keys before expiry; revoke immediately if leaked (DELETE …/keys/{id}).
  • Token is project-bound — do not reuse across projects.
  • Do not send secrets in query strings; Bearer header only.
  • Agents must not attempt forbidden actions (delete project, billing, member admin).
  • Audit trail records method, path, action, status, latency, credential id — not file contents or prompts.
  • Rate limit: treat 429 as retryable with backoff.
  • Production OpenAPI for agents is the slim /api/v1/openapi.json document — not the internal Nest Swagger UI.

Agent workflow (required)

1. `project_status` / `GET /status` — capabilities, credits, revision 2. Read / search only what you need 3. `versions_create` checkpoint before large or destructive edits 4. Mutate with idempotency + `expectedRevision` where supported 5. Verify (re-read files / status); run export only when asked and credits allow 6. Never delete the project or touch billing

Copy-paste Cursor rules and MCP template: `docs/project-api-agent-bundle/`.