> ## Documentation Index
> Fetch the complete documentation index at: https://docs.judt.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API reference

> The base URL, authentication details, conventions, and error codes of the Judt API.

This tab documents each endpoint of the Judt API.

* **REST API base URL**: `https://api.judt.ai` (OpenAPI description at `https://api.judt.ai/api/openapi.json`)
* **MCP server URL**: `https://api.judt.ai/mcp`
* **Authorization server and dashboard**: `https://judt.ai` (agent instructions at [`https://judt.ai/auth.md`](https://judt.ai/auth.md))

For an overview of how to authenticate, see [Authentication](https://docs.judt.ai/manage/authentication.md).

## Authentication details

Send an API key or an OAuth access token in the `Authorization` header of each request:

```http theme={null}
Authorization: Bearer TOKEN
```

### API keys

* **Format**: `judt_` followed by 43 characters.
* **Organization-wide keys**: The keys that you create on the [API keys](https://judt.ai/api-keys) page. They can access all Apps in the organization, create new Apps (`POST /api/apps`), and create App tokens (`POST /api/tokens`).
* **App tokens**: Bound to one App. You create an App token with `POST /api/tokens` (valid for `60` to `86400` seconds).
  * `GET /api/apps` returns only that App.
  * `POST /api/apps` and `POST /api/tokens` return `403 app_scoped_token`.
  * Requests to `/api/apps/{id}` for a different App return `404 not_found`.
  * `GET /api/templates` and `GET /api/models` remain available.

### OAuth scopes, endpoints, and lifetimes

An agent gets an OAuth 2.0 token through MCP OAuth (authorization code grant), self-registration (`service_auth`), or the device authorization grant ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628)). For the full procedure, see [`https://judt.ai/auth.md`](https://judt.ai/auth.md).

| Scope | Dashboard label | Permitted requests |
| - | - | - |
| `apps:read` | **View Apps** | All `GET` and `HEAD` requests (Apps, files, Git, commits, deployments, Secret names, templates, models, sessions, events, and logs). |
| `apps:write` | **Change Apps** | All `POST`, `PUT`, `PATCH`, and `DELETE` requests, except `POST /api/tokens` (which requires an organization-wide API key). |
| `offline_access` | — | Returns a refresh token with the access token. |

* **Metadata endpoints**:
  * Protected resource metadata ([RFC 9728](https://www.rfc-editor.org/rfc/rfc9728)): `https://api.judt.ai/.well-known/oauth-protected-resource` (API) and `https://api.judt.ai/.well-known/oauth-protected-resource/mcp` (MCP server).
  * Authorization server metadata ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)): `https://judt.ai/.well-known/oauth-authorization-server`.
  * Personal Agent Protocol (Poppy 0.1) metadata: `https://judt.ai/.well-known/poppy.json`.
* **OAuth endpoints**:
  * Agent self-registration: `POST https://judt.ai/api/auth/agent/identity`
  * Client registration ([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)): `POST https://judt.ai/api/auth/oauth2/register`
  * Device code ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628)): `POST https://judt.ai/api/auth/device/code`
  * Token and refresh: `POST https://judt.ai/api/auth/oauth2/token`
  * Revocation: `POST https://judt.ai/api/auth/oauth2/revoke`
* **Resource parameters ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707))**: Send one `resource` parameter for each server that you use (`resource=https://api.judt.ai` for the REST API, `resource=https://api.judt.ai/mcp` for the MCP server). If you do not send `resource`, the token works only on `https://api.judt.ai`.
* **Token lifetimes**:
  * **Access token**: 1 hour (`3600` seconds).
  * **Refresh token**: 30 days (`2592000` seconds). Each refresh response returns a new `refresh_token`. Keep the new token and discard the old token.
