unclosed

The Unclosed API · Preview

Connect your agents
to the workspace.

Use the same workspace from your browser, your scripts, or an agent acting on your behalf.

This preview supports accounts, workspaces, projects, discussions, and scoped API credentials, workspace budgets, and Cloudflare account read-access checks, partial provider-cost imports, plus GitHub repository enrollment when the app is configured. Experiment execution, ongoing provider authorization, published SDK packages, and webhooks are not yet available.

01 / Set up access

  1. Request an account, verify your email, and wait for platform approval.
  2. Create a workspace. Its ID appears in Settings.
  3. Open Agent access and create a named API key with only the scopes your agent needs.
  4. Store the key in your secret manager as UNCLOSED_API_KEY. You can see its secret only once.

The examples use https://unclosed.dev. Set UNCLOSED_BASE_URL to your configured Unclosed origin if running a separate deployment. API calls stay on that same origin.

02 / Make your first request

Retrieve your current identity. Your account approval and workspace membership are checked on every request.

curl "${UNCLOSED_BASE_URL:-https://unclosed.dev}/v1/me" \
  -H "Authorization: Bearer $UNCLOSED_API_KEY" \
  -H "Accept: application/json"

List the workspaces available to this credential:

curl "${UNCLOSED_BASE_URL:-https://unclosed.dev}/v1/workspaces" \
  -H "Authorization: Bearer $UNCLOSED_API_KEY"

List responses use {"data": [...], "has_more": false, "next_cursor": null}. Follow a non-null cursor using ?starting_after=… to retrieve additional pages.

03 / Create a project

This action requires projects:write and a human with editor or owner access. Set UNCLOSED_WORKSPACE_ID to your workspace ID. Generate and retain one UUID per logical request:

REQUEST_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"

curl -X POST \
  "${UNCLOSED_BASE_URL:-https://unclosed.dev}/v1/workspaces/$UNCLOSED_WORKSPACE_ID/projects" \
  -H "Authorization: Bearer $UNCLOSED_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $REQUEST_ID" \
  -d '{"name":"A better question","description":"What would change our minds?"}'

If the network fails, retry with the same request ID and body. Generate a new ID only for a new action. Creating a project does not authorize compute or spending.

Errors you can act on

Errors include a stable code, a message, a request identifier, and whether retrying may help. Preserve the request ID when reporting an issue. Never log authorization headers or API secrets.

{
  "error": {
    "code": "permission_denied",
    "message": "This action requires a different permission.",
    "request_id": "…",
    "retryable": false
  }
}

This is an illustrative error shape; exact codes come from the response. Authentication failures require a valid credential. Permission failures require a role or scope change. Rate limits and transient service failures should use bounded retries with backoff; honor Retry-After when present.

Your agent acts for you.

A key is bound to one workspace and its creator. Its effective permissions are the intersection of its scopes, current workspace role, account approval, and revocation state. Removing membership or revoking the key removes delegated access.

Agent credentials cannot sign in for a person, create more API credentials, or use platform administration. Those operations require human identity and the appropriate authority. A future execution API will require explicit reviewed campaign authorization.

Keep secrets out of browser storage, repositories, experiment code, and logs. Rotate credentials by creating a replacement, updating your secret manager, and revoking the old key.

Build against what’s here.

Download the OpenAPI specification for every implemented product endpoint.

Resource Endpoint
Identity /v1/me
Workspaces /v1/workspaces
Projects /v1/workspaces/:id/projects
Members /v1/workspaces/:id/members
Invitations /v1/workspaces/:id/invitations
Discussions /v1/workspaces/:id/comments
Usage records /v1/workspaces/:id/usage
GitHub project repository /v1/workspaces/:id/projects/:project_id/github
Cloudflare account read access /v1/workspaces/:id/connections/cloudflare
Provider cost imports and workspace attribution /v1/workspaces/:id/provider-costs/imports · /v1/workspaces/:id/provider-costs/attributions · /v1/workspaces/:id/provider-costs/report
Owner budget policy /v1/workspaces/:id/budget
Budget journal and allocations /v1/workspaces/:id/ledger · /v1/workspaces/:id/allocations

Provider costs require current owner access and billing:read or billing:write scope. Import one completed UTC day with a fresh selected-account Cloudflare Billing Read token; Unclosed discards the token. Assign an explicit workspace share and read an inclusive report of up to 31 days. Amounts are exact USD strings with up to 18 decimal places. Missing days and superseded observations stay incomplete. These partial usage overages exclude fixed fees and taxes and are not a reconciled invoice. Assignments do not change execution budgets.

Every mutation requires an idempotency key. Use resource_type: "project", the project’s resource_id, and body when creating a comment. Usage records indicate their evidence quality; missing provider data must not be interpreted as zero cost.

Owners can grant agents billing:read and billing:write explicitly. Read the budget before updating it and send its version as expected_version (zero for first setup), exact integer limit_micro_usd as a string, and UTC period_start/period_end timestamps. One dollar is "1000000" micro-USD. Budget windows do not renew automatically. Setting a budget does not enable execution.

Each workspace will use its own Cloudflare account and pay Cloudflare directly. Budget accounting tracks Unclosed’s reserved and consumed allowances; partial provider-cost observations are available through imports, while complete reconciled invoices remain unavailable in this preview.

Owners can grant connections:read or connections:write for account verification. A short-lived, account-scoped user API token is used once and discarded. This records read access at a timestamp; it does not enable provisioning, execution, or a billing feed. The api_token is excluded from retry identity: keep the same account ID, expected version, and idempotency key when reentering a token after an uncertain response. A successful replay returns the original verification timestamp.

To link a GitHub repository, an owner or their scoped agent starts POST /v1/workspaces/:id/projects/:project_id/github/authorizations with repository in owner/name form and the connection’s expected_version. Open the returned local human_action_url in the initiating person’s signed-in browser. They consent through GitHub; the agent can poll the returned authorization ID. Unclosed verifies repository administrator access and retains metadata only. This does not authorize code ingestion or experiments. Disconnect before replacing a repository.

API/SDK parity, durable webhooks, and test-mode execution are planned capabilities. They are not part of the current preview.