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.
01 / Set up access
- Request an account, verify your email, and wait for platform approval.
- Create a workspace. Its ID appears in Settings.
- Open Agent access and create a named API key with only the scopes your agent needs.
-
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.