REST API
Call the Kortix API directly: base URL, authentication, errors, retries, and where the full route reference lives.
The Kortix REST API is the HTTP interface to accounts, projects, sessions, secrets, connectors, triggers, and Apps. The web app, the CLI, the SDK, and the Kortix MCP server all call it. Use it directly from any language that can send an HTTPS request with a bearer token.
The shortest path: create a personal access key, list your projects, and start a session.
kortix tokens new my-script --expires 30d # prints kortix_pat_… once
export TOKEN=kortix_pat_...
curl -s https://api.kortix.com/v1/projects \
-H "Authorization: Bearer $TOKEN"
curl -s https://api.kortix.com/v1/projects/<project_id>/sessions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"initial_prompt": "Summarize the open change requests."}'
GET /v1/projects returns an array of projects, each with project_id. The
session create returns 201 with the session, including session_id and
branch_name. It returns 202 with status, command_id, and session_id
when Kortix queues the create. Read the session with
GET /v1/projects/<project_id>/sessions/<session_id> until it appears.
Pick a surface
All four surfaces reach the same API with the same permissions.
| Surface | Use it for | Credential |
|---|---|---|
| REST API | Any language. Integrations, scripts, and servers that are not TypeScript. | A bearer token from Authenticate |
SDK (@kortix/sdk) |
TypeScript and React. Sessions with live streaming, typed errors, automatic retries, and the session runtime. | getToken returns a bearer token |
CLI (kortix) |
Terminals, CI jobs, and agents inside a session sandbox. | kortix login, or a token in CI |
| Kortix MCP server | Claude, Cursor, ChatGPT, and other MCP clients that act as you. Its search_api, describe_api, and call_api tools read and call this API. |
OAuth sign-in from the client |
Prefer the SDK for anything that drives a session: it owns the start, the readiness wait, the event stream, and the runtime routes that the REST reference does not list.
Base URL and reference
| Deployment | Base URL |
|---|---|
| Kortix Cloud | https://api.kortix.com/v1 |
| Self-hosted | https://api.<domain>/v1 by default. See Self-hosting. |
Every deployment serves its own route reference:
GET /v1/docs: the interactive reference. The API reference tab at the top of these docs opens the Kortix Cloud copy.GET /v1/openapi.json: the OpenAPI 3.1 document. Itsserversentry is the origin you requested it from, so a client generated from a self-hosted instance points at that instance.GET /v1/health: no token needed. Returnsstatus,version, andcommit.
The reference leaves out operator routes. A self-host operator turns off both
/v1/docs and /v1/openapi.json with OPENAPI_PUBLIC_DOCS=false in the API
environment.
Authenticate
Send one header on every request:
Authorization: Bearer <token>
The token decides who the request acts as. Every Kortix credential starts with a prefix that names its kind. Create and manage them on Sign-in, SSO & API keys.
| Prefix | Credential | Acts as |
|---|---|---|
kortix_pat_ |
Personal access key (Settings → Personal access keys, kortix tokens new, kortix login) |
You, with your roles. A key bound to one project gets 403 everywhere else. |
kortix_sa_ |
Service account key (Account settings → API keys) | The service account. It has no access until someone assigns it a role. |
kortix_oat_ |
OAuth access token from Sign in with Kortix. It lasts 1 hour. A kortix_ort_ refresh token gets a new one. |
The person who approved the app. |
kortix_gw_ |
Project gateway key (kortix gateway keys new) |
The LLM gateway inference routes of one project, and nothing else. See Models. |
kortix_scim_ |
SCIM token (Account settings → Identity) | Directory sync under /scim/v2 for one account. |
kortix_pat_ in KORTIX_TOKEN |
Session token. Kortix sets it in every session sandbox. | The agent of that one session, limited to its project. It ends with the session. |
An OAuth token without the kortix scope reaches only /v1/accounts/me,
/v1/oauth/userinfo, and /v1/oauth/revoke. Other routes answer 403, and
message starts with insufficient_scope.
A person’s access token from headless sign-in also works as a bearer token. It acts as that person.
A missing or invalid token returns 401. A 401 with
code: "session_token_revoked" means the credential is revoked, expired, or
belongs to a closed session. It never works again: stop retrying and get a new
token.
The same tokens open the project’s git origin. See Git.
Errors
An error response is JSON. Every body has error. The generic shape adds
message and status. A typed error adds code and can add more fields.
| Field | Type | Meaning |
|---|---|---|
error |
true or text |
Text is a human-readable reason. |
message |
text | A human-readable reason. |
code |
text | A stable machine code. Branch on this, never on the text. |
status |
number | The HTTP status, repeated. |
Some errors add fields. A failed body, query, or path validation returns:
{ "error": true, "message": "Validation failed", "status": 400, "issues": [ … ] }
issues lists each invalid field with its path and message.
| Status | Meaning | What to do |
|---|---|---|
400 |
The request is not valid. | Read issues or message. Fix the request. |
401 |
No valid credential. | Send a valid token. Stop on session_token_revoked. |
402 |
The account cannot pay for this, or its plan does not include it. | Read code: insufficient_credits, subscription_required, no_account, entitlement_required, budget_exceeded. A billing 402 (insufficient_credits, subscription_required, no_account) also carries billing_state, balance, and account_id. Branch on billing_state to choose between subscribe and top up. See Common error codes. |
403 |
The credential is valid, but the action is not allowed. | A permission denial names the action it needed and a code. feature_disabled adds feature, the flag key. |
404 |
The object does not exist, or you may not see it. Kortix answers 404, not 403, for an object you may not see. |
Check the id. Check your access to the object. |
409 |
The request conflicts with the current state. | Read code. Reload the object and decide again. |
422 |
The request is valid, but the operation cannot apply. | Read code, for example CR_HEAD_NOT_AHEAD. |
429 |
A rate limit. | Wait Retry-After seconds. See Retries and idempotency. |
500 |
A defect in Kortix. | Do not retry in a loop. Report it with the X-Request-Id response header. |
502, 503, 504 |
A temporary failure. A 503 usually carries Retry-After. |
Retry reads. Check the state before you repeat a write. |
Every response carries an X-Request-Id header. Quote it when you report a
problem.
Common error codes
These are the codes clients branch on most. The linked page explains each fix.
code |
Status | Cause | Fix on |
|---|---|---|---|
session_token_revoked |
401 |
The credential can never work again. | Sign-in, SSO & API keys |
insufficient_credits |
402 |
The account’s credit balance is empty. | Plans & credits |
subscription_required |
402 |
The account has no plan for Kortix-billed usage. | Plans & credits |
no_account |
402 |
The account has no credit account yet. Its setup is not complete. | Plans & credits |
entitlement_required |
402 |
The plan lacks sso, scim, rbac, auditAccess, or branding. The body names entitlement. |
Sign-in, SSO & API keys |
budget_exceeded |
402 |
A model budget set to block reached its limit. | Models |
feature_disabled |
403 |
The project has this feature flag off. The body names feature. |
Projects |
feature_operator_only |
403 |
Only Kortix (a platform admin) may change this flag, for example apps. |
Projects |
account_mfa_required |
403 |
The account requires two-factor, and this sign-in did not use it. | Sign-in, SSO & API keys |
expiry_required, expiry_too_far |
400 |
A new key breaks the account’s key rules. | Sign-in, SSO & API keys |
INVALID_IDEMPOTENCY_KEY |
400 |
The Idempotency-Key header is not 1 to 255 characters of A-Z a-z 0-9 . _ : + / = -. |
Retries and idempotency |
IDEMPOTENCY_KEY_CONFLICT, IDEMPOTENCY_*_CONFLICT |
409 |
The key is in use for another request, or for this one with different connector_bindings, secrets, runtime_context, or provider_secret_pools. |
Retries and idempotency |
IDEMPOTENCY_KEY_SESSION_DELETED |
409 |
The key belongs to a session that was deleted. | Use a new key. |
INVALID_SESSION_RUNTIME_CONTEXT, INVALID_SESSION_CONNECTOR_BINDINGS, INVALID_SESSION_SECRETS |
400 |
A session create field is malformed. | Sessions from code |
project_archived |
404 |
The project was deleted. | Projects |
request_deadline |
503 |
A request ran past the API’s 25-second processing limit. | Retry the read. Check the state before you repeat a write. |
git_mirror_unavailable |
503 |
The project repository is temporarily unreachable. | Retry after Retry-After. Git |
repository_push_rejected |
409 |
The git host refused a push because of branch protection or repository rules. | Git |
CR_HEAD_NOT_AHEAD |
422 |
The change request branch has no commits ahead of its base. | Git |
CR_SELF_MERGE_REFUSED |
403 |
A session tried to merge its own change request without a project.gitops.merge grant. |
Git |
MANIFEST_INVALID |
422 |
The change request makes kortix.yaml invalid. The merge is blocked. issues lists the errors. |
kortix.yaml reference |
PERSONAL_SECRET_REQUIRES_PRIVATE_SESSION |
409 |
You tried to share a session that holds a secret shared only with you. | Secrets |
project_secret_write_limit |
429 |
The project wrote secrets more than 100 times in one hour. | Secrets |
app_config_invalid |
400 |
An App release config is not valid. issues lists the errors. |
Apps |
app_auth_required |
401 |
An App URL needs a viewer that may open it. | Apps |
A connector call reports its outcome in reason, not code, inside
{ ok: false, status: "denied" | "error", reason }. The reasons, such as
connector_not_connected and policy_block, are on
Connectors. LLM provider and model failures
are on Models.
Retries and idempotency
The SDK retries reads, and a DELETE that got no response. It never repeats
any other write. In your own client, decide per response:
| Response | Retry |
|---|---|
GET or HEAD with 502, 503, or 504 |
Yes, after Retry-After when it is present. The SDK retries 2 times, after 250 ms and 500 ms. |
A write with 502, 503, or 504 |
Read the state first. |
429 |
Yes, after Retry-After seconds. The body repeats the wait as retry_after_seconds. |
| A write with no response (network failure) | Read the state first, or resend with the same Idempotency-Key where the route takes one. |
400, 401, 402, 403, 404, 409, 422 |
No. Change the request or the state first. |
500 |
No. Report it with X-Request-Id. |
Idempotency-Key
Two create routes deduplicate a retried request:
| Route | Where the key goes | Format |
|---|---|---|
POST /v1/projects/<project_id>/sessions (start a session) |
Idempotency-Key header |
1 to 255 characters of A-Z a-z 0-9 . _ : + / = - |
POST /v1/projects/provision (create a project with a new repository) |
idempotency_key body field |
Up to 200 characters of A-Z a-z 0-9 . _ : - |
Send a new random value, such as a UUID, for each intent. Send the same value
again when you retry that intent. A repeated session create returns the original
session instead of starting a second sandbox. Session keys are unique across
all of Kortix. A key that another project or account already used returns
409 IDEMPOTENCY_KEY_CONFLICT.
Pagination
A list route that pages takes limit and cursor. Pass the next cursor back as
cursor. No next cursor means you have the last page.
| List | Next cursor | limit |
|---|---|---|
GET /v1/projects/<project_id>/sessions |
X-Next-Cursor response header |
Default 50, maximum 200 |
GET /v1/accounts/<account_id>/audit |
next_cursor in the body |
Default 50, maximum 200 |
The session list also returns an ETag. Send it back as If-None-Match and an
unchanged list answers 304 with no body.
Rate limits
A rate-limited route returns X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset (seconds until the window resets). When the limit is
reached it returns 429 with Retry-After and this body:
{ "error": "rate_limit_exceeded", "message": "Rate limit exceeded. Please retry shortly.", "retry_after_seconds": 12 }
| Route | Limit |
|---|---|
Headless sign-in routes under /v1/auth |
30 requests per minute per IP address. The 429 body is { "error": "over_request_rate_limit", "error_description": "…" } with Retry-After, and no X-RateLimit-* headers. |
The sandbox proxy, /v1/p/<external_id>/<port>/*: previews and the session runtime |
600 requests per minute per sandbox |
| Secret writes in one project | 100 per hour, code: "project_secret_write_limit" |
What the reference does not cover
The reference does not list the session runtime routes. The surfaces below also take a different credential or protocol than a plain bearer call:
| Surface | Use instead |
|---|---|
The session runtime under /v1/p/<external_id>/8000/kortix/runtime/*: prompts, the event stream, files, and the transcript of a running session. The path takes the sandbox’s external id at the provider, not the session_id. Read it from sandbox_url in kortix sessions info <session_id> --json (after /v1/p/), or from sandboxId in the SDK’s ensureReady(). |
The SDK session handle, kortix.session(projectId, sessionId). See Sessions from code. |
SCIM 2.0 under /scim/v2/accounts/<account_id>. It takes a kortix_scim_ token, not a personal key. |
SCIM provisioning |
Inbound trigger webhooks, POST /v1/webhooks/projects/<project_id>/<slug>. They authenticate with the trigger secret, not a Kortix token: send an HMAC signature (X-Kortix-Signature or X-Hub-Signature-256), X-Kortix-Token, or Authorization: Bearer <secret>. |
Webhooks |
The LLM gateway’s OpenAI- and Anthropic-compatible endpoints. They take a kortix_gw_ key. |
Models |
The project git origin, https://api.kortix.com/v1/git/<project_id>.git. |
Git |
Machine-readable docs
These docs are also published as plain Markdown for coding agents and LLM tools:
| URL | Content |
|---|---|
https://kortix.com/llms.txt |
An index of every public page, docs included, with a link to its Markdown |
https://kortix.com/llms-full.txt |
All public pages in one file |
https://kortix.com/markdown/docs/<path>.md |
One page. For example, this page is /markdown/docs/api.md. |
Any /docs/<path> URL, requested with Accept: text/markdown |
The same page as Markdown |
Point a coding agent at https://kortix.com/llms.txt for the concepts and at
https://api.kortix.com/v1/openapi.json for the exact routes. An MCP client that uses the
Kortix MCP server reads the route reference itself through
search_api and describe_api.