{"openapi":"3.1.0","info":{"title":"Judt API","version":"1.0.0","description":"The Judt API creates and runs Apps. Each App has a sandbox, where you and agents edit and run its source, and a production deployment that serves it.\n\n## Authentication\n\nSend an API token or an OAuth access token in the `Authorization` header.\n\n- **API token:** send it as a bearer token. A token belongs to one organization, and you create and revoke tokens in the dashboard. A token scoped to one App can reach only that App. An organization-wide token can also create a short-lived token for one App with `POST /api/tokens`, to give to an agent or an MCP client.\n- **OAuth access token:** an agent gets one when a person approves its access to one organization, so the person never handles a token. Reads need the `apps:read` scope, and changes need `apps:write`. To register and sign in, follow https://judt.ai/auth.md. Send the token as a bearer token, or with a DPoP proof if it's bound to a DPoP key. One access token can work on both this API and the MCP server at https://api.judt.ai/mcp.\n\n## Sandboxes\n\nCreating an App reserves its address, but it doesn't start a sandbox or publish anything. There's no endpoint to start a sandbox. A request that needs the sandbox starts it first, so the first request after the sandbox goes idle takes longer. Files, Git, commands, commits, restores, previews, and deployments all need the sandbox.\n\nThe sandbox stops on its own when it's idle. If `sandbox_status` is `failed`, the most recent start failed, and the API retries it while the sandbox is needed.\n\n## Pagination\n\nA list returns one page at a time, in the form `{\"results\": [...], \"has_more\": true, \"next_cursor\": \"CURSOR\"}`. To get the next page, list again with `cursor` set to `next_cursor`, and the same `sort` and filters. Results that arrive while you page don't shift your pages. On the last page, `has_more` is `false`, and `next_cursor` still finds results added later.\n\n## Retries\n\nA request that creates, changes, or deletes something returns `200` after the work is done, with the resource as it is afterward. To retry a request safely, send an `Idempotency-Key` header. For 30 days, a retry with the same key and input returns the first request's result instead of repeating the work. If you don't send a key, the API generates one and returns it in the `Idempotency-Key` response header. Setting and deleting a Secret take no key, because repeating them changes nothing. A visitor token or a preview link is created fresh each time, because nothing is stored to repeat.\n\nThe API records file writes and deletes, commands, commits, and restores before it runs them. The response status tells you what to do next:\n\n- `503` or `504`: the action is recorded but not finished. To get its result, retry with the same key. A new key would run the action again.\n- `4xx` or `502`: the result is final. A retry with the same key returns the same response.\n\nA `504 request_timeout` response says how far the work got and which event to check. The work continues, and a retry with the same key picks it up.\n\n## Troubleshooting\n\nTo see what happened to an App's sandbox, deployments, and deletion, list its events with `GET /api/apps/{id}/events`. To see what the App's code did, including requests, output, exceptions, and failed builds, list its logs with `GET /api/apps/{id}/logs`.\n\n## Time limits\n\nRequests have the following time limits:\n\n- Stopping the sandbox: 120 seconds.\n- File writes, commits, and restores: 120 seconds.\n- Commands: the command's `timeout_seconds`, up to 120 seconds, plus 60 seconds to stop it and save its result.\n- Publishing, unpublishing, deleting an App, and changing production settings: 540 seconds, including any sandbox start.\n\nIf a file write, commit, restore, or command has to start the sandbox, it gets up to 120 more seconds.\n\n## Errors\n\nAn error returns a JSON body in the form `{\"error\": {\"code\": \"CODE\", \"message\": \"MESSAGE\", \"detail\": \"DETAIL\", \"request_id\": \"REQUEST_ID\"}}`. The `code` value is stable, so check it rather than `message`. The `detail` value is optional. When a parameter or a body field isn't valid, the request returns `400 invalid_request`, `message` names the field, and `detail` lists every problem if there's more than one.\n\nWhen the server rejects a credential, it returns a JSON error body and sets the `WWW-Authenticate` header. A `401` response includes the `resource_metadata` URL in `WWW-Authenticate` (RFC 9728):\n\n| Status | Error code / header | Cause | Action |\n|---|---|---|---|\n| `401` | `unauthenticated` | The request has no credential, or the API token or OAuth access token is invalid, expired, or revoked. | Send a valid API token or OAuth access token. |\n| `401` | `invalid_token` | `WWW-Authenticate` error when an API token or OAuth access token is expired, revoked, or issued for a different `resource` audience. | Refresh the access token, or request a token for the target server. |\n| `401` | `invalid_dpop_proof` | The `DPoP` proof header is missing, expired, replayed, or signed for a different HTTP method or URL. | Create a new DPoP proof for the request method and URL. |\n| `403` | `insufficient_scope` | The OAuth token does not have the required scope (`apps:read` or `apps:write`). | Request authorization with the scope named in `WWW-Authenticate`. |\n| `403` | `app_scoped_token` | An App-scoped API token called `POST /api/apps` or `POST /api/tokens`. | Use an organization-wide API token. |\n| `403` | `api_token_required` | An OAuth access token called `POST /api/tokens`. | Use an organization-wide API token. |\n| `403` | `forbidden` | Your organization role (`member`) does not allow this action. | Ask an organization `owner` or `admin` to do the action. |\n\nA Personal Agent that sends a Personal Agent Protocol `Poppy-Session` header gets the protocol's errors as `400` responses whose body also carries the code as a string in `error`: `{\"error\": \"invalid_session\", \"code\": \"invalid_session\", \"message\": \"MESSAGE\", \"request_id\": \"REQUEST_ID\"}`.\n\n| Status | Error code | Cause | Action |\n|---|---|---|---|\n| `400` | `version_unsupported` | The request has a `Poppy-Session` header, and its `Poppy-Version` header is missing or names a version Judt doesn't serve. | Fetch https://judt.ai/.well-known/poppy.json again, choose a version it lists, and start a new Session. |\n| `400` | `invalid_session` | The `Poppy-Session` header failed verification, or the access token wasn't issued to its `client_id`. | Fix the Session JWT or its delegation before you retry. |"},"servers":[{"url":"https://api.judt.ai"}],"tags":[{"name":"Account","description":"See who a credential acts for: the person, the organization, and what the credential can reach."},{"name":"Apps","description":"Create, update, and delete Apps, and control who can reach them."},{"name":"Tokens","description":"Create short-lived API tokens that reach one App, to give to an agent or an MCP client."},{"name":"Secrets","description":"Store values, such as API keys, that an App reads at runtime. Responses never include Secret values."},{"name":"Files","description":"Read and write the source files in an App's sandbox."},{"name":"Sandbox","description":"Run commands, use Git, commit and restore source, and preview the App's sandbox."},{"name":"Deployments","description":"Publish an App's source to production, and take it offline."},{"name":"Monitoring","description":"See what happened to an App, and what its code did."},{"name":"Templates","description":"The starting points for new Apps."},{"name":"Agents","description":"Work with the in-product coding agent: sessions, turns, events, and the models it runs on. The agent itself works only through this API."},{"name":"Feedback","description":"Tell the Judt team about bugs, friction, and anything else."}],"paths":{"/api/me":{"get":{"summary":"Get the caller","description":"whoami. Returns who the request acts as: the person, the organization that the credential reaches, the person's role in it, and the credential itself. Use it to check a credential, or to find out whether it reaches one App or all of them.","tags":["Account"],"operationId":"get_me","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[],"responses":{"200":{"description":"The person, organization, role, and credential.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"}}}},"/api/apps":{"post":{"summary":"Create an App","description":"Creates an App and reserves its address. The App's dev sandbox doesn't start, and nothing is published, until you use them.","tags":["Apps"],"operationId":"create_app","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The App's display name, up to 100 characters."},"slug":{"description":"The App's label under the App base domain. If another App has the slug, including a deleted App, or the slug is reserved, the request returns `409 slug_unavailable`. Reserved slugs: `staging`, `preview`, `api`, `www`, `local`. If you omit it, the API generates one, such as `plucky-teal-tatooine`.","type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$"},"template_id":{"description":"The ID of the template to create the App from. To list templates, use `GET /api/templates`. Default: the Hello template. An unknown ID returns `404 template_not_found`.","type":"string","pattern":"^tpl_[0-9A-Za-z]{24}$"},"production":{"default":{"visibility":"public"},"description":"Who can reach the published App. Default: public.","type":"object","properties":{"visibility":{"default":"public","type":"string","enum":["public","password","private"],"description":"Who can reach the published App:\n- `public`: anyone.\n- `password`: anyone with the App's password, for 60 minutes at a time.\n- `private`: members of the App's organization, who sign in through judt.\n\nAccess tokens open password-protected and private Apps. The sandbox is always private to the organization."},"password":{"type":"string","minLength":8,"maxLength":128,"description":"The App's password, 8 to 128 characters. It's required when `visibility` is `password`, and it's accepted only then. Setting a new password signs out every browser that used the old one. Responses never include the password."}},"additionalProperties":false}},"required":["name"],"additionalProperties":false}}}},"responses":{"200":{"description":"The new App.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"description":"- `app_scoped_token`: the request used an App-scoped token, which can't create Apps.\n- `app_limit_reached`: the organization has 10 Apps, the beta limit. Delete one, or send feedback to ask for more.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}},"get":{"summary":"List Apps","description":"Lists existing Apps, oldest first. To include deleted Apps, set `include_deleted` to `true`.","tags":["Apps"],"operationId":"list_apps","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"limit","in":"query","description":"The maximum number of Apps to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first Apps.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `created_at`, `updated_at`, `name`, or `slug`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"created_at"}},{"name":"slug","in":"query","description":"Returns only Apps whose `slug` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"include_deleted","in":"query","description":"If `true`, includes deleted Apps. If `false`, lists only Apps that aren't deleted.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"One page of Apps.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}":{"get":{"summary":"Get an App","description":"Returns the App, with the status of its sandbox and production. A deleted App stays readable for 30 days.","tags":["Apps"],"operationId":"get_app","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}}],"responses":{"200":{"description":"The App.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}},"patch":{"summary":"Update an App","description":"Changes the App's name, slug, or production access. If a deployment is in progress, a change to the slug or to production access returns `409 deployment_unsettled`. Try again after the deployment finishes. A new name applies at once.","tags":["Apps"],"operationId":"update_app","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"description":"The App's display name, up to 100 characters.","type":"string","minLength":1,"maxLength":100},"slug":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$","description":"The App's label under the App base domain. If another App has the slug, including a deleted App, or the slug is reserved, the request returns `409 slug_unavailable`. Reserved slugs: `staging`, `preview`, `api`, `www`, `local`."},"production":{"description":"Who can reach the published App.","type":"object","properties":{"visibility":{"type":"string","enum":["public","password","private"],"description":"Who can reach the published App:\n- `public`: anyone.\n- `password`: anyone with the App's password, for 60 minutes at a time.\n- `private`: members of the App's organization, who sign in through judt.\n\nAccess tokens open password-protected and private Apps. The sandbox is always private to the organization."},"password":{"type":"string","minLength":8,"maxLength":128,"description":"The App's password, 8 to 128 characters. It's required when `visibility` is `password`, and it's accepted only then. Setting a new password signs out every browser that used the old one. Responses never include the password."}},"additionalProperties":false}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated App.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `deployment_unsettled`: another deployment of the App is still in progress, and `detail` names it. Change production settings after it finishes.\n- `slug_unavailable`: another App has the slug, or the slug is reserved.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}},"delete":{"summary":"Delete an App","description":"Deletes the App. The response returns after the App stops serving, its sandbox is removed, and it accepts no more changes. The deleted App stays readable for 30 days, and then it's permanently removed.\n\nIf the request times out, the deletion continues, and the `504` response says what it's waiting for. To follow the deletion, list the App's events. A repeated request returns the deleted App.","tags":["Apps"],"operationId":"delete_app","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The deleted App.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/visitor-tokens":{"post":{"summary":"Create a visitor token","description":"Creates a visitor token, which opens the published App for five minutes without its password or sign-in. To use it, send it in the `X-Judt-Access-Token` header, or add it to a link as the `access_token` URL parameter. Only applies if App is visibiltiy password or private. A public App ignores the token.","tags":["Apps"],"operationId":"create_visitor_token","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key for this request, up to 200 printable characters. The request stores nothing, so a retry makes a new one whatever the key; the API returns the key in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The visitor token.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisitorToken"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `app_unpublished`: the App isn't published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/deep-links":{"post":{"summary":"Create a deep link","description":"Creates a link to a page of the App in the Judt dashboard, for a person to open. The person signs in as usual, and needs to be a member of the App's organization. The link grants no access, and it stops working after 24 hours.\n\nWith `page` set to `secrets` and a list of `secrets`, the page has a field for each named Secret, and the values never reach the caller. To see that a Secret was set, get it by name: the request returns `404` until then. Any member can create the link, and setting a Secret needs the owner or admin role.\n\nA published App needs no deep link: its own URL signs a member in when the App is private.","tags":["Apps"],"operationId":"create_deep_link","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key for this request, up to 200 printable characters. The request stores nothing, so a retry makes a new one whatever the key; the API returns the key in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"page":{"description":"The page to open. Without it, the link opens the App's usual first page.","type":"string","enum":["secrets","access","deploys","logs","preview"]},"secrets":{"description":"The Secrets to ask for, by name, from 1 to 20. The page has a field for each. Only with `page` set to `secrets`.","minItems":1,"maxItems":20,"type":"array","items":{"type":"string","pattern":"^[A-Z_][A-Z0-9_]{0,127}$"}},"reason":{"description":"One sentence, up to 200 characters, on what the Secrets are for. The page shows it as your agent's words. Only with `secrets`.","type":"string","minLength":1,"maxLength":200}},"additionalProperties":false}}}},"responses":{"200":{"description":"The deep link.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeepLink"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"}}}},"/api/tokens":{"post":{"summary":"Create an App token","description":"Creates an API token that reaches only one App, for up to 24 hours. Give it to an agent or an MCP client that should work on that App and nothing else. The token can't reach other Apps, create Apps, or create tokens. It stops working at `expires_at`, and never outlives the token that created it, so it can't extend that token's access.\n\nOnly an organization-wide API token can create one. The response is the only place the token's value appears, so store it before you continue. If the response is lost, retry with the same `Idempotency-Key`: the retry returns the same token with a new value, and the earlier value stops working. To revoke a token before it expires, use the dashboard.","tags":["Tokens"],"operationId":"create_app_token","security":[{"ApiToken":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A name for the token, up to 100 characters. The dashboard lists the token by this name."},"app_id":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","description":"The App that the token reaches. The token can't reach any other App or create Apps."},"expires_in":{"default":3600,"description":"How long the token works, in seconds, from 60 to 86400 (24 hours). Default: 3600. The token never outlives the token that created it.","type":"integer","minimum":60,"maximum":86400,"example":300}},"required":["name","app_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"The new token, with its value.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiToken"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"description":"- `app_scoped_token`: the request used an App-scoped token, which can't create tokens.\n- `api_token_required`: the request used an OAuth access token. Use an organization-wide API token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `token_revoked`: the token that this key created was revoked. Create another with a new key.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/secrets":{"get":{"summary":"List Secrets","description":"Lists the App's Secrets by name. Responses never include Secret values.","tags":["Secrets"],"operationId":"list_secrets","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of Secrets to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first Secrets.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `name`, `created_at`, or `updated_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"name"}},{"name":"name","in":"query","description":"Returns only Secrets whose `name` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"One page of Secrets.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/secrets/{name}":{"get":{"summary":"Get a Secret","description":"Returns a Secret by name, without its value.","tags":["Secrets"],"operationId":"get_secret","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"name","in":"path","required":true,"description":"The Secret's name: uppercase letters, digits, and underscores, not starting with a digit. The App reads the value as an environment variable with this name.","schema":{"type":"string","pattern":"^[A-Z_][A-Z0-9_]{0,127}$","example":"STRIPE_SECRET_KEY"}}],"responses":{"200":{"description":"The Secret, without its value.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Secret"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}},"put":{"summary":"Set a Secret","description":"Creates the Secret with this name, or replaces its value. A running sandbox's dev server gets the new value at once, and production gets it at the next deploy.","tags":["Secrets"],"operationId":"set_secret","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"name","in":"path","required":true,"description":"The Secret's name: uppercase letters, digits, and underscores, not starting with a digit. The App reads the value as an environment variable with this name.","schema":{"type":"string","pattern":"^[A-Z_][A-Z0-9_]{0,127}$","example":"STRIPE_SECRET_KEY"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":65536,"description":"The Secret's value, from 1 byte to 64 KiB of UTF-8. Responses never include it."}},"required":["value"],"additionalProperties":false}}}},"responses":{"200":{"description":"The Secret, without its value.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Secret"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"}}},"delete":{"summary":"Delete a Secret","description":"Deletes the Secret and its stored value. A running sandbox's dev server stops getting it at once, and production at the next deploy.","tags":["Secrets"],"operationId":"delete_secret","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"name","in":"path","required":true,"description":"The Secret's name: uppercase letters, digits, and underscores, not starting with a digit. The App reads the value as an environment variable with this name.","schema":{"type":"string","pattern":"^[A-Z_][A-Z0-9_]{0,127}$","example":"STRIPE_SECRET_KEY"}}],"responses":{"200":{"description":"The deleted Secret.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Secret"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/files":{"get":{"summary":"List files","description":"Lists the App's source files by path: the files Git tracks, and new files that `.gitignore` doesn't ignore. Folders aren't listed separately. A file's path includes its folders.\n\nTo find code, filter paths with `path` or `glob`, and file text with `content` or `pattern`. A text search adds `match_count` and the first 20 matching lines to each file. To see more of a file, read it with `lines`.","tags":["Files"],"operationId":"list_files","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of files to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first files.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `path` or `size`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `path`.","schema":{"type":"string","default":"path"}},{"name":"path","in":"query","description":"Returns only files whose `path` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"content","in":"query","description":"Returns only files whose `content` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"glob","in":"query","description":"Returns only files whose `path` matches this glob, from the App's root. `*` matches within one folder, `**` matches across folders, `?` matches one character, `[abc]` matches one of a set, and `{ts,tsx}` matches any of its alternatives, as in `src/**/*.{ts,tsx}`. Without a leading `**/`, a glob matches only from the root: `*.json` matches `package.json` but not `src/data.json`.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"pattern","in":"query","description":"Returns only files with a line that this regular expression matches, in Perl syntax. Matching is case-sensitive; to ignore case, start the expression with `(?i)`. Can't be combined with `content`.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"One page of files.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileList"}}}},"400":{"description":"- `invalid_request`: a parameter is malformed, or the request sets both `content` and `pattern`.\n- `invalid_pattern`: `glob` or `pattern` isn't valid, and `detail` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox returned a response that the API can't use, such as `sandbox_incompatible` or `helper_output_too_large`. Retrying doesn't change the result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/files/{path}":{"get":{"summary":"Read a file","description":"Returns the file as JSON. For a text file, `content` holds the text, and `line_count` says how many lines it has. To read only some lines, set `lines`. For a binary file, `binary` is `true` and `content` is left out. To get the bytes, send `Accept: application/octet-stream`.\n\nThe `ETag` header holds the whole file's version, even when you read only some lines. To skip an unchanged file, send its ETag in `If-None-Match`, and the request returns `304`.","tags":["Files"],"operationId":"read_file","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"path","in":"path","required":true,"description":"The file's path from the App's root, such as `src/lib/util.ts`.","schema":{"type":"string"}},{"name":"If-None-Match","in":"header","schema":{"type":"string"},"description":"One or more quoted ETags from earlier reads. If the file still matches one of them, the request returns `304` with no body."},{"name":"lines","in":"query","description":"Returns only these lines of a text file: a range such as `120-180`, a start such as `120-` (to the end), or one line such as `120`. Lines are numbered from 1, and both ends are inclusive. An end past the last line stops at the last line.","schema":{"type":"string","pattern":"^[1-9]\\d{0,8}(-([1-9]\\d{0,8})?)?$"}}],"responses":{"200":{"description":"The file, as JSON or as raw bytes.","headers":{"ETag":{"schema":{"type":"string"},"description":"The file's version: its quoted SHA-256 hash."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileContent"}},"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"304":{"description":"The file still matches `If-None-Match`."},"400":{"description":"- `invalid_request`: `lines` is malformed, or it's set with `Accept: application/octet-stream`.\n- `line_out_of_range`: `lines` starts past the file's last line.\n- `binary_file`: `lines` is set for a binary file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox returned a response that the API can't use, such as `sandbox_incompatible` or `helper_output_too_large`. Retrying doesn't change the result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}},"put":{"summary":"Write a file","description":"Creates or replaces a file, up to 4 MiB, and creates any missing folders. Send text as JSON, in the form `{\"content\": \"TEXT\"}`, or send raw bytes with any other `Content-Type`.\n\nTo replace only a specific version, send its ETag in `If-Match`. To create the file only if it doesn't exist, send `If-None-Match: *`.","tags":["Files"],"operationId":"write_file","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"path","in":"path","required":true,"description":"The file's path from the App's root, such as `src/lib/util.ts`.","schema":{"type":"string"}},{"name":"If-Match","in":"header","schema":{"type":"string"},"description":"One or more quoted ETags from earlier reads, or `*`. The request runs only if the file matches one of the ETags, or, for `*`, if the file exists. Otherwise, it returns `412`."},{"name":"If-None-Match","in":"header","schema":{"type":"string"},"description":"`*` to create the file only if it doesn't exist. If the file exists, the request returns `412`."},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"content":{"type":"string","maxLength":4194304,"description":"The file's UTF-8 text, up to 4 MiB."}},"required":["content"],"additionalProperties":false}},"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"The replaced file.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."},"ETag":{"schema":{"type":"string"},"description":"The file's version: its quoted SHA-256 hash."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/File"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"412":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox failed the action, for example with `process_isolation_unavailable`, or it returned a response that the API can't use (`sandbox_incompatible`). This result is final. A retry with the same `Idempotency-Key` returns the same error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The action is recorded but not finished, and `code` and `message` say what's holding it back. The action hasn't run yet, its outcome isn't known yet, or it's applied but not yet backed up (`recovery_degraded`). To get its result, retry with the same `Idempotency-Key`. A new key would run the action again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"$ref":"#/components/responses/Error"}}},"delete":{"summary":"Delete a file","description":"Deletes the file and any folders that it leaves empty. To delete only a specific version, send its ETag in `If-Match`.","tags":["Files"],"operationId":"delete_file","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"path","in":"path","required":true,"description":"The file's path from the App's root, such as `src/lib/util.ts`.","schema":{"type":"string"}},{"name":"If-Match","in":"header","schema":{"type":"string"},"description":"One or more quoted ETags from earlier reads, or `*`. The request runs only if the file matches one of the ETags, or, for `*`, if the file exists. Otherwise, it returns `412`."},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The deleted file.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/File"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"412":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox failed the action, for example with `process_isolation_unavailable`, or it returned a response that the API can't use (`sandbox_incompatible`). This result is final. A retry with the same `Idempotency-Key` returns the same error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The action is recorded but not finished, and `code` and `message` say what's holding it back. The action hasn't run yet, its outcome isn't known yet, or it's applied but not yet backed up (`recovery_degraded`). To get its result, retry with the same `Idempotency-Key`. A new key would run the action again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/commands":{"post":{"summary":"Run a command","description":"Runs a shell command in the App's sandbox and returns its output after it finishes or times out. The command doesn't get the App's Secrets, and its output shows any Secret value as `[secret:NAME]`.\n\nTo retry safely, send the same `Idempotency-Key`. A retry returns the first run's result instead of running the command again.","tags":["Sandbox"],"operationId":"run_command","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"command":{"type":"string","minLength":1,"maxLength":8192,"description":"The shell command to run, up to 8192 characters."},"timeout_seconds":{"default":60,"description":"How long the command can run, in seconds, before it's stopped.","type":"integer","minimum":1,"maximum":120}},"required":["command"],"additionalProperties":false}}}},"responses":{"200":{"description":"The command's result.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResult"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox failed the action, for example with `process_isolation_unavailable`, or it returned a response that the API can't use (`sandbox_incompatible`). This result is final. A retry with the same `Idempotency-Key` returns the same error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The action is recorded but not finished, and `code` and `message` say what's holding it back. The action hasn't run yet, its outcome isn't known yet, or it's applied but not yet backed up (`recovery_degraded`). To get its result, retry with the same `Idempotency-Key`. A new key would run the action again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/git":{"get":{"summary":"Get Git information","description":"Returns the sandbox's Git status, its uncommitted changes, or its recent history, depending on `mode`.","tags":["Sandbox"],"operationId":"get_git","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"mode","in":"query","description":"What to return:\n- `status`: the Git status.\n- `diff`: the uncommitted changes, as a patch.\n- `stat`: a summary of the uncommitted changes.\n- `history`: the recent commits.","schema":{"type":"string","enum":["status","diff","stat","history"],"default":"status"}}],"responses":{"200":{"description":"The Git output.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitResult"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox returned a response that the API can't use, such as `sandbox_incompatible` or `helper_output_too_large`. Retrying doesn't change the result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/commits":{"post":{"summary":"Create a commit","description":"Commits the sandbox's changes to Git and backs up the commit, so that you can restore it later.","tags":["Sandbox"],"operationId":"create_commit","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"message":{"type":"string","minLength":1,"maxLength":240,"pattern":"^[^\\r\\n]*$","description":"A one-line summary of what changed since the last commit, up to 240 characters. It becomes the Git commit message."}},"required":["message"],"additionalProperties":false}}}},"responses":{"200":{"description":"The new commit.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Commit"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox failed the action, for example with `process_isolation_unavailable`, or it returned a response that the API can't use (`sandbox_incompatible`). This result is final. A retry with the same `Idempotency-Key` returns the same error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The action is recorded but not finished, and `code` and `message` say what's holding it back. The action hasn't run yet, its outcome isn't known yet, or it's applied but not yet backed up (`recovery_degraded`). To get its result, retry with the same `Idempotency-Key`. A new key would run the action again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"$ref":"#/components/responses/Error"}}},"get":{"summary":"List commits","description":"Lists the App's commits, newest first. To restore a commit, pass its `id` to `POST /api/apps/{id}/restore`, and to publish it, pass it as `commit_id` to `POST /api/apps/{id}/deployments`. To find the commit for a Git SHA, search by `commit_sha`.","tags":["Sandbox"],"operationId":"list_commits","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of commits to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first commits.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `created_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"-created_at"}},{"name":"commit_sha","in":"query","description":"Returns only commits whose `commit_sha` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"message","in":"query","description":"Returns only commits whose `message` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"One page of commits.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommitList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/restore":{"post":{"summary":"Restore a commit","description":"Resets the sandbox's files to a commit. Production doesn't change. To publish the restored source, create a deployment.","tags":["Sandbox"],"operationId":"restore_commit","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"commit_id":{"type":"string","pattern":"^cmt_[0-9A-Za-z]{24}$","description":"The ID of the commit to restore, from `GET /api/apps/{id}/commits`."}},"required":["commit_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"The App.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"422":{"description":"- `commit_not_found`: the commit is no longer in the App's repository.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox failed the action, for example with `process_isolation_unavailable`, or it returned a response that the API can't use (`sandbox_incompatible`). This result is final. A retry with the same `Idempotency-Key` returns the same error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The action is recorded but not finished, and `code` and `message` say what's holding it back. The action hasn't run yet, its outcome isn't known yet, or it's applied but not yet backed up (`recovery_degraded`). To get its result, retry with the same `Idempotency-Key`. A new key would run the action again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/preview":{"post":{"summary":"Create a preview link","description":"Returns a short-lived URL that opens the sandbox's dev server in a browser. The request starts the dev server if it isn't running, and waits for it to serve the sandbox's current files, including a rebuild that a recent change started.\n\nIf the dev server can't build or start the App, the request returns `422 preview_failed`, and `detail` has the dev server's output from that build. After a change breaks a rebuild, the dev server keeps serving the previous build, but the request still returns `422` until the App builds again. To check the App after a change, call this endpoint, then read its logs with `GET /api/apps/{id}/logs`.","tags":["Sandbox"],"operationId":"create_preview","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key for this request, up to 200 printable characters. The request stores nothing, so a retry makes a new one whatever the key; the API returns the key in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The preview link.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewResult"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"},"422":{"description":"- `preview_failed`: the dev server can't build or start the App. `detail` has its output from that build. Change the source, then retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"The sandbox returned a response that the API can't use, such as `sandbox_incompatible` or `helper_output_too_large`. Retrying doesn't change the result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/stop":{"post":{"summary":"Stop the sandbox","description":"Commits any changes and stops the sandbox without waiting for it to go idle. The response returns the App after the sandbox stops.\n\nIf a later request needs the sandbox before the stop finishes, the sandbox keeps running, and the response returns the App as it is. The next request that needs the sandbox starts it again.","tags":["Sandbox"],"operationId":"stop_sandbox","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The App.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/deployments":{"post":{"summary":"Deploy the App","description":"Publishes the App to production and waits until the new version serves. By default, the request first commits any changes in the sandbox, then publishes that commit; pass `commit_id` to publish an earlier version. Publishing an unpublished App puts it back online.","tags":["Deployments"],"operationId":"deploy","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"commit_id":{"description":"The ID of the commit to publish, from `GET /api/apps/{id}/commits`. Default: the sandbox's files as they are, committed first if they changed since the last commit.","type":"string","pattern":"^cmt_[0-9A-Za-z]{24}$"}},"additionalProperties":false}}}},"responses":{"200":{"description":"The deployment.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deployment"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"description":"- `not_found`: the App, or the commit that `commit_id` names, doesn't exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"- `deployment_unsettled`: another deployment of the App is still in progress, and `detail` names it. Deploy again after it finishes.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"- `app_deleted`: the App was deleted before the request, or while it was publishing and the publication didn't complete. If the publication completed before the deletion, the request returns `200` with `status` set to `disabled`. The deployment stays readable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"$ref":"#/components/responses/Error"},"422":{"description":"The source can't be deployed as it is. `message` says what to change, and `code` is one of the following: `unsupported_entrypoint`, `unsupported_dependencies`, `lockfile_missing`, `dependency_install_failed`, `build_failed`, `entrypoint_missing`, `source_limit`, or `commit_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/Error"},"502":{"description":"Publication or activation failed, and `message` says why. `code` is `deploy_failed`, `publish_timeout`, `activation_timeout`, `activation_failed`, or a failure code from the runtime host.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}},"get":{"summary":"List deployments","description":"Lists the App's deployments, newest first.","tags":["Deployments"],"operationId":"list_deployments","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of deployments to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first deployments.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `created_at` or `published_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"-created_at"}},{"name":"commit_sha","in":"query","description":"Returns only deployments whose `commit_sha` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"One page of deployments.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeploymentList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/deployments/{deployment_id}":{"get":{"summary":"Get a deployment","description":"Returns the deployment, including its status, why it failed, and its database migrations.","tags":["Deployments"],"operationId":"get_deployment","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"deployment_id","in":"path","required":true,"description":"A unique ID that starts with `dep_`.","schema":{"type":"string","pattern":"^dep_[0-9A-Za-z]{24}$","example":"dep_Zj7XhMgplmTJcp5Dk4vcYuk0"}}],"responses":{"200":{"description":"The deployment.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deployment"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/unpublish":{"post":{"summary":"Unpublish an App","description":"Takes the App offline. The App's deployments stay readable, and the next deployment publishes the App again.","tags":["Deployments"],"operationId":"unpublish_app","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The App.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `deployment_unsettled`: another deployment of the App is still in progress, and `detail` names it. Unpublish after it finishes.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"504":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/events":{"get":{"summary":"List App events","description":"Lists what happened to the App's sandbox, deployments, and deletion, newest first. Use events to find out why an action failed or what it's waiting for. To list only what needs attention, set `kind` to `error,blocked`.\n\nTo see what the App's code did, list its logs instead.","tags":["Monitoring"],"operationId":"list_events","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of events to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first events.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `created_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"-created_at"}},{"name":"subject","in":"query","description":"Returns only events whose `subject` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"category","in":"query","description":"Returns only events in these categories. Separate values with commas. Values: `sandbox`, `deployment`, `deletion`.","schema":{"type":"string"}},{"name":"kind","in":"query","description":"Returns only events of these kinds. Separate values with commas. Values: `transition`, `action`, `error`, `blocked`, `resolved`. To list what needs attention, use `error,blocked`.","schema":{"type":"string"}}],"responses":{"200":{"description":"One page of events.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/logs":{"get":{"summary":"List logs","description":"Lists the App's log entries, newest first, from production and the sandbox. Entries include the requests that the App served, what its code wrote and threw, dev server builds, and deployment failures. A failed build includes the bundler's report.\n\nEntries for one request share a `trace_id`. Logs are kept for 30 days.","tags":["Monitoring"],"operationId":"list_logs","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of log entries to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first log entries.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `created_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"-created_at"}},{"name":"message","in":"query","description":"Returns only log entries whose `message` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"environment","in":"query","description":"Returns only entries from the published App (`production`) or from its sandbox (`development`).","schema":{"type":"string","enum":["production","development"]}},{"name":"source","in":"query","description":"Returns only entries from these sources. Separate values with commas. Values: `request`, `app`, `dev_server`, `runtime`, `deployment`, `platform`.","schema":{"type":"string"}},{"name":"level","in":"query","description":"Returns only entries at these levels. Separate values with commas. Values: `debug`, `info`, `warn`, `error`.","schema":{"type":"string"}},{"name":"trace_id","in":"query","description":"Returns only the entries of one request, by its `trace_id`.","schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}},{"name":"since","in":"query","description":"Returns only entries at or after this time.","schema":{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"}},{"name":"until","in":"query","description":"Returns only entries before this time. To page through a fixed window while new entries arrive, set it to the time of your first request.","schema":{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"}}],"responses":{"200":{"description":"One page of log entries.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/api/templates":{"get":{"summary":"List templates","description":"Lists the templates that you can create an App from, by name.","tags":["Templates"],"operationId":"list_templates","security":[{"ApiToken":[]},{"OAuth":["apps:read"]},{"DPoP":["apps:read"]}],"parameters":[{"name":"limit","in":"query","description":"The maximum number of templates to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first templates.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `name`, `created_at`, or `updated_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"name"}},{"name":"name","in":"query","description":"Returns only templates whose `name` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"One page of templates.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"}}}},"/api/feedback":{"post":{"summary":"Send feedback","description":"Sends feedback to the Judt team: a bug, something that was harder than it should be, or anything else. People and agents can both send it, and the team reads every message.\n\nYou can send up to 30 messages an hour. After that, the request returns `429 feedback_limit`.","tags":["Feedback"],"operationId":"send_feedback","security":[{"ApiToken":[]},{"OAuth":["apps:write"]},{"DPoP":["apps:write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"type":{"default":"general","description":"What kind of feedback it is: `bug` for something broken, `friction` for something that works but is hard to use, or `general` for anything else. Default: `general`.","type":"string","enum":["bug","friction","general"]},"message":{"type":"string","minLength":1,"maxLength":10000,"description":"The feedback, up to 10,000 characters. Say what you tried, what happened, and what you expected."}},"required":["message"],"additionalProperties":false}}}},"responses":{"200":{"description":"The saved feedback.","headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Feedback"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"$ref":"#/components/responses/Error"},"429":{"description":"- `feedback_limit`: you've sent 30 messages in the last hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"$ref":"#/components/responses/Error"}}}},"/api/models":{"get":{"summary":"List models","description":"Returns the models that the agent can run on, starting with `auto`, the default. `auto` chooses a model for each message by how hard the task is, and each turn's `model` shows the choice. To choose a model yourself, pass its `id` as `model` when you create a session or send a message.","tags":["Agents"],"operationId":"list_models","security":[{"ApiToken":[]}],"parameters":[],"responses":{"200":{"description":"The available models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelList"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/sessions":{"post":{"summary":"Create a session","description":"Creates an agent session for the App. If you include `message`, the session's first turn starts right away. An App runs one agent at a time, so if another session has an active turn, the request returns `409 agent_busy`.","tags":["Agents"],"operationId":"create_session","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"description":"The session's title. Default: a summary of the first message.","type":"string","minLength":1,"maxLength":200},"model":{"description":"The session's default model. Default: `auto`.","type":"string","maxLength":100},"message":{"description":"A message that starts the session's first turn.","type":"string","minLength":1,"maxLength":32768},"effort":{"description":"The effort for the first turn. Requires `message`.","type":"string","enum":["low","medium","high","xhigh","max"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The new session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}},"headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `agent_busy`: another session of this App has an active turn, and `detail` names it. An App runs one agent at a time.\n- `queue_full`: the session already has five queued turns.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"422":{"description":"- `model_unavailable`: the model is unknown or no longer offered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"- `agent_limit`: the organization has reached its limit of running turns. Turns waiting for input do not count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"List sessions","description":"Lists the App's agent sessions, most recently active first.","tags":["Agents"],"operationId":"list_sessions","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of sessions to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first sessions.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `created_at` or `updated_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"-updated_at"}},{"name":"title","in":"query","description":"Returns only sessions whose `title` contains this text, ignoring case.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"status","in":"query","description":"Returns only sessions with this status.","schema":{"type":"string","enum":["idle","running","waiting"]}}],"responses":{"200":{"description":"One page of sessions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/sessions/{session_id}":{"get":{"summary":"Get a session","description":"Returns the session's status and active turn. If that turn is waiting for input, get the turn to read its `input_request`. You don't need an open event stream to see it.","tags":["Agents"],"operationId":"get_session","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}}],"responses":{"200":{"description":"The session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}},"patch":{"summary":"Update a session","description":"Renames the session or changes its default model.","tags":["Agents"],"operationId":"update_session","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"description":"The session's title.","type":"string","minLength":1,"maxLength":200},"model":{"description":"The session's default model.","type":"string","maxLength":100}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}},"headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"422":{"description":"- `model_unavailable`: the model is unknown or no longer offered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a session","description":"Deletes the session with its turns and events. If the session has an active turn, cancel that turn first. Otherwise, the request returns `409 session_active`.","tags":["Agents"],"operationId":"delete_session","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The deleted session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}},"headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `session_active`: the session has an active turn. Cancel the turn first.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/sessions/{session_id}/turns":{"post":{"summary":"Send a message","description":"Starts a turn with your message. If the session already has an active turn, the new turn waits in a queue behind it. The queue holds up to five turns, and they run in the order that you sent them.\n\nThe response returns when the turn starts or joins the queue, not when it finishes. To follow the turn, stream the session's events or get the turn.","tags":["Agents"],"operationId":"create_turn","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"message":{"type":"string","minLength":1,"maxLength":32768,"description":"What to ask the agent, up to 32 KiB."},"model":{"description":"A model for this turn only. Default: the session's model.","type":"string","maxLength":100},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much the model thinks, from `low` to `max`. Higher effort is slower and uses more tokens, and helps on tasks that need careful reasoning. Default: Auto's choice when `model` is `auto`; otherwise the model's default."}},"required":["message"],"additionalProperties":false}}}},"responses":{"200":{"description":"The new turn, running or queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Turn"}}},"headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `agent_busy`: another session of this App has an active turn, and `detail` names it. An App runs one agent at a time.\n- `queue_full`: the session already has five queued turns.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"},"422":{"description":"- `model_unavailable`: the model is unknown or no longer offered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"- `agent_limit`: the organization has reached its limit of running turns. Turns waiting for input do not count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"List turns","description":"Lists the session's turns, oldest first.","tags":["Agents"],"operationId":"list_turns","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"limit","in":"query","description":"The maximum number of turns to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Where to start: the `next_cursor` of the page before, listed with the same `sort`. Without it, the list starts with the first turns.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"sort","in":"query","description":"A comma-separated list of fields to sort by: `created_at`. Each field sorts ascending. To sort a field descending, prefix it with `-`. Ties are sorted by `id`.","schema":{"type":"string","default":"created_at"}},{"name":"status","in":"query","description":"Returns only turns with this status.","schema":{"type":"string","enum":["queued","running","waiting","completed","failed","cancelled"]}}],"responses":{"200":{"description":"One page of turns.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/sessions/{session_id}/turns/{turn_id}":{"get":{"summary":"Get a turn","description":"Returns the turn's status, any pending input request, its outcome, and its token usage.","tags":["Agents"],"operationId":"get_turn","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"turn_id","in":"path","required":true,"description":"A unique ID that starts with `trn_`.","schema":{"type":"string","pattern":"^trn_[0-9A-Za-z]{24}$","example":"trn_Zj7XhMgplmTJcp5Dk4vcYuk0"}}],"responses":{"200":{"description":"The turn.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Turn"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/sessions/{session_id}/turns/{turn_id}/cancel":{"post":{"summary":"Cancel a turn","description":"Cancels a queued, running, or waiting turn and revokes its credential, so the agent can't start new work. A command that's already running continues until its own timeout. If the turn has already finished, the request returns it unchanged. The session's next queued turn then starts.","tags":["Agents"],"operationId":"cancel_turn","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"turn_id","in":"path","required":true,"description":"A unique ID that starts with `trn_`.","schema":{"type":"string","pattern":"^trn_[0-9A-Za-z]{24}$","example":"trn_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"The canceled turn, or the finished turn unchanged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Turn"}}},"headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/sessions/{session_id}/turns/{turn_id}/decline":{"post":{"summary":"Decline an input request","description":"Declines the turn's pending input request, and the agent resumes. The agent receives your `message`, if you send one, and can continue without the input or end the turn.\n\nTo provide the requested Secrets instead, set each one with `PUT /api/apps/{id}/secrets/{name}`. The turn resumes after all of them are set.","tags":["Agents"],"operationId":"decline_input","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"turn_id","in":"path","required":true,"description":"A unique ID that starts with `trn_`.","schema":{"type":"string","pattern":"^trn_[0-9A-Za-z]{24}$","example":"trn_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key that identifies this request for retries, up to 200 printable characters. A retry with the same key and input returns the first request's result instead of repeating the work. If you omit the key, the API generates one and returns it in the `Idempotency-Key` response header.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"message":{"description":"A message for the agent, such as why you declined.","type":"string","minLength":1,"maxLength":4096}},"additionalProperties":false}}}},"responses":{"200":{"description":"The resumed turn.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Turn"}}},"headers":{"Idempotency-Key":{"schema":{"type":"string"},"description":"The key this request ran under: the one you sent, or one the API generated. To pick up the work after a timeout, retry with this key."},"Idempotency-Retry-Window":{"schema":{"type":"string","example":"2592000"},"description":"How long you can retry with the same key, in seconds: 2592000 (30 days)."}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"description":"- `turn_not_waiting`: the turn has no pending input request.\n- `idempotency_conflict`: the key was used with a different request.\n- `idempotency_expired`: the key is too old to reuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"$ref":"#/components/responses/Error"}}}},"/api/apps/{id}/sessions/{session_id}/events":{"get":{"summary":"List session events","description":"Returns the session's events in sequence order, after `cursor`. The cursor is an event's `sequence`: pass a page's `next_cursor`, or a session's `last_sequence` to get only what happens next.\n\nTo follow the session live, send `Accept: text/event-stream`. The stream sends the following:\n\n- A `session.status` event, without an `id`, whose data is the `Session`.\n- Every event after the cursor, with `id` set to its sequence and `event` set to its type.\n- New events as they happen, and `message.delta` events with partial agent text. A `message.delta` event has no `id`, and its data is `{turn_id, text}`. The log doesn't store it.\n- A heartbeat comment every 15 seconds.\n\nThe server closes the stream within 10 minutes. To resume, reconnect with `Last-Event-ID`.","tags":["Agents"],"operationId":"list_session_events","security":[{"ApiToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A unique ID that starts with `app_`.","schema":{"type":"string","pattern":"^app_[0-9A-Za-z]{24}$","example":"app_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"session_id","in":"path","required":true,"description":"A unique ID that starts with `ses_`.","schema":{"type":"string","pattern":"^ses_[0-9A-Za-z]{24}$","example":"ses_Zj7XhMgplmTJcp5Dk4vcYuk0"}},{"name":"cursor","in":"query","description":"Returns only events whose sequence is greater than this value.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","description":"The maximum number of events to return. Applies only to JSON responses.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"Last-Event-ID","in":"header","description":"For streams, the sequence of the last event that you received. The stream resumes after it, and the header takes precedence over `cursor`.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"One page of events, or the live stream.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentEventList"}},"text/event-stream":{"schema":{"type":"string","description":"Server-sent events, as the operation describes."}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}}}},"components":{"securitySchemes":{"ApiToken":{"type":"http","scheme":"bearer","description":"An API token for one organization. You create and revoke tokens in the dashboard. A token scoped to one App can reach only that App, so give that kind of token to an agent. An organization-wide token can create a short-lived one with `POST /api/tokens`."},"OAuth":{"type":"oauth2","description":"An OAuth access token for one person and organization, sent as a bearer token. https://judt.ai/auth.md describes the ways an agent can get one. Request the resource https://api.judt.ai, and also https://api.judt.ai/mcp to use the same token on the MCP server. The person's role still applies.","flows":{"authorizationCode":{"authorizationUrl":"https://judt.ai/api/auth/oauth2/authorize","tokenUrl":"https://judt.ai/api/auth/oauth2/token","refreshUrl":"https://judt.ai/api/auth/oauth2/token","scopes":{"apps:read":"See the organization's Apps, with their files, deployments, and logs.","apps:write":"Change the organization's Apps: edit files, run commands, deploy, and manage Secrets."}}}},"DPoP":{"type":"http","scheme":"DPoP","description":"An OAuth access token bound to a DPoP key (RFC 9449), sent with a DPoP proof. A token request with a DPoP proof gets one. Personal Agents start at https://judt.ai/.well-known/poppy.json. Scopes are the same as for OAuth."}},"schemas":{"App":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The App's ID, which starts with `app_`."},"name":{"type":"string","description":"The App's display name."},"slug":{"type":"string","description":"The App's label under the App base domain. The App keeps serving at this address after you add a custom domain."},"domain":{"description":"The App's custom domain, or null if it doesn't have one.","type":["string","null"]},"url":{"type":"string","format":"uri","description":"The App's public URL: its custom domain if it has one, or else its slug address."},"template":{"type":"object","properties":{"id":{"type":"string","description":"The template's ID."},"version":{"type":"integer","exclusiveMinimum":0,"description":"The template's version.","example":3}},"required":["id","version"],"additionalProperties":false,"description":"The template that the App was created from. Until the sandbox first starts, `version` is the template's version when the App was created. After that, it's the version that the sandbox was set up from."},"sandbox_status":{"type":"string","enum":["stopped","starting","running","stopping","failed"],"description":"The sandbox's state. Any request that needs the sandbox starts it, and the sandbox stops on its own when it's idle.\n- `stopped`: the sandbox isn't running.\n- `starting`: the sandbox is starting.\n- `running`: the sandbox is running.\n- `stopping`: the sandbox is committing changes and stopping.\n- `failed`: the most recent start failed. The API retries while the sandbox is needed, and any request that needs the sandbox retries right away."},"sandbox_error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."},"at":{"type":"string","format":"date-time","description":"When the attempt failed.","example":"2026-10-03T14:01:30.000Z"},"retry_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the API tries again on its own, or null if it won't. If it's null, the next request that needs the sandbox tries again."}},"required":["code","message","at","retry_at"],"additionalProperties":false},{"type":"null"}],"description":"The most recent failed attempt to start or stop the sandbox, or null. It's cleared after an attempt succeeds."},"production":{"type":"object","properties":{"visibility":{"type":"string","enum":["public","password","private"],"description":"Who can reach the published App:\n- `public`: anyone.\n- `password`: anyone with the App's password, for 60 minutes at a time.\n- `private`: members of the App's organization, who sign in through judt.\n\nAccess tokens open password-protected and private Apps. The sandbox is always private to the organization."},"password_updated_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the App's password was last set, or null if `visibility` isn't `password`."},"status":{"type":"string","enum":["unpublished","starting","ready","idle","stopped","unhealthy","failed","unknown"],"description":"The state of the published App:\n- `unpublished`: nothing is published, either before the first deployment or after you unpublish the App. The next deployment publishes it.\n- `starting`: the published version is starting.\n- `ready`: the App is serving requests.\n- `idle`: the App is asleep between requests. The next request wakes it.\n- `stopped`: the App isn't serving until something changes. `error` says why.\n- `unhealthy`: the App is running but isn't healthy. `error` says why.\n- `failed`: the App failed. `error` says why.\n- `unknown`: the runtime hasn't reported the App's state recently."},"deployment_id":{"description":"The ID of the deployment that's serving, or null if nothing is published.","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the App stopped, failed, or is unhealthy, or null otherwise."},"observed_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the runtime last reported the App's state, or null if there's no recent report."}},"required":["visibility","password_updated_at","status","deployment_id","error","observed_at"],"additionalProperties":false,"description":"The published App and who can reach it."},"deleted_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the App was deleted, or null if it isn't deleted. A deleted App stays readable for 30 days."},"created_at":{"type":"string","format":"date-time","description":"When the App was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the App was last changed.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","name","slug","domain","url","template","sandbox_status","sandbox_error","production","deleted_at","created_at","updated_at"],"additionalProperties":false},"AppList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The App's ID, which starts with `app_`."},"name":{"type":"string","description":"The App's display name."},"slug":{"type":"string","description":"The App's label under the App base domain. The App keeps serving at this address after you add a custom domain."},"domain":{"description":"The App's custom domain, or null if it doesn't have one.","type":["string","null"]},"url":{"type":"string","format":"uri","description":"The App's public URL: its custom domain if it has one, or else its slug address."},"template":{"type":"object","properties":{"id":{"type":"string","description":"The template's ID."},"version":{"type":"integer","exclusiveMinimum":0,"description":"The template's version.","example":3}},"required":["id","version"],"additionalProperties":false,"description":"The template that the App was created from. Until the sandbox first starts, `version` is the template's version when the App was created. After that, it's the version that the sandbox was set up from."},"sandbox_status":{"type":"string","enum":["stopped","starting","running","stopping","failed"],"description":"The sandbox's state. Any request that needs the sandbox starts it, and the sandbox stops on its own when it's idle.\n- `stopped`: the sandbox isn't running.\n- `starting`: the sandbox is starting.\n- `running`: the sandbox is running.\n- `stopping`: the sandbox is committing changes and stopping.\n- `failed`: the most recent start failed. The API retries while the sandbox is needed, and any request that needs the sandbox retries right away."},"sandbox_error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."},"at":{"type":"string","format":"date-time","description":"When the attempt failed.","example":"2026-10-03T14:01:30.000Z"},"retry_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the API tries again on its own, or null if it won't. If it's null, the next request that needs the sandbox tries again."}},"required":["code","message","at","retry_at"],"additionalProperties":false},{"type":"null"}],"description":"The most recent failed attempt to start or stop the sandbox, or null. It's cleared after an attempt succeeds."},"production":{"type":"object","properties":{"visibility":{"type":"string","enum":["public","password","private"],"description":"Who can reach the published App:\n- `public`: anyone.\n- `password`: anyone with the App's password, for 60 minutes at a time.\n- `private`: members of the App's organization, who sign in through judt.\n\nAccess tokens open password-protected and private Apps. The sandbox is always private to the organization."},"password_updated_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the App's password was last set, or null if `visibility` isn't `password`."},"status":{"type":"string","enum":["unpublished","starting","ready","idle","stopped","unhealthy","failed","unknown"],"description":"The state of the published App:\n- `unpublished`: nothing is published, either before the first deployment or after you unpublish the App. The next deployment publishes it.\n- `starting`: the published version is starting.\n- `ready`: the App is serving requests.\n- `idle`: the App is asleep between requests. The next request wakes it.\n- `stopped`: the App isn't serving until something changes. `error` says why.\n- `unhealthy`: the App is running but isn't healthy. `error` says why.\n- `failed`: the App failed. `error` says why.\n- `unknown`: the runtime hasn't reported the App's state recently."},"deployment_id":{"description":"The ID of the deployment that's serving, or null if nothing is published.","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the App stopped, failed, or is unhealthy, or null otherwise."},"observed_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the runtime last reported the App's state, or null if there's no recent report."}},"required":["visibility","password_updated_at","status","deployment_id","error","observed_at"],"additionalProperties":false,"description":"The published App and who can reach it."},"deleted_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the App was deleted, or null if it isn't deleted. A deleted App stays readable for 30 days."},"created_at":{"type":"string","format":"date-time","description":"When the App was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the App was last changed.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","name","slug","domain","url","template","sandbox_status","sandbox_error","production","deleted_at","created_at","updated_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"DeploymentList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The deployment's ID, which starts with `dep_`."},"app_id":{"type":"string","description":"The ID of the App that the deployment belongs to."},"commit_id":{"description":"The ID of the deployed commit, or null if Judt didn't make the commit, such as a template's first commit.","type":["string","null"]},"commit_sha":{"type":"string","description":"The Git SHA of the deployed commit."},"status":{"type":"string","enum":["publishing","starting","succeeded","failed","disabled"],"description":"Where the deployment is:\n- `publishing`: the version is being built and published, after any database migrations that must run first.\n- `starting`: production points to this version, and it's starting.\n- `succeeded`: the version started serving. It stays `succeeded` after a later deployment replaces it or the App is unpublished. To see which deployment is serving now, check the App's `production.deployment_id`.\n- `failed`: the version wasn't published, or it didn't start serving. `error` says why.\n- `disabled`: the App was deleted while the version was publishing, so it never served."},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the deployment failed, or null if it didn't fail."},"migrations":{"anyOf":[{"type":"object","properties":{"databases":{"type":"array","items":{"type":"object","properties":{"database_name":{"type":"string","description":"The database's name."},"phase":{"type":"string","enum":["before","after","applied"],"description":"When the database is migrated:\n- `before`: the live version already uses the database, so it's migrated before this version is published.\n- `after`: this version adds the database, so it's migrated after this version is serving.\n- `applied`: its migrations match the live version's and are already applied."}},"required":["database_name","phase"],"additionalProperties":false},"description":"The databases that the source declares."},"before":{"anyOf":[{"type":"string","enum":["requested","succeeded","failed"]},{"type":"null"}],"description":"The status of the migrations that run before publishing: `requested`, `succeeded`, or `failed`. Null if there are none."},"after":{"anyOf":[{"type":"string","enum":["requested","succeeded","failed"]},{"type":"null"}],"description":"The status of the migrations that run after this version is serving: `requested`, `succeeded`, or `failed`. Null if there are none."},"detail":{"description":"The failing database and the migration tool's output, or null if no migration failed.","type":["string","null"]}},"required":["databases","before","after","detail"],"additionalProperties":false},{"type":"null"}],"description":"The D1 database migrations that the source declares, or null until the source is checked."},"published_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the version was published, or null if it isn't published."},"created_at":{"type":"string","format":"date-time","description":"When the deployment was created.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","commit_id","commit_sha","status","error","migrations","published_at","created_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"CommandResult":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The command's action ID, which starts with `act_`."},"app_id":{"type":"string","description":"The ID of the App that ran the command."},"status":{"type":"string","enum":["completed","timed_out","interrupted"],"description":"How the command ended:\n- `completed`: the command exited, and `exit_code` holds its exit code.\n- `timed_out`: the command ran past `timeout_seconds` and was stopped.\n- `interrupted`: the command ended without a result, for example because the sandbox was lost. Its effects are unknown, and it's never run again."},"exit_code":{"anyOf":[{"type":"integer","example":0},{"type":"null"}],"description":"The command's exit code, or null if it was interrupted."},"output":{"type":"string","description":"The command's captured output, up to the output limit. If the command was interrupted, the output is partial."},"truncated":{"type":"boolean","description":"True if the output passed the limit and was cut off; false otherwise."},"started_at":{"type":"string","format":"date-time","description":"When the command started.","example":"2026-10-03T14:01:30.000Z"},"finished_at":{"type":"string","format":"date-time","description":"When the command ended.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","status","exit_code","output","truncated","started_at","finished_at"],"additionalProperties":false},"Commit":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The commit's ID, which starts with `cmt_`. To restore the commit, pass this ID to `POST /api/apps/{id}/restore`. To publish it, pass it to `POST /api/apps/{id}/deployments`."},"app_id":{"type":"string","description":"The ID of the App that the commit belongs to."},"commit_sha":{"type":"string","description":"The Git commit SHA."},"message":{"type":"string","description":"The commit message."},"created_by":{"type":"string","description":"The ID of the user who created the commit."},"created_at":{"type":"string","format":"date-time","description":"When the commit was created.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","commit_sha","message","created_by","created_at"],"additionalProperties":false},"CommitList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The commit's ID, which starts with `cmt_`. To restore the commit, pass this ID to `POST /api/apps/{id}/restore`. To publish it, pass it to `POST /api/apps/{id}/deployments`."},"app_id":{"type":"string","description":"The ID of the App that the commit belongs to."},"commit_sha":{"type":"string","description":"The Git commit SHA."},"message":{"type":"string","description":"The commit message."},"created_by":{"type":"string","description":"The ID of the user who created the commit."},"created_at":{"type":"string","format":"date-time","description":"When the commit was created.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","commit_sha","message","created_by","created_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"Deployment":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The deployment's ID, which starts with `dep_`."},"app_id":{"type":"string","description":"The ID of the App that the deployment belongs to."},"commit_id":{"description":"The ID of the deployed commit, or null if Judt didn't make the commit, such as a template's first commit.","type":["string","null"]},"commit_sha":{"type":"string","description":"The Git SHA of the deployed commit."},"status":{"type":"string","enum":["publishing","starting","succeeded","failed","disabled"],"description":"Where the deployment is:\n- `publishing`: the version is being built and published, after any database migrations that must run first.\n- `starting`: production points to this version, and it's starting.\n- `succeeded`: the version started serving. It stays `succeeded` after a later deployment replaces it or the App is unpublished. To see which deployment is serving now, check the App's `production.deployment_id`.\n- `failed`: the version wasn't published, or it didn't start serving. `error` says why.\n- `disabled`: the App was deleted while the version was publishing, so it never served."},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the deployment failed, or null if it didn't fail."},"migrations":{"anyOf":[{"type":"object","properties":{"databases":{"type":"array","items":{"type":"object","properties":{"database_name":{"type":"string","description":"The database's name."},"phase":{"type":"string","enum":["before","after","applied"],"description":"When the database is migrated:\n- `before`: the live version already uses the database, so it's migrated before this version is published.\n- `after`: this version adds the database, so it's migrated after this version is serving.\n- `applied`: its migrations match the live version's and are already applied."}},"required":["database_name","phase"],"additionalProperties":false},"description":"The databases that the source declares."},"before":{"anyOf":[{"type":"string","enum":["requested","succeeded","failed"]},{"type":"null"}],"description":"The status of the migrations that run before publishing: `requested`, `succeeded`, or `failed`. Null if there are none."},"after":{"anyOf":[{"type":"string","enum":["requested","succeeded","failed"]},{"type":"null"}],"description":"The status of the migrations that run after this version is serving: `requested`, `succeeded`, or `failed`. Null if there are none."},"detail":{"description":"The failing database and the migration tool's output, or null if no migration failed.","type":["string","null"]}},"required":["databases","before","after","detail"],"additionalProperties":false},{"type":"null"}],"description":"The D1 database migrations that the source declares, or null until the source is checked."},"published_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the version was published, or null if it isn't published."},"created_at":{"type":"string","format":"date-time","description":"When the deployment was created.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","commit_id","commit_sha","status","error","migrations","published_at","created_at"],"additionalProperties":false},"Event":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The event's ID, which starts with `evt_`."},"app_id":{"type":"string","description":"The ID of the App that the event belongs to."},"subject":{"type":"string","description":"What the event is about: `sandbox`, `deletion`, a deployment ID, which starts with `dep_`, or an action ID, which starts with `act_`."},"category":{"type":"string","enum":["sandbox","deployment","deletion"],"description":"What area of the App the event is about:\n- `sandbox`: the sandbox and its actions, such as file writes, commands, commits, and restores.\n- `deployment`: a deployment.\n- `deletion`: the App's deletion."},"kind":{"type":"string","enum":["transition","action","error","blocked","resolved"],"description":"What kind of event it is:\n- `transition`: a status changed.\n- `action`: the API took a step.\n- `error`: an attempt failed.\n- `blocked`: progress is waiting on something.\n- `resolved`: an earlier error or block cleared."},"from_status":{"description":"For a transition, the status before it. Otherwise, null.","type":["string","null"]},"to_status":{"description":"For a transition, the status after it. Otherwise, null.","type":["string","null"]},"action":{"description":"The step that the API took, such as `create_sandbox` or `pause`, or null.","type":["string","null"]},"code":{"description":"A stable code for an error or a block, or null.","type":["string","null"]},"message":{"description":"A readable description of the event, or null.","type":["string","null"]},"detail":{"description":"More information, such as a provider message or the end of a command's output, or null. Never includes Secret values.","type":["string","null"]},"failure_count":{"anyOf":[{"type":"integer","example":1},{"type":"null"}],"description":"How many times in a row the step has failed, or null."},"request_id":{"description":"The `X-Request-ID` of the request that caused the event, or null if the API did the work in the background.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","subject","category","kind","from_status","to_status","action","code","message","detail","failure_count","request_id","created_at"],"additionalProperties":false},"EventList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The event's ID, which starts with `evt_`."},"app_id":{"type":"string","description":"The ID of the App that the event belongs to."},"subject":{"type":"string","description":"What the event is about: `sandbox`, `deletion`, a deployment ID, which starts with `dep_`, or an action ID, which starts with `act_`."},"category":{"type":"string","enum":["sandbox","deployment","deletion"],"description":"What area of the App the event is about:\n- `sandbox`: the sandbox and its actions, such as file writes, commands, commits, and restores.\n- `deployment`: a deployment.\n- `deletion`: the App's deletion."},"kind":{"type":"string","enum":["transition","action","error","blocked","resolved"],"description":"What kind of event it is:\n- `transition`: a status changed.\n- `action`: the API took a step.\n- `error`: an attempt failed.\n- `blocked`: progress is waiting on something.\n- `resolved`: an earlier error or block cleared."},"from_status":{"description":"For a transition, the status before it. Otherwise, null.","type":["string","null"]},"to_status":{"description":"For a transition, the status after it. Otherwise, null.","type":["string","null"]},"action":{"description":"The step that the API took, such as `create_sandbox` or `pause`, or null.","type":["string","null"]},"code":{"description":"A stable code for an error or a block, or null.","type":["string","null"]},"message":{"description":"A readable description of the event, or null.","type":["string","null"]},"detail":{"description":"More information, such as a provider message or the end of a command's output, or null. Never includes Secret values.","type":["string","null"]},"failure_count":{"anyOf":[{"type":"integer","example":1},{"type":"null"}],"description":"How many times in a row the step has failed, or null."},"request_id":{"description":"The `X-Request-ID` of the request that caused the event, or null if the API did the work in the background.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","subject","category","kind","from_status","to_status","action","code","message","detail","failure_count","request_id","created_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"Log":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"An opaque, stable ID. While you page newest first, new entries shift later pages, so skip entries whose ID you've already seen."},"created_at":{"type":"string","format":"date-time","description":"When the entry was recorded, to the nanosecond.","example":"2026-10-03T14:01:30.000Z"},"environment":{"type":"string","enum":["production","development"],"description":"Where the entry came from: `production` for the published App, or `development` for its sandbox."},"source":{"type":"string","enum":["request","app","dev_server","runtime","deployment","platform"],"description":"What wrote the entry:\n- `request`: a request that the App served, from its access log.\n- `app`: a line that the App's code wrote, or an exception that it threw.\n- `dev_server`: the sandbox's dev server building, reloading, or exiting.\n- `runtime`: production instances starting, stopping, or failing.\n- `deployment`: a deployment's build output or failure.\n- `platform`: a notice from Judt, such as `telemetry.capped` when the App logged more than its per-minute limit."},"level":{"type":"string","enum":["debug","info","warn","error"],"description":"The entry's level. A line that the App wrote takes its console method's level, so `console.log` is `info`. A request is `error` at 5xx or when no response was sent, and `warn` at 4xx."},"event":{"description":"What the entry records, such as `exception`, `dev.build_failed`, `dev.exited`, `deployment.failed`, or `instance.transition`. A `dev.build_failed` entry includes the bundler's report. Null for requests and for the App's own output.","type":["string","null"]},"message":{"type":"string","description":"The entry's text."},"request":{"anyOf":[{"type":"object","properties":{"method":{"type":"string","description":"The HTTP method."},"path":{"type":"string","description":"The request path, without the query string."},"host":{"description":"The requested host, or null.","type":["string","null"]},"status":{"anyOf":[{"type":"integer","example":200},{"type":"null"}],"description":"The response status, or null if no response was sent."},"duration_ms":{"type":"number","minimum":0,"description":"How long the request took, in milliseconds.","example":42}},"required":["method","path","host","status","duration_ms"],"additionalProperties":false},{"type":"null"}],"description":"The request that the entry belongs to: the request itself, or the request being served when the App wrote the line or threw the exception. Null otherwise."},"trace_id":{"description":"An ID shared by a request and the entries it caused. To see one request's entries, filter by it.","type":["string","null"]},"attributes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]},"description":"Structured details that vary by event, such as `process.exit.code`, `deployment.id`, `dev.reload`, `dev.serving_previous_build`, and `error.type`."}},"required":["id","created_at","environment","source","level","event","message","request","trace_id","attributes"],"additionalProperties":false},"LogList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"An opaque, stable ID. While you page newest first, new entries shift later pages, so skip entries whose ID you've already seen."},"created_at":{"type":"string","format":"date-time","description":"When the entry was recorded, to the nanosecond.","example":"2026-10-03T14:01:30.000Z"},"environment":{"type":"string","enum":["production","development"],"description":"Where the entry came from: `production` for the published App, or `development` for its sandbox."},"source":{"type":"string","enum":["request","app","dev_server","runtime","deployment","platform"],"description":"What wrote the entry:\n- `request`: a request that the App served, from its access log.\n- `app`: a line that the App's code wrote, or an exception that it threw.\n- `dev_server`: the sandbox's dev server building, reloading, or exiting.\n- `runtime`: production instances starting, stopping, or failing.\n- `deployment`: a deployment's build output or failure.\n- `platform`: a notice from Judt, such as `telemetry.capped` when the App logged more than its per-minute limit."},"level":{"type":"string","enum":["debug","info","warn","error"],"description":"The entry's level. A line that the App wrote takes its console method's level, so `console.log` is `info`. A request is `error` at 5xx or when no response was sent, and `warn` at 4xx."},"event":{"description":"What the entry records, such as `exception`, `dev.build_failed`, `dev.exited`, `deployment.failed`, or `instance.transition`. A `dev.build_failed` entry includes the bundler's report. Null for requests and for the App's own output.","type":["string","null"]},"message":{"type":"string","description":"The entry's text."},"request":{"anyOf":[{"type":"object","properties":{"method":{"type":"string","description":"The HTTP method."},"path":{"type":"string","description":"The request path, without the query string."},"host":{"description":"The requested host, or null.","type":["string","null"]},"status":{"anyOf":[{"type":"integer","example":200},{"type":"null"}],"description":"The response status, or null if no response was sent."},"duration_ms":{"type":"number","minimum":0,"description":"How long the request took, in milliseconds.","example":42}},"required":["method","path","host","status","duration_ms"],"additionalProperties":false},{"type":"null"}],"description":"The request that the entry belongs to: the request itself, or the request being served when the App wrote the line or threw the exception. Null otherwise."},"trace_id":{"description":"An ID shared by a request and the entries it caused. To see one request's entries, filter by it.","type":["string","null"]},"attributes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]},"description":"Structured details that vary by event, such as `process.exit.code`, `deployment.id`, `dev.reload`, `dev.serving_previous_build`, and `error.type`."}},"required":["id","created_at","environment","source","level","event","message","request","trace_id","attributes"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"File":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"path":{"type":"string","description":"The file's path from the App's root."},"size":{"type":"integer","minimum":0,"description":"The file's size, in bytes.","example":2048}},"required":["path","size"],"additionalProperties":false},"FileContent":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"path":{"type":"string","description":"The file's path from the App's root."},"size":{"type":"integer","minimum":0,"description":"The file's size, in bytes.","example":2048},"binary":{"type":"boolean","description":"True if the file isn't UTF-8 text; false otherwise. For a binary file, `content` is left out. To get the bytes, send `Accept: application/octet-stream`."},"content":{"description":"The file's text, or only the lines that `lines` asked for. Left out for a binary file.","type":"string"},"line_count":{"description":"How many lines the whole file has. A final line ending doesn't start another line. Left out for a binary file.","type":"integer","minimum":0},"lines":{"description":"The lines that `content` holds. Present only when the request sets `lines`.","type":"object","properties":{"start":{"type":"integer","minimum":1,"description":"The first line in `content`, numbered from 1."},"end":{"type":"integer","minimum":1,"description":"The last line in `content`."}},"required":["start","end"],"additionalProperties":false}},"required":["path","size","binary"],"additionalProperties":false},"FileList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file's path from the App's root."},"size":{"type":"integer","minimum":0,"description":"The file's size, in bytes.","example":2048},"match_count":{"description":"How many lines in the file match. Present only when the request sets `content` or `pattern`.","type":"integer","minimum":1},"matches":{"description":"The first 20 matching lines, in line order. Present only when the request sets `content` or `pattern`.","type":"array","items":{"type":"object","properties":{"line":{"type":"integer","minimum":1,"description":"The line number, from 1."},"text":{"type":"string","description":"The line's text, without its line ending. A line longer than 300 characters is cut to the 300 around its first match, with `…` marking each cut."}},"required":["line","text"],"additionalProperties":false}}},"required":["path","size"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"GitResult":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"output":{"type":"string","description":"Git's output for the requested `mode`."}},"required":["output"],"additionalProperties":false},"PreviewResult":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","format":"uri","description":"A URL that opens the sandbox's dev server."},"expires_at":{"type":"string","format":"date-time","description":"When the URL stops working.","example":"2026-10-03T14:01:30.000Z"}},"required":["url","expires_at"],"additionalProperties":false},"DeepLink":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","format":"uri","description":"The link, for a person to open. It grants no access: the person signs in as usual and needs to be a member of the App's organization."},"expires_at":{"type":"string","format":"date-time","description":"When the link stops working, 24 hours after it was created.","example":"2026-10-03T14:01:30.000Z"}},"required":["url","expires_at"],"additionalProperties":false},"VisitorToken":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"token":{"type":"string","description":"The visitor token. Send it in the `X-Judt-Access-Token` header or the `access_token` URL parameter."},"expires_at":{"type":"string","format":"date-time","description":"When the token expires, five minutes after it was created.","example":"2026-10-03T14:01:30.000Z"}},"required":["token","expires_at"],"additionalProperties":false},"CreatedApiToken":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The token's ID, which starts with `tok_`."},"organization_id":{"type":"string","description":"The organization that the token belongs to."},"app_id":{"description":"The one App an App-scoped token reaches; null for the whole organization","type":["string","null"]},"name":{"type":"string","description":"The token's name."},"preview":{"type":"string","description":"`judt_***` and the token's last four characters, to tell tokens apart."},"expires_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the token stops working; null if it never expires."},"last_used_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the token was last used, to the minute; null if it hasn't been used."},"created_at":{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},"token":{"type":"string","description":"The token. Send it as a bearer token in the `Authorization` header, to the API or the MCP server. This is the only response that includes it."}},"required":["id","organization_id","app_id","name","preview","expires_at","last_used_at","created_at","token"],"additionalProperties":false},"Secret":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The Secret's ID, which starts with `sec_`."},"app_id":{"type":"string","description":"The ID of the App that the Secret belongs to."},"name":{"type":"string","description":"The Secret's name. The App reads the value as an environment variable with this name."},"created_at":{"type":"string","format":"date-time","description":"When the Secret was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the Secret's name or value last changed.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","name","created_at","updated_at"],"additionalProperties":false},"SecretList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The Secret's ID, which starts with `sec_`."},"app_id":{"type":"string","description":"The ID of the App that the Secret belongs to."},"name":{"type":"string","description":"The Secret's name. The App reads the value as an environment variable with this name."},"created_at":{"type":"string","format":"date-time","description":"When the Secret was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the Secret's name or value last changed.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","name","created_at","updated_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"Template":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The template's ID, which starts with `tpl_`."},"name":{"type":"string","description":"The template's name."},"description":{"type":"string","description":"What the template contains."},"version":{"type":"integer","exclusiveMinimum":0,"description":"The template's version, which increases each time its source changes.","example":3},"created_at":{"type":"string","format":"date-time","description":"When the template was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the template last changed.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","name","description","version","created_at","updated_at"],"additionalProperties":false},"Feedback":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The feedback's ID, which starts with `fbk_`."},"type":{"type":"string","enum":["bug","friction","general"],"description":"What kind of feedback it is."},"message":{"type":"string","description":"The feedback."},"created_at":{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"}},"required":["id","type","message","created_at"],"additionalProperties":false},"Me":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","description":"The person's ID."},"name":{"type":"string","description":"The person's name."},"email":{"type":"string","description":"The person's email address."}},"required":["id","name","email"],"additionalProperties":false,"description":"The person the credential acts for. An API token acts for the person who created it."},"organization":{"type":"object","properties":{"id":{"type":"string","description":"The organization's ID."},"name":{"type":"string","description":"The organization's name."},"slug":{"type":"string","description":"The organization's slug."}},"required":["id","name","slug"],"additionalProperties":false,"description":"The organization that the credential reaches."},"role":{"type":"string","enum":["owner","admin","member"],"description":"The person's role in the organization. Only an `owner` or `admin` can create, delete, publish, or unpublish Apps, or change their production settings."},"credential":{"type":"object","properties":{"type":{"type":"string","enum":["api_token","oauth","session"],"description":"How the request authenticated: an API token, an OAuth access token, or a dashboard session."},"id":{"description":"The API token's ID, which starts with `tok_`. `null` for other credentials.","type":["string","null"]},"app_id":{"description":"The ID of the one App that an App-scoped API token reaches. `null` if the credential reaches every App in the organization.","type":["string","null"]},"client_id":{"description":"The OAuth client's ID. `null` for other credentials.","type":["string","null"]},"scopes":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The OAuth access token's scopes. `null` for other credentials, which aren't limited by scope."},"expires_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the API token expires. `null` if it doesn't expire, or for other credentials."}},"required":["type","id","app_id","client_id","scopes","expires_at"],"additionalProperties":false,"description":"The credential that the request sent."}},"required":["user","organization","role","credential"],"additionalProperties":false},"TemplateList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The template's ID, which starts with `tpl_`."},"name":{"type":"string","description":"The template's name."},"description":{"type":"string","description":"What the template contains."},"version":{"type":"integer","exclusiveMinimum":0,"description":"The template's version, which increases each time its source changes.","example":3},"created_at":{"type":"string","format":"date-time","description":"When the template was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the template last changed.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","name","description","version","created_at","updated_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"ModelList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The model's ID: `auto`, or `PROVIDER/MODEL`. Pass it as `model` on a session or a turn."},"name":{"type":"string","description":"The model's display name."},"provider":{"description":"The company that provides the model, or null for `auto`.","type":["string","null"]},"context_window":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"example":200000},{"type":"null"}],"description":"The maximum number of tokens the model can read in one request, or null for `auto`."},"default":{"type":"boolean","description":"True if a session gets this model when it doesn't name one; false otherwise."}},"required":["id","name","provider","context_window","default"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false},"Session":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The session's ID, which starts with `ses_`."},"app_id":{"type":"string","description":"The ID of the App that the session belongs to."},"title":{"type":"string","description":"The session's title."},"model":{"type":"string","description":"The default model for the session's turns: `auto` or a model ID."},"status":{"type":"string","enum":["idle","running","waiting"],"description":"The session's state:\n- `idle`: no turn is running or queued.\n- `running`: a turn is running or queued.\n- `waiting`: the active turn needs input from a person. The turn's `input_request` field describes the input."},"active_turn_id":{"description":"The ID of the running or waiting turn, or null if there isn't one.","type":["string","null"]},"last_sequence":{"type":"integer","minimum":0,"description":"The sequence of the session's newest event. A transcript that includes this event is up to date.","example":48},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens used by all of the session's turns."},"created_by":{"description":"The ID of the user who created the session, or null if that user deleted their account.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","description":"When the session was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the session last changed. Sessions are listed by this time.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","title","model","status","active_turn_id","last_sequence","usage","created_by","created_at","updated_at"],"additionalProperties":false},"SessionList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The session's ID, which starts with `ses_`."},"app_id":{"type":"string","description":"The ID of the App that the session belongs to."},"title":{"type":"string","description":"The session's title."},"model":{"type":"string","description":"The default model for the session's turns: `auto` or a model ID."},"status":{"type":"string","enum":["idle","running","waiting"],"description":"The session's state:\n- `idle`: no turn is running or queued.\n- `running`: a turn is running or queued.\n- `waiting`: the active turn needs input from a person. The turn's `input_request` field describes the input."},"active_turn_id":{"description":"The ID of the running or waiting turn, or null if there isn't one.","type":["string","null"]},"last_sequence":{"type":"integer","minimum":0,"description":"The sequence of the session's newest event. A transcript that includes this event is up to date.","example":48},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens used by all of the session's turns."},"created_by":{"description":"The ID of the user who created the session, or null if that user deleted their account.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","description":"When the session was created.","example":"2026-10-03T14:01:30.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the session last changed. Sessions are listed by this time.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","app_id","title","model","status","active_turn_id","last_sequence","usage","created_by","created_at","updated_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"Turn":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The turn's ID, which starts with `trn_`."},"session_id":{"type":"string","description":"The ID of the session that the turn belongs to."},"status":{"type":"string","enum":["queued","running","waiting","completed","failed","cancelled"],"description":"The turn's state:\n- `queued`: waiting behind the session's active turn.\n- `running`: the agent is working.\n- `waiting`: the agent needs input from a person, which `input_request` describes.\n- `completed`, `failed`, or `cancelled`: the turn has ended, and its status doesn't change again."},"message":{"type":"string","description":"The message that started the turn."},"model":{"type":"string","description":"The model that the turn runs on. If `requested_model` is `auto`, this is the model that Auto chose."},"requested_model":{"type":"string","description":"The model that the turn asked for: `auto` or a model ID."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much the model thinks on this turn, from `low` to `max`. Auto chooses it by how much reasoning the task needs, unless the request set `effort`; with a chosen model, it is the model's default unless the request set it."},"input_request":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The input request's ID."},"kind":{"type":"string","const":"secrets","description":"The kind of input that the agent needs."},"secrets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The Secret's name."},"provided":{"type":"boolean","description":"True if the Secret is set; false otherwise."}},"required":["name","provided"],"additionalProperties":false},"description":"The Secrets that the agent needs. To provide one, set it with `PUT /api/apps/{id}/secrets/{name}`. The turn resumes after all of them are set. The agent never receives their values."},"reason":{"type":"string","description":"Why the agent needs the input, in the agent's words."},"expires_at":{"type":"string","format":"date-time","description":"When the request expires. If no one provides or declines the input by then, the turn fails with `input_expired`.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","kind","secrets","reason","expires_at"],"additionalProperties":false,"description":"A request for input from a person."},{"type":"null"}],"description":"The input that the agent is waiting for, or null if the turn isn't waiting."},"base_commit_id":{"description":"The commit that the turn started from. To undo the turn, pass this ID as `commit_id` to `POST /api/apps/{id}/restore`.","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the turn failed, or null if it didn't fail. The code is one of the following:\n- `agent_error`: the agent stopped unexpectedly.\n- `app_deleted`: the App was deleted.\n- `input_expired`: no one answered the input request in time.\n- `interrupted`: the turn was interrupted repeatedly without progress.\n- `turn_budget_exceeded`: the turn ran out of time.\n- `user_deleted`: the user the turn acts for deleted their account."},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."},"created_by":{"description":"The ID of the user the turn acts for, or null if that user deleted their account. The turn has that user's current role.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","description":"When the turn was created.","example":"2026-10-03T14:01:30.000Z"},"started_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the turn started running, or null if it hasn't started."},"ended_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the turn ended, or null if it hasn't ended."}},"required":["id","session_id","status","message","model","requested_model","effort","input_request","base_commit_id","error","usage","created_by","created_at","started_at","ended_at"],"additionalProperties":false},"TurnList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The turn's ID, which starts with `trn_`."},"session_id":{"type":"string","description":"The ID of the session that the turn belongs to."},"status":{"type":"string","enum":["queued","running","waiting","completed","failed","cancelled"],"description":"The turn's state:\n- `queued`: waiting behind the session's active turn.\n- `running`: the agent is working.\n- `waiting`: the agent needs input from a person, which `input_request` describes.\n- `completed`, `failed`, or `cancelled`: the turn has ended, and its status doesn't change again."},"message":{"type":"string","description":"The message that started the turn."},"model":{"type":"string","description":"The model that the turn runs on. If `requested_model` is `auto`, this is the model that Auto chose."},"requested_model":{"type":"string","description":"The model that the turn asked for: `auto` or a model ID."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much the model thinks on this turn, from `low` to `max`. Auto chooses it by how much reasoning the task needs, unless the request set `effort`; with a chosen model, it is the model's default unless the request set it."},"input_request":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The input request's ID."},"kind":{"type":"string","const":"secrets","description":"The kind of input that the agent needs."},"secrets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The Secret's name."},"provided":{"type":"boolean","description":"True if the Secret is set; false otherwise."}},"required":["name","provided"],"additionalProperties":false},"description":"The Secrets that the agent needs. To provide one, set it with `PUT /api/apps/{id}/secrets/{name}`. The turn resumes after all of them are set. The agent never receives their values."},"reason":{"type":"string","description":"Why the agent needs the input, in the agent's words."},"expires_at":{"type":"string","format":"date-time","description":"When the request expires. If no one provides or declines the input by then, the turn fails with `input_expired`.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","kind","secrets","reason","expires_at"],"additionalProperties":false,"description":"A request for input from a person."},{"type":"null"}],"description":"The input that the agent is waiting for, or null if the turn isn't waiting."},"base_commit_id":{"description":"The commit that the turn started from. To undo the turn, pass this ID as `commit_id` to `POST /api/apps/{id}/restore`.","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the turn failed, or null if it didn't fail. The code is one of the following:\n- `agent_error`: the agent stopped unexpectedly.\n- `app_deleted`: the App was deleted.\n- `input_expired`: no one answered the input request in time.\n- `interrupted`: the turn was interrupted repeatedly without progress.\n- `turn_budget_exceeded`: the turn ran out of time.\n- `user_deleted`: the user the turn acts for deleted their account."},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."},"created_by":{"description":"The ID of the user the turn acts for, or null if that user deleted their account. The turn has that user's current role.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","description":"When the turn was created.","example":"2026-10-03T14:01:30.000Z"},"started_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the turn started running, or null if it hasn't started."},"ended_at":{"anyOf":[{"type":"string","format":"date-time","example":"2026-10-03T14:01:30.000Z"},{"type":"null"}],"description":"When the turn ended, or null if it hasn't ended."}},"required":["id","session_id","status","message","model","requested_model","effort","input_request","base_commit_id","error","usage","created_by","created_at","started_at","ended_at"],"additionalProperties":false},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"AgentEvent":{"$schema":"https://json-schema.org/draft/2020-12/schema","oneOf":[{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"message"},"role":{"type":"string","enum":["user","agent"],"description":"Who wrote the message."},"text":{"type":"string","description":"The message text."}},"required":["sequence","turn_id","created_at","type","role","text"],"additionalProperties":false,"title":"message","description":"A message from the user or the agent."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.started"},"model":{"type":"string","description":"The model that the turn runs on."},"base_commit_id":{"description":"The commit that the turn started from.","type":["string","null"]}},"required":["sequence","turn_id","created_at","type","model","base_commit_id"],"additionalProperties":false,"title":"turn.started","description":"The turn started running."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"tool.started"},"call_id":{"type":"string","description":"The ID that links a tool call's `tool.started` and `tool.completed` events."},"tool":{"type":"string","description":"The tool's name."},"input":{"description":"The tool's input. File contents are replaced by their path and size, and other inputs are truncated to 4 KiB."}},"required":["sequence","turn_id","created_at","type","call_id","tool","input"],"additionalProperties":false,"title":"tool.started","description":"The agent called a tool."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"tool.completed"},"call_id":{"type":"string","description":"The ID that links a tool call's `tool.started` and `tool.completed` events."},"tool":{"type":"string","description":"The tool's name."},"status":{"type":"string","enum":["succeeded","failed"],"description":"Whether the tool call succeeded."},"output":{"description":"The tool's output, truncated to 16 KiB.","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the tool call failed, or null if it succeeded."},"commit_id":{"description":"The commit that the tool created, if any.","type":"string"},"deployment_id":{"description":"The deployment that the tool started, if any.","type":"string"}},"required":["sequence","turn_id","created_at","type","call_id","tool","status","output","error"],"additionalProperties":false,"title":"tool.completed","description":"A tool call finished."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"input.requested"},"input_request":{"type":"object","properties":{"id":{"type":"string","description":"The input request's ID."},"kind":{"type":"string","const":"secrets","description":"The kind of input that the agent needs."},"secrets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The Secret's name."},"provided":{"type":"boolean","description":"True if the Secret is set; false otherwise."}},"required":["name","provided"],"additionalProperties":false},"description":"The Secrets that the agent needs. To provide one, set it with `PUT /api/apps/{id}/secrets/{name}`. The turn resumes after all of them are set. The agent never receives their values."},"reason":{"type":"string","description":"Why the agent needs the input, in the agent's words."},"expires_at":{"type":"string","format":"date-time","description":"When the request expires. If no one provides or declines the input by then, the turn fails with `input_expired`.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","kind","secrets","reason","expires_at"],"additionalProperties":false,"description":"A request for input from a person."}},"required":["sequence","turn_id","created_at","type","input_request"],"additionalProperties":false,"title":"input.requested","description":"The agent needs input from a person, and the turn is waiting."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"input.resolved"},"input_request_id":{"type":"string","description":"The ID of the input request."},"resolution":{"type":"string","enum":["provided","declined","expired"],"description":"How the request ended."},"message":{"description":"The message sent with a decline, if any.","type":"string"}},"required":["sequence","turn_id","created_at","type","input_request_id","resolution"],"additionalProperties":false,"title":"input.resolved","description":"An input request was provided, declined, or expired, and the turn resumed or failed."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.completed"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."}},"required":["sequence","turn_id","created_at","type","usage"],"additionalProperties":false,"title":"turn.completed","description":"The turn finished successfully."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.failed"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."},"error":{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false,"description":"Why the turn failed."}},"required":["sequence","turn_id","created_at","type","usage","error"],"additionalProperties":false,"title":"turn.failed","description":"The turn failed."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.cancelled"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."}},"required":["sequence","turn_id","created_at","type","usage"],"additionalProperties":false,"title":"turn.cancelled","description":"The turn was canceled."}]},"AgentEventList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"results":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"message"},"role":{"type":"string","enum":["user","agent"],"description":"Who wrote the message."},"text":{"type":"string","description":"The message text."}},"required":["sequence","turn_id","created_at","type","role","text"],"additionalProperties":false,"title":"message","description":"A message from the user or the agent."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.started"},"model":{"type":"string","description":"The model that the turn runs on."},"base_commit_id":{"description":"The commit that the turn started from.","type":["string","null"]}},"required":["sequence","turn_id","created_at","type","model","base_commit_id"],"additionalProperties":false,"title":"turn.started","description":"The turn started running."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"tool.started"},"call_id":{"type":"string","description":"The ID that links a tool call's `tool.started` and `tool.completed` events."},"tool":{"type":"string","description":"The tool's name."},"input":{"description":"The tool's input. File contents are replaced by their path and size, and other inputs are truncated to 4 KiB."}},"required":["sequence","turn_id","created_at","type","call_id","tool","input"],"additionalProperties":false,"title":"tool.started","description":"The agent called a tool."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"tool.completed"},"call_id":{"type":"string","description":"The ID that links a tool call's `tool.started` and `tool.completed` events."},"tool":{"type":"string","description":"The tool's name."},"status":{"type":"string","enum":["succeeded","failed"],"description":"Whether the tool call succeeded."},"output":{"description":"The tool's output, truncated to 16 KiB.","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why the tool call failed, or null if it succeeded."},"commit_id":{"description":"The commit that the tool created, if any.","type":"string"},"deployment_id":{"description":"The deployment that the tool started, if any.","type":"string"}},"required":["sequence","turn_id","created_at","type","call_id","tool","status","output","error"],"additionalProperties":false,"title":"tool.completed","description":"A tool call finished."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"input.requested"},"input_request":{"type":"object","properties":{"id":{"type":"string","description":"The input request's ID."},"kind":{"type":"string","const":"secrets","description":"The kind of input that the agent needs."},"secrets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The Secret's name."},"provided":{"type":"boolean","description":"True if the Secret is set; false otherwise."}},"required":["name","provided"],"additionalProperties":false},"description":"The Secrets that the agent needs. To provide one, set it with `PUT /api/apps/{id}/secrets/{name}`. The turn resumes after all of them are set. The agent never receives their values."},"reason":{"type":"string","description":"Why the agent needs the input, in the agent's words."},"expires_at":{"type":"string","format":"date-time","description":"When the request expires. If no one provides or declines the input by then, the turn fails with `input_expired`.","example":"2026-10-03T14:01:30.000Z"}},"required":["id","kind","secrets","reason","expires_at"],"additionalProperties":false,"description":"A request for input from a person."}},"required":["sequence","turn_id","created_at","type","input_request"],"additionalProperties":false,"title":"input.requested","description":"The agent needs input from a person, and the turn is waiting."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"input.resolved"},"input_request_id":{"type":"string","description":"The ID of the input request."},"resolution":{"type":"string","enum":["provided","declined","expired"],"description":"How the request ended."},"message":{"description":"The message sent with a decline, if any.","type":"string"}},"required":["sequence","turn_id","created_at","type","input_request_id","resolution"],"additionalProperties":false,"title":"input.resolved","description":"An input request was provided, declined, or expired, and the turn resumed or failed."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.completed"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."}},"required":["sequence","turn_id","created_at","type","usage"],"additionalProperties":false,"title":"turn.completed","description":"The turn finished successfully."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.failed"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."},"error":{"type":"object","properties":{"code":{"type":"string","description":"A stable code for the error."},"message":{"type":"string","description":"A readable explanation of the error."}},"required":["code","message"],"additionalProperties":false,"description":"Why the turn failed."}},"required":["sequence","turn_id","created_at","type","usage","error"],"additionalProperties":false,"title":"turn.failed","description":"The turn failed."},{"type":"object","properties":{"sequence":{"type":"integer","exclusiveMinimum":0,"description":"The event's position in the session's log.","example":12},"turn_id":{"type":"string","description":"The ID of the turn that the event belongs to."},"created_at":{"type":"string","format":"date-time","description":"When the event happened.","example":"2026-10-03T14:01:30.000Z"},"type":{"type":"string","const":"turn.cancelled"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model read.","example":12840},"output_tokens":{"type":"integer","minimum":0,"description":"The number of tokens the model wrote.","example":1260},"cache_read_tokens":{"type":"integer","minimum":0,"description":"The number of input tokens read from the prompt cache.","example":9600}},"required":["input_tokens","output_tokens","cache_read_tokens"],"additionalProperties":false,"description":"The tokens that the turn used."}},"required":["sequence","turn_id","created_at","type","usage"],"additionalProperties":false,"title":"turn.cancelled","description":"The turn was canceled."}]},"description":"The results in this page."},"has_more":{"type":"boolean","description":"True if more results follow this page; false otherwise."},"next_cursor":{"description":"Where the next page starts: to get it, list again with this value as `cursor`. A cursor from the last page later finds results added after it. Null if the list is empty.","type":["string","null"]}},"required":["results","has_more","next_cursor"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"A stable code for the error, such as `not_found`."},"message":{"type":"string","description":"A readable explanation of the error."},"detail":{"type":"string","description":"More information, if any: the sandbox's error, a provider message, or, for a `504`, how far the work got and the ID of its latest event."},"request_id":{"type":"string","description":"The request's ID. Include it when you contact support."}}}}}},"responses":{"Error":{"description":"An error, with its code and the request ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}