Generated from the OpenAPI document
API reference
On this page
Everything you can call in the cronai.app API. Paths start at the site's address, https://www.cronai.app, and requests and answers are JSON.
The machine-readable document is /api/v1/openapi.json (OpenAPI 3.1). This page as Markdown: /docs/api.md.
Authentication
The API takes two credentials. The session cookie better-auth.session_token that signing in at /sign-in sets (on https its name carries the __Secure- prefix) works for every operation.
An API key works for the operations below marked as taking an API key, in the one workspace it belongs to. A signed-in person makes it on the account page, under API keys, which shows its secret once; an agent sends it as Authorization: Bearer cronai_….
A key holds read scopes only for now (workflows:read, runs:read and usage:read), and reads only what the person who made it may read today.
Where an answer holds a webhook's address, a delivery's or a webhook step's, a key reads only its origin, the scheme and host (https://hooks.example.com), never its path or query, and a webhook step's header values as [redacted]; a session reads them in full.
A key stops working when it expires, when it is revoked, or when the person who made it leaves the workspace. A key lasts 90 days unless its maker chooses another lifetime, at most 365 days, and a workspace holds at most 5 keys that have not expired or been revoked. Making, listing and revoking keys takes a session.
A request with an Authorization header is authenticated by that header alone. A request without a credential, or with a key that is not valid, is answered 401. The published schemas need neither.
Example call
A first call with an API key: GET /api/v1/me answers whose key it is, with its workspace and its scopes.
curl https://www.cronai.app/api/v1/me \ -H "Authorization: Bearer cronai_…"
It answers 200 with JSON, here for a key with two scopes:
{
"kind": "api_key",
"workspace": "A7mGeeUjHdTWXNYhLwbeufCG63EYdLlp",
"scopes": [
"workflows:read",
"runs:read"
],
"user": {
"id": "LLTh1gLpwlhfadCbYTQNS00hC8h6OeBd",
"name": "Ada Example",
"email": "ada@example.com"
},
"key": {
"id": "f9dcf1a3-acc2-43ed-8df6-4c2be893397b",
"name": "Docs example",
"prefix": "cronai_2SfWdIAD"
}
}In a path, {ws} is the workspace: the caller's active organization, as get_me answers it. Another workspace answers 404.
Errors
When it refuses a request, an operation that needs a session says why in a problem document, application/problem+json (RFC 9457): a code and, for a request that is not valid, issues naming each field.
Limits
Requests are limited per minute, counted in fixed windows.
| Counted | Requests per minute |
|---|---|
| One API key | 20 |
| One workspace, across all its API keys | 50 |
| One signed-in person with a session, the pages of the app they open included | 120 |
| One client address, for requests without a valid credential | 5 |
| One client address, for the OpenAPI document and the published schemas | 60 |
| One client address and echo endpoint, for deliveries to an echo endpoint | 30 |
| One echo endpoint, for the service's own deliveries from a run, which are counted apart | 120 |
- One API key
- Requests per minute
- 20
- One workspace, across all its API keys
- Requests per minute
- 50
- One signed-in person with a session, the pages of the app they open included
- Requests per minute
- 120
- One client address, for requests without a valid credential
- Requests per minute
- 5
- One client address, for the OpenAPI document and the published schemas
- Requests per minute
- 60
- One client address and echo endpoint, for deliveries to an echo endpoint
- Requests per minute
- 30
- One echo endpoint, for the service's own deliveries from a run, which are counted apart
- Requests per minute
- 120
An IPv6 client address is counted per /56.
A request past a limit is answered 429 with the problem code rate_limited and a Retry-After header, the whole seconds until its window ends; its operation does not run.
An API key or a session is checked before the request is counted, since a valid one never counts against its address: a refused request that carries one still costs that check.
Operations
Every operation's maturity is alpha, unless its line says otherwise. A name in braces is a path parameter. A line lists the operation's query parameters, headers and body fields, each optional unless marked required.
Their types, and the schema of each answer, are in the OpenAPI document under the operation's path and method: /api/v1/openapi.json.
Account
GET /apiGet me. Scope: none. Session or API key./v1 /me
Workflows
GET /apiList workflows. Scope: workflows:read. Session or API key./v1 /workspaces /{ws} /workflows POST /apiCreate workflow. Scope: workflows:write. Session only.Header:/v1 /workspaces /{ws} /workflows Idempotency-Key. Body:cron(required),delivery(required),name(required),prompt(required),runImmediately,systemPrompt,timezone(required),webSearch.GET /apiGet workflow. Scope: workflows:read. Session or API key./v1 /workspaces /{ws} /workflows /{id} PATCH /apiUpdate workflow. Scope: workflows:write. Session only.Headers:/v1 /workspaces /{ws} /workflows /{id} If-Match(required),Idempotency-Key. Body:cron,delivery,name,prompt,systemPrompt,timezone,webSearch.DELETE /apiDelete workflow. Scope: workflows:write. Session only./v1 /workspaces /{ws} /workflows /{id} POST /apiPause workflow. Scope: workflows:write. Session only.Body: JSON, no fields named./v1 /workspaces /{ws} /workflows /{id} /pause POST /apiRestore revision. Scope: workflows:write. Session only.Header:/v1 /workspaces /{ws} /workflows /{id} /restore If-Match(required). Body:revision_id(required).POST /apiResume workflow. Scope: workflows:write. Session only.Body: JSON, no fields named./v1 /workspaces /{ws} /workflows /{id} /resume GET /apiList revisions. Scope: workflows:read. Session or API key.Query:/v1 /workspaces /{ws} /workflows /{id} /revisions after,limit.POST /apiRotate webhook secret. Scope: deliveries:write. Session only.Body: JSON, no fields named./v1 /workspaces /{ws} /workflows /{id} /rotate-secret POST /apiRun workflow. Scope: runs:trigger. Session only.Header:/v1 /workspaces /{ws} /workflows /{id} /runs Idempotency-Key. Body: JSON, no fields named.GET /apiGet workflow trends. Scope: workflows:read. Session, or API key with runs:read too.Query:/v1 /workspaces /{ws} /workflows /{id} /trends from,to.
Runs
GET /apiList runs. Scope: runs:read. Session or API key.Query:/v1 /workspaces /{ws} /runs workflow_id,since,after,limit.GET /apiCompare runs. Scope: runs:read. Session or API key.Query:/v1 /workspaces /{ws} /runs /compare a(required),b(required).GET /apiGet run. Scope: runs:read. Session or API key./v1 /workspaces /{ws} /runs /{id} GET /apiGet run provenance. Scope: runs:read. Session or API key./v1 /workspaces /{ws} /runs /{id} /provenance
Echo endpoints
POST /apiCreate echo endpoint. Scope: deliveries:write. Session only.Body: JSON, no fields named./v1 /workspaces /{ws} /echo-endpoints PATCH /apiSet echo endpoint secret. Scope: deliveries:write. Session only.Body:/v1 /workspaces /{ws} /echo-endpoints /{token} secret(required).GET /apiList echo receipts. Scope: deliveries:read. Session only./v1 /workspaces /{ws} /echo-endpoints /{token} /receipts
Usage
GET /apiGet usage. Scope: usage:read. Session or API key./v1 /workspaces /{ws} /usage
Workspace status
GET /apiGet workspace status. Scope: workflows:read. Session, or API key with runs:read too./v1 /workspaces /{ws} /status
API keys
GET /apiList api keys. Scope: none. Session only./v1 /workspaces /{ws} /keys POST /apiCreate api key. Scope: none. Session only.Body:/v1 /workspaces /{ws} /keys expiresInDays,name(required),password(required),scopes(required).DELETE /apiRevoke api key. Scope: none. Session only./v1 /workspaces /{ws} /keys /{id}
Published schemas
GET /apiGet a published JSON Schema. Scope: none, no session needed. Maturity: none.Path:/v1 /schemas /{name} {name}isworkflow-v1.jsonorworkflow-export-v1.json.