* **DPoP key binding ([RFC 9449](https://www.rfc-editor.org/rfc/rfc9449))**: If you send a `DPoP` proof header when you request or refresh a token, the token is bound to your key. Send each API or MCP request with `Authorization: DPoP ACCESS_TOKEN` and a new `DPoP` proof header.

### Organization roles

Each API key and OAuth token acts as you in one organization and has your role in that organization:

| Role | Allowed actions |
| - | - |
| `owner`, `admin` | All actions: create, update, and delete Apps (`POST /api/apps`, `PATCH /api/apps/{id}`, `DELETE /api/apps/{id}`), set and delete Secrets (`PUT` and `DELETE` on `/api/apps/{id}/secrets`), and publish or unpublish deployments (`POST /api/apps/{id}/deployments`, `POST /api/apps/{id}/unpublish`). |
| `member` | Read all App resources (including Secret names), write and delete sandbox files, run commands, create and restore commits, create sandbox preview links, create 5-minute visitor tokens (`POST /api/apps/{id}/visitor-tokens`), and run agent sessions and turns. Owner-only and admin-only endpoints return `403 forbidden`. |

## Sandboxes, retries, and time limits

* **Sandboxes**: `POST /api/apps` reserves the App's address, but it does not start a sandbox. A request that uses files, Git, commands, commits, restores, previews, or deployments starts the sandbox first. The sandbox stops automatically when it is idle.
* **Idempotency and retries**: Each write request runs under an `Idempotency-Key`, and each response returns the key in a header. You can send your own key (up to 200 printable characters), or the API creates one. For 30 days, a retry with the same key and input returns the first request's result instead of repeating the work. `PUT` and `DELETE` on a Secret do not use a key.
  * `503` or `504`: The API recorded the action, but the action is not finished. Retry with the **same** `Idempotency-Key` to get the result. A new key runs the action again.
  * `4xx` or `502`: The result is final. Correct the cause before you send a new request.
* **Time limits**:
  * File writes, commits, restores, and `POST /api/apps/{id}/stop`: `120` seconds (plus up to `120` seconds if the request starts a stopped sandbox).
  * Commands (`POST /api/apps/{id}/commands`): `timeout_seconds` (up to `120` seconds), plus `60` seconds to stop the command and save its output.
  * Publishing, unpublishing, deleting an App, and changing production settings: `540` seconds.

## Pagination

A list returns one page, in the form `{"results": [], "has_more": false, "next_cursor": null}`. To get the next page, send `next_cursor` as the `cursor` query parameter while `has_more` is `true`. Use `limit` to set the page size. The default is `50`, and the maximum is `100`.

## Errors

When a request fails, the API returns a JSON body in this format:

```json theme={null}
{
  "error": {
    "code": "CODE",
    "message": "MESSAGE",
    "detail": "DETAIL",
    "request_id": "REQUEST_ID"
  }
}
```

The `code` value does not change. Check `code`, not `message`. The `detail` field is optional.

### Authentication and authorization errors

When 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](https://www.rfc-editor.org/rfc/rfc9728)).

| Status | Error code / header | Cause | Action |
| - | - | - | - |
| `401` | `unauthenticated` | The request has no credential, or the API key or OAuth token is invalid, expired, or revoked. | Send a valid API key or OAuth access token. |
| `401` | `invalid_token` | `WWW-Authenticate` error when an API key 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. |
| `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. |
| `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`. |
| `403` | `app_scoped_token` | An App token called `POST /api/apps` or `POST /api/tokens`. | Use an organization-wide API key. |
| `403` | `api_token_required` | An OAuth access token called `POST /api/tokens`. | Use an organization-wide API key. |
| `403` | `sign_in_required` | A Personal Agent Session Token was used before the user signed in or after the user signed out. | Tell the user to sign in to the Session. |
| `403` | `forbidden` | Your organization role (`member`) does not allow this action. | Ask an organization `owner` or `admin` to do the action. |

### General API errors

| Status | Error code | Cause |
| - | - | - |
| `400` | `invalid_request` | A request body, query parameter, or header is not valid. |
| `400` | `invalid_pattern` | The `glob` or `pattern` filter in `GET /api/apps/{id}/files` is not valid (`detail` gives the reason). |
| `400` | `line_out_of_range` | The `lines` parameter starts after the last line of the file. |
| `400` | `binary_file` | The `lines` parameter was sent for a binary file. |
| `400` | `invalid_path`, `reserved_path` | The file path is not a relative source path, or it is a [reserved path](https://docs.judt.ai/manage/concepts/sandbox.md#reserved-paths). |
| `400` | `password_required`, `password_not_applicable` | `password` visibility needs a password, or a password was sent for another visibility. |
| `403` | `app_limit_reached` | The organization has the maximum number of Apps for the beta. Delete an App, or send feedback to ask for more. |
| `404` | `not_found`, `file_not_found`, `template_not_found` | The App, file, Secret, commit, deployment, session, turn, or template does not exist, or the API key is scoped to a different App. |
| `409` | `idempotency_conflict` | The `Idempotency-Key` was already used with a different request. |
| `409` | `idempotency_expired` | The `Idempotency-Key` is older than 30 days. |
| `409` | `slug_unavailable` | Another App (or a deleted App) uses the slug, or the slug is reserved. |
| `409` | `deployment_unsettled` | Another deployment of the App is in progress (`detail` gives the deployment ID). |
| `409` | `app_unpublished` | `POST /api/apps/{id}/visitor-tokens` was called on an App that is not published. |
| `409` | `nothing_to_deploy` | The sandbox has no commit to deploy. |
| `409` | `agent_busy` | Another session of the App has an active turn. An App runs one agent at a time. |
| `409` | `queue_full`, `session_active`, `turn_not_waiting` | The session has too many queued turns, has an active turn that stops the request, or has no pending input request. |
| `410` | `app_deleted` | The App is deleted. Only its tombstone and history are readable. |
| `412` | `precondition_failed` | The `If-Match` or `If-None-Match` ETag header did not match the file. |
| `413` | `file_limit`, `body_too_large` | The file is larger than the limit (1 MiB through the API), or the JSON body is too large. |
| `415` | `json_required` | The request must have `Content-Type: application/json`. |
| `422` | `preview_failed` | The sandbox dev server cannot build or start the App (`detail` contains the build output). |
| `422` | `commit_not_found`, `build_failed`, `entrypoint_missing`, `unsupported_entrypoint`, `unsupported_dependencies`, `lockfile_missing`, `dependency_install_failed`, `source_limit`, `unsupported_deploy_configuration`, `build_timeout`, `migration_failed` | The source cannot be deployed or restored as it is (`message` and `detail` say what to change). |
| `429` | `agent_limit`, `feedback_limit` | The organization has too many running agent turns, or you sent too much feedback in the last hour. |
| `502` | `deploy_failed`, `publish_timeout`, `activation_timeout`, `activation_failed`, `sandbox_incompatible`, `helper_output_too_large` | The sandbox or runtime host failed the action. Retrying does not change the result. |
| `503` | `provider_unavailable`, `preview_not_ready`, `writers_not_stopped`, `recovery_degraded` | The sandbox cannot do the action now, or a recorded action is not finished. Retry with the same `Idempotency-Key`. |
| `504` | `request_timeout` | The request reached its time limit while the work continues (`detail` gives the latest progress and event ID). Retry with the same `Idempotency-Key`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.