Environments API
The /v1/projects and /v1/environments endpoints on ai.codai.ro, with request and response examples.
Base URL https://ai.codai.ro, Authorization: Bearer codai_…, JSON in and out. The live daemon view (online, listening ports, git state, locks, attached clients) is served by the relay at https://relay.codai.ro/v1/environments/{id}/status with the same key.
Conventions, all taken from the gateway:
- Project and environment rows are returned camelCase, as stored; request bodies and member/secret lists are snake_case.
- A project or environment you cannot see answers
404(never403), so ids cannot be enumerated. A role violation on something you can see answers403. - Errors use the gateway envelope
{ "error": { "message", "type", "code" } }; validation errors are400 bad_request. - Every mutation appends an
environment_eventsrow.
Endpoints
| Method | Path | Min. role | Notes |
|---|---|---|---|
GET | /v1/projects | — | Yours plus your orgs', newest first |
POST | /v1/projects | — | org_id needs org owner/admin |
GET | /v1/projects/{id} | visible | Project + all its environments |
PATCH | /v1/projects/{id} | project owner / org admin | Slug cannot change |
DELETE | /v1/projects/{id} | project owner / org admin | 409 project_has_environments until all are destroyed |
GET | /v1/environments | — | ?project_id=, ?for_user_id=me |
POST | /v1/environments | project visible | Managed providers are feature-gated (403) |
GET | /v1/environments/{id} | viewer | Env + role + isolation + members + ports + last 20 events |
PATCH | /v1/environments/{id} | maintainer | name, idle_timeout_minutes |
POST | /v1/environments/{id}/start · stop · archive | maintainer | 409 invalid_transition |
POST | /v1/environments/{id}/destroy | owner | |
POST | /v1/environments/{id}/enroll-token | maintainer | BYO only (409 not_byo) |
POST | /v1/environments/enroll | token | Called by codaid, no API key |
GET | /v1/environments/{id}/members | viewer | |
POST | /v1/environments/{id}/members | maintainer | owner needs owner |
PUT | /v1/environments/{id}/members/me/ssh-key | developer | Your own key |
POST | /v1/environments/{id}/members/me/ssh-cert | developer | { public_key } → 12 h OpenSSH user cert for your Linux user (certificate, principal, valid_before, host); 503 ssh_ca_unconfigured without a CA |
GET | /v1/environments/{id}/ssh-ca | viewer | { public_keys } of the SSH user CA the VMs trust; 404 when no CA is configured |
PATCH · DELETE | /v1/environments/{id}/members/{userId} | maintainer | Leaving yourself needs no role |
GET | /v1/environments/{id}/ports | viewer | Published ports |
PUT · DELETE | /v1/environments/{id}/ports/{port} | developer | public needs maintainer |
GET | /v1/environments/{id}/secrets | developer | Names only |
PUT · DELETE | /v1/environments/{id}/secrets/{name} | maintainer | Names are ENV_STYLE |
GET | /v1/environments/{id}/tasks | viewer | Newest 50 agent tasks |
POST | /v1/environments/{id}/tasks | developer | 202; env must be running (409 environment_not_running) |
GET | /v1/environments/{id}/tasks/{taskId} | viewer | |
POST | /v1/environments/{id}/exec | developer | One relay op, blocking; 409 environment_offline, 504 host_timeout |
GET | /v1/environments/{id}/files | developer | ?path= (default your workspace) |
GET · PUT | /v1/environments/{id}/files/{path} | developer | UTF-8 text; read ≤ 512 KB, write ≤ 2 MB (413) |
POST | /v1/environments/{id}/jobs | developer | Background job, returns job_id at once |
GET | /v1/environments/{id}/jobs/{jobId} | developer | Status + output (?since=, ?tail=) |
Create a project
curl https://ai.codai.ro/v1/projects \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "name": "brivio", "repo_url": "https://github.com/acme/brivio", "isolation": "shared_vm" }'201:
{
"project": {
"id": "2b6d1c0e-4f3a-4b9c-8d7e-1a2b3c4d5e6f",
"ownerUserId": "5f0c…", "orgId": null,
"name": "brivio", "slug": "brivio",
"repoUrl": "https://github.com/acme/brivio", "defaultBranch": "main", "devcontainerPath": null,
"isolation": "shared_vm", "settings": {},
"createdAt": "2026-09-25T09:00:00.000Z", "updatedAt": "2026-09-25T09:00:00.000Z"
}
}slug defaults to the slugified name and must be unique among your projects (409 slug_taken). isolation is shared_vm (default), container or vm_per_dev.
Create an environment
| Field | Type | Notes |
|---|---|---|
project_id | uuid | Required. |
provider | byo · openstack · hetzner | Required. local is reserved. |
name | string ≤ 120 | Required, except in vm_per_dev projects (defaults to <project> — <you>). |
slug | ^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$ | Default <project-slug>-<4 random chars>; globally unique (409 slug_taken). |
size | small · medium · large | Managed only; default small. |
region | string | Managed only; default eu-east-1 (OpenStack) or nbg1 (Hetzner). |
idle_timeout_minutes | 5–1440 | Default 30. |
budget_micro_eur | integer ≥ 0 or null | Managed only; monthly compute cap. |
curl https://ai.codai.ro/v1/environments \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "project_id": "2b6d1c0e-4f3a-4b9c-8d7e-1a2b3c4d5e6f", "name": "dev-box", "provider": "byo" }'201:
{
"environment": {
"id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c",
"projectId": "2b6d1c0e-4f3a-4b9c-8d7e-1a2b3c4d5e6f",
"ownerUserId": "5f0c…", "orgId": null, "forUserId": null, "for_user_id": null,
"isolation": "shared_vm",
"name": "dev-box", "slug": "brivio-k3xq",
"provider": "byo", "region": null, "size": null,
"state": "pending", "stateReason": null,
"deviceId": null, "providerRef": null, "publicIp": null,
"idleTimeoutMinutes": 30, "lastActivityAt": null, "startedAt": null, "stoppedAt": null,
"hourlyRateMicroEur": null, "includedHoursUsedSeconds": 0,
"metadata": {},
"createdAt": "2026-09-25T09:01:00.000Z", "updatedAt": "2026-09-25T09:01:00.000Z"
}
}A managed provider starts in creating with size, region, hourlyRateMicroEur (e.g. 30000 for small) and metadata.budget_micro_eur filled in. In a vm_per_dev project a second live environment answers:
{
"error": { "message": "You already have an environment in this vm_per_dev project.", "type": "invalid_request_error", "code": "already_has_env" },
"environment_id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c"
}Enrol a BYO machine
curl -X POST https://ai.codai.ro/v1/environments/$ENV_ID/enroll-token -H "Authorization: Bearer $CODAI_API_KEY"{ "token": "codai_env_4Qm9…32 chars", "expires_at": "2026-09-25T09:31:00.000Z", "environment_id": "9c1e5a7b-…" }The token is returned once, stored only as a SHA-256 hash, valid 30 minutes, and minting again replaces it. codaid presents it to POST /v1/environments/enroll:
{ "token": "codai_env_…", "device_id": "1c2f…-uuid", "daemon_version": "0.3.3", "os": "linux", "arch": "x86_64" }→ { "environment_id": "9c1e…", "relay_url": "wss://relay.codai.ro" } (managed machines also receive a one-time api_key). An unknown, used or expired token is 401 invalid_api_key; a state that does not admit enrolment is 409 not_enrollable.
Lifecycle actions
curl -X POST https://ai.codai.ro/v1/environments/$ENV_ID/stop -H "Authorization: Bearer $CODAI_API_KEY"{ "environment": { "id": "9c1e…", "state": "stopping", "stateReason": "user_request", "…": "…" }, "from": "running", "to": "stopping" }Allowed transitions are on the managed page; BYO environments accept only archive and destroy.
Members
curl https://ai.codai.ro/v1/environments/$ENV_ID/members \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "email": "ana@example.com", "role": "developer" }'201 → { "member": { "user_id": "7a1c09e4-…", "role": "developer", "linux_username": "u-7a1c09e4", "port_range_start": 30100 } }. GET …/members returns { "members": [{ "user_id", "role", "email", "name", "linux_username", "port_range_start", "ssh_public_key": true, "provisioned_at", "created_at" }] } — ssh_public_key is only a flag. Errors: 404 (no account with that e-mail), 409 already_member, 409 vm_per_dev_single_member, 409 last_owner.
Register your own key:
curl -X PUT https://ai.codai.ro/v1/environments/$ENV_ID/members/me/ssh-key \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "public_key": "ssh-ed25519 AAAAC3Nza… ana@laptop" }'→ { "user_id": "…", "ssh_public_key": true }.
Short-lived SSH certificates
Instead of a long-lived key in authorized_keys, ask the environment's SSH CA for a 12-hour certificate. The CA signs ssh-ed25519 and ecdsa-sha2-nistp256 public keys — the latter is what a non-exportable Secure Enclave key on the iPhone produces, so the codai iOS app never holds a copyable private key. Other key types (RSA, other curves) get 400.
curl https://ai.codai.ro/v1/environments/$ENV_ID/members/me/ssh-cert \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "public_key": "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAI… ana@iphone" }'→ { "certificate": "ecdsa-sha2-nistp256-cert-v01@openssh.com AAAA… user:<uuid>/env:<id>", "principal": "u-7a1c09e4", "valid_before": "…", "host": "203.0.113.7" }. Save certificate next to the key as <key>-cert.pub (codaid env ssh does this for you). The certificate is valid from 5 minutes ago to 12 hours from now, for your Linux user only.
Ports
PUT /v1/environments/{id}/ports/{port} with { "visibility": "owner" | "org" | "authenticated" | "public", "label"?: string ≤ 64, "protocol"?: "http" } returns { "port": { "id", "environmentId", "port", "protocol", "label", "visibility", "openedByUserId", "createdAt", "updatedAt" } }. See ports and previews.
Secrets
curl -X PUT https://ai.codai.ro/v1/environments/$ENV_ID/secrets/DATABASE_URL \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "value": "postgres://…" }'201 → { "name": "DATABASE_URL", "updated": false } (200 and true when replacing). Values are envelope-sealed at rest (≤ 64 KB) and never returned; GET …/secrets lists { "name", "created_by_user_id", "created_at", "updated_at" }. Injecting secrets into shells inside the environment is coming soon; today only CODAI_GIT_TOKEN is used, for cloning (see team).
Agent tasks
Fire-and-forget: queue a prompt, close the laptop, get a notification when the work is committed.
curl https://ai.codai.ro/v1/environments/$ENV_ID/tasks \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "prompt": "Add a /health route with a test", "branch": "feat/health" }'202 → { "task_id": "…", "session_id": "…", "status": "queued" }. The relay hands the task to the daemon on its next heartbeat; the agent runs headless as your Linux user in your worktree, on branch (default codai/task-<first 8 chars of the id>), with autopilot permissions and a 2 h cap. When it exits, uncommitted changes are committed as codai task <id8> with your git identity. You then get an inbox notification (GET /v1/notifications) and a push on your phone: env.task.done with the diff stat, the short SHA and a preview URL when a dev server is listening in your port range, or env.task.failed with the reason (no_agent_runtime when the machine has no agent sidecar installed).
session_id is an ordinary shared session: open it in the desktop app or on the phone to watch the run live, steer it, or cancel it. GET …/tasks/{taskId} returns status (queued → running → done | failed) and result (diff_stat, commit_sha, branch, preview_url, error).
REST exec
Run commands and touch files in a running environment over plain HTTPS, without a WebSocket client. The gateway opens a short-lived relay connection as you (your own API key, so the same role checks apply), sends one operation and returns the answer. Viewers get 403; the command runs as your Linux user in your workspace.
curl https://ai.codai.ro/v1/environments/$ENV_ID/exec \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{ "command": "pnpm test", "timeout_ms": 120000 }'200 → { "ok": true, "exit_code": 0, "stdout": "…", "stderr": "", "timed_out": false, "truncated": false, "duration_ms": 8123 }. timeout_ms defaults to 60 000 (max 600 000). Other relay ops go through the same endpoint with op + args ({ "op": "fs_roots" }, { "op": "job_kill", "args": { "id": 3 } }) and return { ok, result }; a refusal on the machine (path outside your roots, unknown job) is 200 { "ok": false, "error": "…" }. Errors: 409 environment_offline (daemon not connected), 504 host_timeout, 502 relay_unavailable.
- Files:
GET …/files?path=/home/u-…/worklists a directory;GET …/files/home/u-…/work/README.mdreturns{ path, content, size, truncated }(64 KB default,?max_bytes=up to 512 KB);PUT …/files/<path>with a raw text body (≤ 2 MB, else413) writes it and creates parent directories. - Long work:
POST …/jobs{ "command": "pnpm build", "expected_s": 300 }→201 { "job_id": 4 }; pollGET …/jobs/4?since=<next_offset>for{ job, output }instead of holding an exec open.
Relay status
curl https://relay.codai.ro/v1/environments/$ENV_ID/status -H "Authorization: Bearer $CODAI_API_KEY"{
"environment_id": "9c1e…", "online": true, "instance": "relay-7f3a", "last_heartbeat": 1790326860000,
"activity": "agent",
"ports": [{ "port": 3000, "pid": 4121, "process": "node" }],
"git_state": { "branch": "main", "dirty": true, "ahead": 1, "behind": 0, "worktrees": ["/home/u-7a1c09e4/work/brivio"] },
"daemon": { "daemon_version": "0.3.3", "os": "linux", "arch": "x86_64", "capabilities": ["shell", "pty", "tcp", "http", "lock:build", "identity:per-user"] },
"streams": 2,
"attached": [{ "client_id": "…", "user_id": "…", "role": "owner", "kind": "agent", "device_id": "…", "name": "Work laptop", "platform": "desktop", "attached_at": "2026-09-25T09:20:00.000Z" }],
"locks": [{ "name": "build-0123456789ab", "holder": "u-7a1c09e4 · pnpm build", "since": "2026-09-25T09:20:30Z" }],
"clients": 1
}Reference
bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Your projects.
application/json- response
projects*array<>curl -X GET 'https://ai.codai.ro/v1/projects'{ "projects": [ { "id": "2b6d1c0e-4f3a-4b9c-8d7e-1a2b3c4d5e6f", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "name": "brivio", "slug": "brivio", "repoUrl": "https://github.com/acme/brivio", "defaultBranch": "main", "devcontainerPath": ".devcontainer/devcontainer.json", "isolation": "shared_vm", "settings": {}, "prebuildPoolSize": 0, "prebuildProvider": "openstack", "prebuildSize": "small", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
name*string1 <= length <= 120slug?stringDefaults to a slugified name.
^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$repo_url?|urilength <= 2048default_branch?string1 <= length <= 255"main"devcontainer_path?|1 <= length <= 512isolation?stringHow a project's environments are shared. shared_vm (default) — one machine, many members; container — one container per member; vm_per_dev — one machine per developer, pinned via for_user_id.
"shared_vm""shared_vm""container""vm_per_dev"org_id?|Attach to an org you own or administer.
uuidThe new project.
application/json- response
project*The stored project row (camelCase, as persisted).
curl -X POST 'https://ai.codai.ro/v1/projects' \ -H 'Content-Type: application/json' \ -d '{ "name": "brivio"}'{ "project": { "id": "2b6d1c0e-4f3a-4b9c-8d7e-1a2b3c4d5e6f", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "name": "brivio", "slug": "brivio", "repoUrl": "https://github.com/acme/brivio", "defaultBranch": "main", "devcontainerPath": ".devcontainer/devcontainer.json", "isolation": "shared_vm", "settings": {}, "prebuildPoolSize": 0, "prebuildProvider": "openstack", "prebuildSize": "small", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringProject UUID. A malformed value is 400; an unknown or foreign project is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The project and its environments.
application/json- response
project*The stored project row (camelCase, as persisted).
environments*array<>curl -X GET 'https://ai.codai.ro/v1/projects/497f6eca-6276-4993-bfeb-53cbbbba6f08'{ "project": { "id": "2b6d1c0e-4f3a-4b9c-8d7e-1a2b3c4d5e6f", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "name": "brivio", "slug": "brivio", "repoUrl": "https://github.com/acme/brivio", "defaultBranch": "main", "devcontainerPath": ".devcontainer/devcontainer.json", "isolation": "shared_vm", "settings": {}, "prebuildPoolSize": 0, "prebuildProvider": "openstack", "prebuildSize": "small", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }, "environments": [ { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringProject UUID. A malformed value is 400; an unknown or foreign project is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
At least one field is required.
name?string1 <= length <= 120repo_url?|urilength <= 2048default_branch?string1 <= length <= 255devcontainer_path?|1 <= length <= 512isolation?ProjectIsolationHow a project's environments are shared. shared_vm (default) — one machine, many members; container — one container per member; vm_per_dev — one machine per developer, pinned via for_user_id.
"shared_vm""container""vm_per_dev"prebuild_pool_size?integerPrebuild pool size; the project owner pays the pool's compute and storage.
0 <= value <= 5prebuild_provider?string"openstack""hetzner"prebuild_size?string"small""medium""large"The updated project.
application/json- response
project*The stored project row (camelCase, as persisted).
curl -X PATCH 'https://ai.codai.ro/v1/projects/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Content-Type: application/json' \ -d '{}'{ "project": { "id": "2b6d1c0e-4f3a-4b9c-8d7e-1a2b3c4d5e6f", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "name": "brivio", "slug": "brivio", "repoUrl": "https://github.com/acme/brivio", "defaultBranch": "main", "devcontainerPath": ".devcontainer/devcontainer.json", "isolation": "shared_vm", "settings": {}, "prebuildPoolSize": 0, "prebuildProvider": "openstack", "prebuildSize": "small", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringProject UUID. A malformed value is 400; an unknown or foreign project is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Deleted.
application/json- response
deleted*truetrueid*stringuuidcurl -X DELETE 'https://ai.codai.ro/v1/projects/497f6eca-6276-4993-bfeb-53cbbbba6f08'{ "deleted": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
project_id?stringRestrict to one project.
uuidfor_user_id?"me"Only me is accepted — your own per-developer environments.
"me"x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The environments you can see.
application/json- response
environments*array<>curl -X GET 'https://ai.codai.ro/v1/environments'{ "environments": [ { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
project_id*stringuuidname?stringRequired unless the project is vm_per_dev (then defaults to <project> — <you>).
1 <= length <= 120slug?stringDefaults to <project-slug>-<4 random chars>.
^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$provider*EnvironmentProviderbyo (bring your own machine; the daemon enrols with a token) is open to every key. openstack / hetzner are managed by the worker and billed hourly; gce (Google Cloud) offers on-demand CPU sizes small/medium/large (e2-standard-2/4/8) and the GPU size gpu-l4 (Spot, 30-minute cap); it is available only where the deployment enables it AND to super_admin accounts (403 for everyone else); local is a developer convenience. Managed providers answer 403 until enabled for your key.
"byo""openstack""hetzner""local""gce"region?stringManaged providers; defaults per provider (hetzner → nbg1, openstack → eu-east-1).
1 <= length <= 64size?stringManaged providers; one of the catalog sizes, default small.
1 <= length <= 64idle_timeout_minutes?integer5 <= value <= 144030budget_micro_eur?|Per-environment spend ceiling in micro-EUR, enforced by the worker; null = none.
0 <= valueThe new environment (or the claimed prebuild).
application/json- response
environment*The stored environment row (camelCase, as persisted) plus snake_case for_user_id and, when known, the project's isolation. metadata never contains the enrolment token hash.
prebuild_claimed?booleanPresent and true when a prebuild pool machine was claimed and started instead of provisioning a new one.
curl -X POST 'https://ai.codai.ro/v1/environments' \ -H 'Content-Type: application/json' \ -d '{ "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9", "provider": "byo"}'{ "environment": { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }, "prebuild_claimed": true}x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
token*string^codai_env_[0-9A-Za-z]{32}$device_id*stringThe daemon's device UUID (client-chosen; registered under the owner when unknown).
uuiddaemon_version*string1 <= length <= 64os*string1 <= length <= 32arch*string1 <= length <= 32Enrolled.
application/json- response
environment_id*stringuuidrelay_url*stringWebSocket URL the daemon should connect to.
uriapi_key?stringManaged providers only — an owner API key for the daemon, returned exactly once.
curl -X POST 'https://ai.codai.ro/v1/environments/enroll' \ -H 'Content-Type: application/json' \ -d '{ "token": "string", "device_id": "3bafab7b-4400-4bcf-8e6e-09f954699940", "daemon_version": "string", "os": "linux", "arch": "x64"}'{ "environment_id": "40ef0e48-a11f-4963-a229-e396c9f7e7c4", "relay_url": "wss://relay.codai.ro", "api_key": "string"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The environment detail.
application/json- response
environment*The stored environment row (camelCase, as persisted) plus snake_case for_user_id and, when known, the project's isolation. metadata never contains the enrolment token hash.
isolation*ProjectIsolationHow a project's environments are shared. shared_vm (default) — one machine, many members; container — one container per member; vm_per_dev — one machine per developer, pinned via for_user_id.
"shared_vm""container""vm_per_dev"role*stringYour effective role (owner / org admin → owner; plain org member → viewer).
"owner""maintainer""developer""viewer"members*array<>ports*array<>events*array<>The 20 most recent events, newest first.
items <= 20curl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08'{ "environment": { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }, "isolation": "shared_vm", "role": "owner", "members": [ { "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", "role": "owner", "email": "user@example.com", "name": "string", "linux_username": "string", "port_range_start": 0, "ssh_public_key": true, "provisioned_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z" } ], "ports": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "environmentId": "19f5cc2e-7657-437a-9268-83cd3d563563", "port": 3000, "protocol": "http", "label": "web", "visibility": "owner", "openedByUserId": "c717c8e6-87b5-4b69-9353-93a60c6b5d41", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" } ], "events": [ { "id": 0, "environmentId": "19f5cc2e-7657-437a-9268-83cd3d563563", "actorUserId": "94141e08-adc7-44ce-bed3-45f031b9f36f", "actorKind": "user", "kind": "state:pending->enrolling", "payload": {}, "createdAt": "2019-08-24T14:15:22Z" } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
Provide name and/or idle_timeout_minutes.
name?string1 <= length <= 120idle_timeout_minutes?integer5 <= value <= 1440The updated environment.
application/json- response
environment*The stored environment row (camelCase, as persisted) plus snake_case for_user_id and, when known, the project's isolation. metadata never contains the enrolment token hash.
curl -X PATCH 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Content-Type: application/json' \ -d '{}'{ "environment": { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The transition that was applied.
application/json- response
environment*The stored environment row (camelCase, as persisted) plus snake_case for_user_id and, when known, the project's isolation. metadata never contains the enrolment token hash.
from*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"to*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"curl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/start'{ "environment": { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }, "from": "pending", "to": "pending"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The transition that was applied.
application/json- response
environment*The stored environment row (camelCase, as persisted) plus snake_case for_user_id and, when known, the project's isolation. metadata never contains the enrolment token hash.
from*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"to*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"curl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/stop'{ "environment": { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }, "from": "pending", "to": "pending"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The transition that was applied.
application/json- response
environment*The stored environment row (camelCase, as persisted) plus snake_case for_user_id and, when known, the project's isolation. metadata never contains the enrolment token hash.
from*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"to*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"curl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/archive'{ "environment": { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }, "from": "pending", "to": "pending"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The transition that was applied.
application/json- response
environment*The stored environment row (camelCase, as persisted) plus snake_case for_user_id and, when known, the project's isolation. metadata never contains the enrolment token hash.
from*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"to*EnvironmentStateLifecycle state. byo starts in pending and reaches enrolling when the daemon enrols; managed providers start in creating and reach running once enrolled. destroyed is terminal.
"pending""creating""enrolling""starting""running""stopping""stopped""archiving""archived""destroying""destroyed""error"curl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/destroy'{ "environment": { "id": "9c1e5a7b-2d4f-4e6a-8b0c-3d5e7f9a1b2c", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "ownerUserId": "283eeac5-beea-465a-80a5-763009480d0f", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "forUserId": "e7ebe9fe-55a5-420c-a31b-7f6410828de5", "for_user_id": "5c382187-9b39-4b04-b883-3a2ecc9f4e6e", "isolation": "shared_vm", "name": "brivio — Ana", "slug": "brivio-k3xq", "provider": "byo", "region": "nbg1", "size": "small", "state": "pending", "stateReason": "string", "deviceId": "4de4adb9-21ee-47e3-aeb4-8cf8ed6c109a", "providerRef": "string", "publicIp": "string", "idleTimeoutMinutes": 30, "lastActivityAt": "2019-08-24T14:15:22Z", "startedAt": "2019-08-24T14:15:22Z", "stoppedAt": "2019-08-24T14:15:22Z", "hourlyRateMicroEur": 0, "includedHoursUsedSeconds": 0, "metadata": {}, "prebuild": false, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }, "from": "pending", "to": "pending"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The token (plaintext, returned once).
application/json- response
token*stringPlaintext codai_env_… token, returned once; only its hash is stored.
expires_at*string30 minutes after minting.
date-timeenvironment_id*stringuuidcurl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/enroll-token'{ "token": "string", "expires_at": "2019-08-24T14:15:22Z", "environment_id": "40ef0e48-a11f-4963-a229-e396c9f7e7c4"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Members of the environment.
application/json- response
members*array<>curl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/members'{ "members": [ { "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", "role": "owner", "email": "user@example.com", "name": "string", "linux_username": "string", "port_range_start": 0, "ssh_public_key": true, "provisioned_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z" } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
Exactly one of user_id or email is required.
user_id?stringuuidemail?stringemailrole?stringDefault developer. owner can only be granted by an owner.
"developer""owner""maintainer""developer""viewer"The membership as stored.
application/json- response
member*curl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/members' \ -H 'Content-Type: application/json' \ -d '{}'{ "member": { "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", "role": "owner", "linux_username": "string", "port_range_start": 0 }}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
public_key*stringOne authorized_keys line (ssh-ed25519, ssh-rsa or ecdsa-*).
1 <= length <= 1000Stored.
application/json- response
user_id*stringuuidssh_public_key*truetruecurl -X PUT 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/members/me/ssh-key' \ -H 'Content-Type: application/json' \ -d '{ "public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGx7v0o6Q3m1q0Y4bJw2m9V7a2X5b1x6ZJ3o8Kk1QzAB ana@laptop"}'{ "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", "ssh_public_key": true}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuiduserId*stringUUID of the member.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
role*EnvironmentRoleRole of a member on an environment; owner ≥ maintainer ≥ developer ≥ viewer.
"owner""maintainer""developer""viewer"The updated membership.
application/json- response
member*curl -X PATCH 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/members/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Content-Type: application/json' \ -d '{ "role": "owner"}'{ "member": { "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", "role": "owner" }}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuiduserId*stringUUID of the member to remove (your own id to leave).
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Removed.
application/json- response
deleted*truetrueuser_id*stringuuidcurl -X DELETE 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/members/497f6eca-6276-4993-bfeb-53cbbbba6f08'{ "deleted": true, "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Published ports.
application/json- response
ports*array<>curl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/ports'{ "ports": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "environmentId": "19f5cc2e-7657-437a-9268-83cd3d563563", "port": 3000, "protocol": "http", "label": "web", "visibility": "owner", "openedByUserId": "c717c8e6-87b5-4b69-9353-93a60c6b5d41", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidport*integerTCP port inside the environment (1..65535).
1 <= value <= 65535x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
visibility*PortVisibilityWho may reach a published port through the preview router. public needs maintainer to set.
"owner""org""authenticated""public"label?|1 <= length <= 64protocol?string"http""http""https""tcp"The port was already published and has been updated.
application/json- response
port*curl -X PUT 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/ports/1' \ -H 'Content-Type: application/json' \ -d '{ "visibility": "owner"}'{ "port": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "environmentId": "19f5cc2e-7657-437a-9268-83cd3d563563", "port": 3000, "protocol": "http", "label": "web", "visibility": "owner", "openedByUserId": "c717c8e6-87b5-4b69-9353-93a60c6b5d41", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidport*integerTCP port inside the environment (1..65535).
1 <= value <= 65535x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Unpublished.
application/json- response
deleted*truetrueport*integercurl -X DELETE 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/ports/1'{ "deleted": true, "port": 0}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Secret names.
application/json- response
secrets*array<>curl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/secrets'{ "secrets": [ { "name": "DATABASE_URL", "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidname*stringSecret name, ENV_STYLE.
^[A-Z_][A-Z0-9_]{0,127}$x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
value*stringSealed before storage; never returned.
1 <= length <= 65536Replaced.
application/json- response
name*stringupdated*booleantrue when an existing value was replaced.
curl -X PUT 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/secrets/string' \ -H 'Content-Type: application/json' \ -d '{ "value": "string"}'{ "name": "string", "updated": true}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidname*stringSecret name, ENV_STYLE.
^[A-Z_][A-Z0-9_]{0,127}$x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Deleted.
application/json- response
deleted*truetruename*stringcurl -X DELETE 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/secrets/string'{ "deleted": true, "name": "string"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Tasks of the environment.
application/json- response
data*array<>curl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/tasks'{ "data": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "environment_id": "40ef0e48-a11f-4963-a229-e396c9f7e7c4", "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", "session_id": "1ffd059c-17ea-40a8-8aef-70fd0307db82", "prompt": "string", "branch": "string", "status": "queued", "result": { "diff_stat": "string", "commit_sha": "string", "branch": "string", "preview_url": "string", "error": "string" }, "created_at": "2019-08-24T14:15:22Z", "started_at": "2019-08-24T14:15:22Z", "finished_at": "2019-08-24T14:15:22Z", "output_signature": { "alg": "hide-hybrid-ed25519-mldsa65", "context": "codai.agent-output.v1", "key_id": "string", "sig": "string", "signed_at": "2019-08-24T14:15:22Z" } } ]}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The prompt and an optional branch.
application/json- body
prompt*stringWhat the agent should do (1..20000 characters).
1 <= length <= 20000branch?stringGit branch to create for the work; default codai/task-<first 8 chars of the id>.
length <= 200Queued.
application/json- response
task_id*stringuuidsession_id*stringConversation session the agent runs in (follow it with the sessions API).
uuidstatus*"queued""queued"curl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/tasks' \ -H 'Content-Type: application/json' \ -d '{ "prompt": "string"}'{ "task_id": "736fde4d-9029-4915-8189-01353d6982cb", "session_id": "1ffd059c-17ea-40a8-8aef-70fd0307db82", "status": "queued"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidtaskId*stringAgent task id.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The task.
application/json- response
task*curl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/tasks/497f6eca-6276-4993-bfeb-53cbbbba6f08'{ "task": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "environment_id": "40ef0e48-a11f-4963-a229-e396c9f7e7c4", "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", "session_id": "1ffd059c-17ea-40a8-8aef-70fd0307db82", "prompt": "string", "branch": "string", "status": "queued", "result": { "diff_stat": "string", "commit_sha": "string", "branch": "string", "preview_url": "string", "error": "string" }, "created_at": "2019-08-24T14:15:22Z", "started_at": "2019-08-24T14:15:22Z", "finished_at": "2019-08-24T14:15:22Z", "output_signature": { "alg": "hide-hybrid-ed25519-mldsa65", "context": "codai.agent-output.v1", "key_id": "string", "sig": "string", "signed_at": "2019-08-24T14:15:22Z" } }}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
op?stringRelay operation; shell needs command (or args.cmd).
"shell""shell""fs_read""fs_write""fs_list""fs_roots""info""job_start""job_status""job_output""job_send""job_wait""job_kill""job_list"command?stringShell line for op: shell.
length <= 100000args?Operation arguments (e.g. { path } for fs_read, { id } for job_kill, cwd for shell).
timeout_ms?integerPer-request budget, default 60000 (10 minutes max).
1000 <= value <= 600000The operation result.
application/json- response
ok*booleanexit_code?|Shell exit code; -1 when killed.
stdout?stringstderr?stringtimed_out?booleantruncated?booleanduration_ms?integer|nullresult?unknownRaw result of a non-shell operation.
error?string|nullcurl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/exec' \ -H 'Content-Type: application/json' \ -d '{}'{ "ok": true, "exit_code": 0, "stdout": "string", "stderr": "string", "timed_out": true, "truncated": true, "duration_ms": 0, "result": null, "error": "string"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidpath?stringAbsolute directory path; default your workspace.
length <= 4096x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Directory entries.
application/json- response
path*stringentries*array<>truncated*booleancurl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/files'{ "path": "string", "entries": [ { "name": "string", "kind": "string", "size": 0 } ], "truncated": true}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidpath*stringAbsolute file path inside the environment (slashes allowed, each segment URL-encoded); must be under the roots the daemon serves for you.
length <= 4096max_bytes?integerRead at most this many bytes (1..524288).
1 <= value <= 524288x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128The file content.
application/json- response
path*stringcontent*stringsize*integertruncated*booleancurl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/files/string'{ "path": "string", "content": "string", "size": 0, "truncated": true}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidpath*stringAbsolute file path inside the environment (slashes allowed, each segment URL-encoded); must be under the roots the daemon serves for you.
length <= 4096x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128body*stringtext/plainWritten.
application/json- response
path*stringbytes*integercreated*booleancurl -X PUT 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/files/string' \ -H 'Content-Type: text/plain' \ -d 'string'{ "path": "string", "bytes": 0, "created": true}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidx-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128application/json- body
command*stringlength <= 100000cwd?stringlength <= 4096expected_s?integerExpected runtime in seconds (shown in job status).
1 <= valuesession?stringlength <= 120The job was started.
application/json- response
job_id*integerlabel?stringcurl -X POST 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/jobs' \ -H 'Content-Type: application/json' \ -d '{ "command": "string"}'{ "job_id": 0, "label": "string"}bearerAuthAuthorizationBearer <codai_…>A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringEnvironment UUID. A malformed value is 400; an unknown or foreign environment is 404.
uuidjobId*stringJob id from startEnvironmentJob.
^(job)?[0-9]{1,18}$since?integerOutput byte offset to continue from (next_offset of the previous call).
0 <= valuetail?integerReturn only the last N bytes of output.
0 <= valuesession?stringJob session the job was started in (default default).
length <= 120x-request-id?stringClient correlation id; echoed back as x-codai-trace-id and persisted on the usage row.
length <= 128Status and output.
application/json- response
job*|job_status entry: id, label, state, exit_code, runtime_s…
output*job_output chunk: text, from, next_offset, state…
curl -X GET 'https://ai.codai.ro/v1/environments/497f6eca-6276-4993-bfeb-53cbbbba6f08/jobs/string'{ "job": {}, "output": {}}Last updated:
Billing
Managed environments are charged per second of runtime and per GB-month of kept disk from your wallet, fail-closed; your own servers are free.
Sign in with codai
auth.codai.ro is a standard OpenID Connect provider. Use it when your app needs to act on behalf of a codai user; use a plain API key when it does not.