rect.sh

HTTP API

The plain-JSON endpoints behind every surface — for programs without an MCP client.

Everything the MCP tools and the CLI do bottoms out in plain HTTP + JSON. Use the API directly when the caller is a server, a webhook, a cron job, or any program where an MCP client or the CLI doesn't fit.

Authentication by endpoint

The API splits cleanly along the trust boundary:

  • Management endpoints (/api/rects, /api/view-sources, /api/rect-instances) operate on your account's templates and instance lists. Call them authenticated: with a browser session, or an Authorization: Bearer <token> header carrying an account API token from account settings. The CLI sends RECT_API_TOKEN this way in CI.
  • Capability endpoints (/api/rect/<instanceId>/…) operate on one live instance. They are unauthenticated by design: the opaque instance id is the capability, the same property that lets a human open the view from just a link. Treat instance ids like the secrets they are.
  • Template discovery (/api/rects/search) is public for public templates. A browser session or bearer token also includes templates visible to that caller; accountId narrows results to a workspace the caller can access.

Discover templates

curl -X POST https://rect.sh/api/rects/search \
  -H 'content-type: application/json' \
  -d '{ "query": "project planning dashboard", "limit": 10 }'

Returns ranked compact candidates in results. Fetch the selected template's full spec before issuing it; MCP uses rect_get_spec and the CLI uses rect spec.

{
  "results": [
    {
      "rectId": "…",
      "name": "Project Board",
      "slug": "project-board",
      "description": "Plan and track project work.",
      "owner": {
        "accountId": "…",
        "name": "rect.sh",
        "slug": "official"
      },
      "source": "official",
      "score": 0.039
    }
  ]
}

score is PostgreSQL full-text relevance and determines result order.

Keyword-based search matches words and phrases across each template's name, slug, description, actions, and state fields. The default limit is 10 and the maximum is 20; query must contain 1–1000 characters after trimming. Invalid input returns 400, unauthorized or inaccessible accountId values return 401 or 403, and a temporarily unavailable search dependency returns 503.

Drive an instance (capability)

The endpoints a running loop actually touches. <id> is the instance id from issuing (also visible in the capability URL /r/<id>).

EndpointMethodWhat it does
/api/rect/<id>/checkGETLatest view model + revision.
/api/rect/<id>/statePATCHApply a merge patch: { patch }.
/api/rect/<id>/dispatchPOSTRun a named action: { action, input? }.
/api/rect/<id>/attachments/uploadsPOSTCreate a signed attachment upload ticket.
/api/rect/<id>/attachments/<attId>/completePOSTComplete a signed upload.
/api/rect/<id>/attachments/<attId>/<fileName>GETDownload an attachment's bytes.

Read the current state:

curl https://rect.sh/api/rect/<id>/check

Every read and write returns the same instance envelope:

{
  "instanceId": "…",
  "template": "client-intake",
  "name": "Acme Corp — intake",
  "status": "open",
  "revision": 7,
  "viewUrl": "https://rect.sh/r/…",
  "checkUrl": "https://rect.sh/api/rect/…/check",
  "stateUrl": "https://rect.sh/api/rect/…/state",
  "dispatchUrl": "https://rect.sh/api/rect/…/dispatch",
  "viewModel": { "…": "…" }
}

Patch it (RFC 7386 — objects deep-merge, null deletes, arrays replace):

curl -X PATCH https://rect.sh/api/rect/<id>/state \
  -H 'content-type: application/json' \
  -d '{ "patch": { "note": "reviewed" } }'

Patches apply against the latest state — concurrent writes are re-merged server-side, so no revision bookkeeping is needed. Writes to reserved namespaces ($attachments) return 400 with code: "reserved_key".

Dispatch a named action:

curl -X POST https://rect.sh/api/rect/<id>/dispatch \
  -H 'content-type: application/json' \
  -d '{ "action": "approve" }'

Status codes map to the dispatch result codes: 200 ok, 400 no_actions / unknown_action / invalid_input / reserved_key, 409 conflict, 413 too_large, 422 rejected (the handler's ctx.reject, with its rejectCode), 500 runtime_error / timeout.

Issue an instance

curl -X POST https://rect.sh/api/rects/<rectId>/instances \
  -H 'content-type: application/json' \
  -d '{ "name": "Acme Corp — intake", "viewModel": { "company": "Acme" } }'

Returns 201 with the instance envelope — viewUrl is what you hand the human. viewModel is optional; omitted, the instance starts from the template's example. Knowing a template's id is what authorizes issuing from it.

Manage templates & instances (authenticated)

EndpointMethodWhat it does
/api/rectsGETList an account's templates.
/api/rectsPOSTRegister (or update) a template: point it at an uploaded view source, upserted by (account, slug).
/api/rects/<rectId>DELETEDelete a template.
/api/rects/<rectId>/remix-sourceGETDownload a template's remix source.
/api/view-sources/uploadsPOSTRequest a signed upload for a compiled bundle (.html / .zip, ≤ 20 MB).
/api/view-sources/<sourceId>/completePOSTFinalize an upload — extracts and validates the spec and actions blocks.
/api/rect-instancesGETList issued instances.
/api/rect-instancesPATCHUpdate instance metadata (name).
curl https://rect.sh/api/rects \
  -H "authorization: Bearer $RECT_API_TOKEN"

These are the endpoints rect publish, rect list, and rect find are built on — for publishing from your own machine or CI, the CLI is almost always the better interface.

MCP transport

The MCP server itself is one more HTTP endpoint:

EndpointMethodWhat it does
/api/mcp/rectshGET (no event-stream Accept)JSON info doc: tool list + HTTP fallbacks.
/api/mcp/rectshPOST / GET / DELETEThe streamable-HTTP MCP transport.

On this page