Skip to content

Cloud REST API reference

Audience. Anyone calling iterion programmatically — a CI job, an SDK author, an operator writing curl runbooks. Every endpoint listed here exists in pkg/server/; the table is grouped by domain and machine-grepped from the register* functions, not curated by hand.

Authentication. Most routes accept any of:

  • Cookie: iterion_auth (access JWT) + iterion_refresh (rotation).
  • Bearer JWT: Authorization: Bearer <access-jwt> issued by login / refresh.
  • Bearer PAT: Authorization: Bearer iap_… — long-lived personal access token; authenticates as the issuing user with that user's role + super-admin flag (pkg/server/pat_routes.go:identityFromPAT).
  • WS query: ?t=<access-jwt> for WebSocket clients that can't set headers.

Where a route says "team member", "team admin" or "super-admin", the guard maps to canViewTeam / canManageTeam / requireSuperAdmin. Webhook delivery URLs (POST /api/webhooks/<provider>/<id>) use their own auth (token bearer or HMAC body signature) and are public to the JWT layer.

Authentication + identity

MethodPathAuthPurpose
POST/api/auth/loginpublicEmail + password login
POST/api/auth/refreshrefresh cookieRotate access JWT
POST/api/auth/logoutpublicDrop refresh session + cookies
POST/api/auth/registerpublic (when signup_mode=open or with invite)Create account
POST/api/auth/password/changepublic (legacy)First-login password rotation for pending_password_change users
POST/api/auth/password/reset/requestpublicMint + email a reset token (always 200, no enumeration)
POST/api/auth/password/reset/confirmpublicRedeem iar_…, set new password, issue fresh session
GET/api/auth/providerspublicList configured OIDC connectors + signup_mode
GET/api/auth/oidc/{provider}/startpublicStart OIDC dance
GET/api/auth/oidc/{provider}/callbackpublicOIDC redirect target
GET/api/auth/invitations/lookuppublicResolve invitation token → email + team
POST/api/auth/invitations/acceptmemberAccept an invitation while logged in
GET/api/auth/mememberCurrent user + active team identity
POST/api/auth/me/team/{team_id}memberSwitch active team
POST/api/auth/me/org/{org_id}memberSwitch active org (re-issues the JWT; validates org then team)
POST/api/me/passwordmemberSelf-service password change
POST/api/me/sessions/revoke-allmemberSign out every device

Source: pkg/server/auth_routes.go + pkg/server/password_routes.go.

Teams + members + invitations

MethodPathAuthPurpose
GET/api/teamsmemberList the caller's teams
POST/api/teamsmemberCreate a team
GET/api/teams/{id}/membersteam memberList members
PATCH/api/teams/{id}/members/{user_id}team adminChange role
DELETE/api/teams/{id}/members/{user_id}team adminRemove a member
GET/api/teams/{id}/invitationsteam adminList pending invitations
POST/api/teams/{id}/invitationsteam adminMint a token (shown once)
DELETE/api/teams/{id}/invitations/{invite_id}team adminRevoke
GET/api/orgs/{id}/usageorg memberOrg-member mirror of the admin usage view (see below)
GET/api/teams/{id}/auditteam adminTenant audit log

Organisations — self-serve (org members, teams, SSO)

The org-admin self-serve mirror of the super-admin org views (two-level tenancy, ADR-048). All routes are requireAuth; the org role is checked in-handler — read routes need org membership (canViewOrg), mutations need org admin/owner (canManageOrg). Sources: pkg/server/orgs_routes.go, pkg/server/org_sso_routes.go, pkg/server/org_sso_domain_routes.go.

MethodPathAuthPurpose
GET/api/orgs/{id}/membersorg memberList org members + roles
PATCH/api/orgs/{id}/members/{user_id}org adminChange a member's org role (member|admin|owner)
DELETE/api/orgs/{id}/members/{user_id}org adminRemove a member
GET/api/orgs/{id}/invitationsorg adminList pending org invitations
POST/api/orgs/{id}/invitationsorg adminMint an org invitation token
DELETE/api/orgs/{id}/invitations/{invite_id}org adminRevoke
GET/api/orgs/{id}/teamsorg memberList the org's teams
POST/api/orgs/{id}/teamsorg adminCreate a team in the org
GET/api/orgs/{id}/auditorg adminOrg audit log
GET/api/orgs/{id}/sso/providersorg memberList SSO providers
POST/api/orgs/{id}/sso/providersorg adminAdd an SSO provider (OIDC)
PATCH/api/orgs/{id}/sso/providers/{provider_id}org adminUpdate a provider
DELETE/api/orgs/{id}/sso/providers/{provider_id}org adminRemove a provider
POST/api/orgs/{id}/sso/providers/{provider_id}/testorg adminTest a provider's config
GET/api/orgs/{id}/sso/domainsorg memberList claimed SSO domains
POST/api/orgs/{id}/sso/domainsorg adminClaim a domain
POST/api/orgs/{id}/sso/domains/{domain_id}/verifyorg adminVerify a claimed domain
DELETE/api/orgs/{id}/sso/domains/{domain_id}org adminRelease a domain

BYOK LLM keys + generic secrets + bindings

User-scoped + team-scoped flavours share the same payload shape. Both return metadata only — the plaintext is write-only.

MethodPathAuthPurpose
GET/api/teams/{id}/api-keysteam memberList team's BYOK keys
POST/api/teams/{id}/api-keysteam adminCreate
PATCH/api/teams/{id}/api-keys/{key_id}team adminToggle default / rename
DELETE/api/teams/{id}/api-keys/{key_id}team adminDelete
GET/api/me/api-keysmemberList own user-scoped keys
POST/api/me/api-keysmemberCreate personal key
PATCH/api/me/api-keys/{key_id}memberUpdate
DELETE/api/me/api-keys/{key_id}memberDelete
GET/api/teams/{id}/secretsteam memberList team's generic secrets
POST/api/teams/{id}/secretsteam adminCreate
PATCH/api/teams/{id}/secrets/{secret_id}team adminUpdate
DELETE/api/teams/{id}/secrets/{secret_id}team adminDelete
GET/api/me/secretsmemberPersonal secrets
POST/api/me/secretsmemberCreate
PATCH/api/me/secrets/{secret_id}memberUpdate
DELETE/api/me/secrets/{secret_id}memberDelete
GET/api/teams/{id}/bots/{bot_id}/bindingsteam memberList bot bindings
POST/api/teams/{id}/bots/{bot_id}/bindingsteam adminCreate binding
PATCH/api/teams/{id}/bots/{bot_id}/bindings/{binding_id}team adminUpdate
DELETE/api/teams/{id}/bots/{bot_id}/bindings/{binding_id}team adminDelete

Sources: pkg/server/byok_routes.go, pkg/server/generic_secrets_routes.go, pkg/server/bot_bindings_routes.go. Full semantics in secrets-reference.md.

Team bot sources (cloud bot editing)

Team-authored bot bundles — the writable, tenant-scoped store the studio editor saves into (cloud pods bake the catalog read-only). Each source is a multi-file bundle (main.bot + manifest.yaml + skills/…). Edit rights = the config_editor capability, team admin, or owner (canEditBots); if the server has no bot-source store wired every route returns 501 bot editing is not enabled on this server.

MethodPathAccessPurpose
GET/api/teams/{id}/bot-sourcesbot editorList the team's bots (metadata only — file bodies omitted)
GET/api/teams/{id}/bot-sources/{slug}bot editorOne bot with its full file map
PUT/api/teams/{id}/bot-sources/{slug}bot editorCreate or replace the whole bundle ({files, version?})
PUT/api/teams/{id}/bot-sources/{slug}/files/{path...}bot editorPer-file save ({content, version?})
DELETE/api/teams/{id}/bot-sources/{slug}/files/{path...}bot editorDelete one file (never main.bot)
DELETE/api/teams/{id}/bot-sources/{slug}bot editorDelete the bot
POST/api/teams/{id}/bot-sources/{slug}/forkbot editorFork a baked catalog bot ({from}) into an editable copy

Every write compiles the bundle before it persists — a bot that fails to parse/compile is rejected 400 bot does not compile: <diagnostics>, never left to fail at launch. A non-zero version is an optimistic if-match token (409 if a concurrent editor advanced it); slug collisions are 409. Source: pkg/server/bot_sources_routes.go, store pkg/botsource/.

Forge integrations (connections, OAuth apps, repo-bots)

The team-scoped, outbound forge layer behind the studio's repo-first shell (docs/repo-scope.md): connect a forge, hold an OAuth app / GitHub App credential, and provision a set of bots onto a repo (webhook + hook

MethodPathAuthPurpose
GET/api/teams/{id}/forge/connectionsteam memberList forge connections
POST/api/teams/{id}/forge/connectionsteam adminConnect a forge (PAT / OAuth / GitHub-App install)
DELETE/api/teams/{id}/forge/connections/{conn_id}team adminRemove a connection
GET/api/teams/{id}/forge/connections/{conn_id}/healthteam memberConnection health / token probe
POST/api/teams/{id}/forge/connections/{conn_id}/refreshteam memberGitHub App only: re-probe live installation grants, persist them, and force a fresh token mint
GET/api/teams/{id}/forge/connections/{conn_id}/reposteam memberRepos visible to the connection
GET/api/teams/{id}/forge/reposteam memberTeam's forge-linked repos
POST/api/teams/{id}/forge/reposteam adminCreate a repo (opt-in RepoCreator capability)
GET/api/teams/{id}/forge/oauth-appsteam memberList per-tenant OAuth apps
POST/api/teams/{id}/forge/oauth-appsteam adminRegister an OAuth app (manual / auto / auto_from_connection)
DELETE/api/teams/{id}/forge/oauth-apps/{app_id}team adminRemove an OAuth app
POST/api/teams/{id}/forge/oauth-apps/github-manifestteam adminStart the GitHub App-manifest auto-create flow
GET/api/teams/{id}/forge/repo-botsteam memberList repo→bot provisionings (integrations)
GET/api/teams/{id}/forge/repo-bots/previewteam memberPreview what enabling a bot set subscribes to (no forge writes)
POST/api/teams/{id}/forge/repo-botsteam adminEnable bots on a repo (provision webhook + hook + secret + bindings)
PATCH/api/teams/{id}/forge/repo-bots/{integration_id}team adminSet the exact bot set (replace semantics)
DELETE/api/teams/{id}/forge/repo-bots/{integration_id}team adminDisable / deprovision (tears down webhook + hook)
PATCH/api/teams/{id}/forge/integrations/{iid}team adminUpdate an integration (incl. sync_issues_enabled)
POST/api/teams/{id}/forge/integrations/{iid}/syncteam adminRun the forge→board issue sync now (one-way, forge is source)
GET/api/teams/{id}/forge/integrations/{iid}/hooksteam memberList the webhooks on an integration

The OAuth handshake completes on public callbacks the SPA is redirected to: GET /api/forge/oauth/callback, GET /api/forge/github/app/callback, and GET /api/forge/github/app-manifest/callback.

Inbound webhooks

CRUD (operator-side) plus per-provider delivery URLs.

MethodPathAuthPurpose
GET/api/teams/{id}/webhooksteam memberList
POST/api/teams/{id}/webhooksteam adminCreate + mint iwh_ token (shown once)
GET/api/teams/{id}/webhooks/{webhook_id}team memberGet one
PATCH/api/teams/{id}/webhooks/{webhook_id}team adminUpdate
DELETE/api/teams/{id}/webhooks/{webhook_id}team adminDelete
POST/api/teams/{id}/webhooks/{webhook_id}/rotateteam adminRotate token + re-seal HMAC
GET/api/teams/{id}/webhooks/{webhook_id}/deliveriesteam memberLast ~100 deliveries
POST/api/webhooks/gitlab/{id}webhook tokenInbound delivery (MR + /revi)
POST/api/webhooks/github/{id}webhook HMACInbound PR delivery
POST/api/webhooks/forgejo/{id}webhook HMACInbound PR delivery (also Gitea headers)
POST/api/webhooks/generic/{id}webhook token (or HMAC opt-in)Bot-agnostic JSON delivery

Source: pkg/server/webhooks_routes.go. Full reference: webhooks.md.

OAuth-forfait (Claude / Codex)

MethodPathAuthPurpose
GET/api/me/oauth/connectionsmemberList configured forfait kinds + expiry
POST/api/me/oauth/{kind}/credentialsmemberUpload pasted credentials.json / auth.json
POST/api/me/oauth/{kind}/refreshmemberRefresh stored access token against the IdP
DELETE/api/me/oauth/{kind}memberDisconnect

Source: pkg/server/oauth_routes.go.

Personal access tokens (PATs)

MethodPathAuthPurpose
GET/api/me/tokensmemberList own PATs (no plaintext)
POST/api/me/tokensmemberMint a PAT (iap_… shown once)
DELETE/api/me/tokens/{token_id}member (owner) or super-adminRevoke

Source: pkg/server/pat_routes.go.

Memory + knowledge

Spaces are addressed by query params (?name=, ?visibility=, plus ?bot= for visibility=bot and ?project= for project/bot scopes).

MethodPathAuthPurpose
GET/api/memory/usagemember{used_bytes, quota_bytes} for one space
GET/api/memory/docsmemberList documents in a space (optional ?dir=)
GET/api/memory/docmemberRead a document (?path=)
PUT/api/memory/docmember (super-admin for visibility=global)Write
DELETE/api/memory/docmember (super-admin for global)Delete
GET/api/memory/exportmemberTarball export of the space
POST/api/memory/importmember (super-admin for global)Import a tarball

Source: pkg/server/memory_routes.go. Full reference: memory-and-knowledge.md.

Runs surface

Read-only views plus the launch / resume mutations the studio drives.

MethodPathAuthPurpose
GET/api/runsmember (tenant-scoped)List runs
GET/api/runs/global-activesuper-adminAll active runs platform-wide
POST/api/runsmemberLaunch a workflow
POST/api/runs/preview-costmemberEstimate cost before launch
POST/api/runs/uploadsmemberUpload an attachment
GET/api/runs/{id}member (run tenant)Run state
GET/api/runs/{id}/eventsmemberEvent log
GET/api/runs/{id}/workflowmemberWorkflow source attached to the run
GET/api/runs/{id}/artifacts/{node}memberArtifact versions for a node
GET/api/runs/{id}/artifacts/{node}/{version}memberOne artifact
GET/api/runs/{id}/files / …/files/content / …/files/diffmemberWorking-tree views
GET/api/runs/{id}/commits etc.memberWorktree commit history
GET/api/runs/{id}/attachments/{name}memberDownload an attachment
GET/api/runs/{id}/attachments/{name}/urlmemberPre-signed S3 URL
POST/api/runs/{id}/cancelmemberCancel a running run
POST/api/runs/{id}/pausememberPause
POST/api/runs/{id}/resumememberResume (re-publishes through the queue)
POST/api/runs/{id}/forkmemberFork at a prior turn
POST/api/runs/{id}/mergememberMerge the run's worktree onto a branch
POST/api/runs/{id}/commit-and-finalizememberCommit pending work and finalise
POST/api/runs/{id}/renamememberRename a run
GET/api/runs/{id}/logmemberStreamed run log
GET/api/runs/{id}/previewmemberPreview proxy (SSRF-guarded)
GET/api/ws/runs/{id}member (via ?t=)Live run-console WebSocket
GET/api/v1/runs/statsmemberRolling stats (for the studio)
GET/api/v1/limits/costmemberCost-cap status
POST/api/v1/limits/cost/overridesuper-adminTemporary cost-cap override

Source: pkg/server/runs.go.

Super-admin (organisations + users + DLQ + audit)

MethodPathAuthPurpose
GET/api/admin/orgssuper-adminList every org
POST/api/admin/orgssuper-adminCreate org
GET/api/admin/orgs/{id}super-adminRead
PATCH/api/admin/orgs/{id}super-adminUpdate name / slug / quotas
DELETE/api/admin/orgs/{id}super-adminSchedule org deletion (reversible until it runs)
POST/api/admin/orgs/{id}/restoresuper-adminCancel a scheduled deletion
POST/api/admin/orgs/{id}/statussuper-adminSuspend / read-only / activate
GET/api/admin/orgs/{id}/usagesuper-adminUsage snapshot
GET/api/admin/orgs/{id}/teamssuper-adminList the org's teams
GET/api/admin/userssuper-adminList users (?offset=&limit= pagination; limit default 50, max 200)
PATCH/api/admin/users/{id}super-adminStatus / super-admin flag
POST/api/admin/users/{id}/reset-passwordsuper-adminForce a user's password reset
GET/api/admin/auditsuper-adminPlatform audit log (filters: action, actor, from, to, offset, limit)
GET/api/admin/dlqsuper-adminList parked messages
GET/api/admin/dlq/{seq}super-adminPeek payload
POST/api/admin/dlq/{seq}/replaysuper-adminRe-publish onto the live subject
DELETE/api/admin/dlq/{seq}super-adminDiscard

Sources: pkg/server/admin_orgs_routes.go, pkg/server/queue_sweeper.go.

Server info + health

MethodPathAuthPurpose
GET/api/server/infopublicMode, version, auth_required, email_enabled, per-feature enablement flags, upload limits
GET/healthzpublicLiveness — HTTP listener up
GET/readyzpublicReadiness — Mongo + NATS + S3 reachable under 1s deadline
GET/metricspublic on the metrics port (ClusterIP-only by design)Prometheus scrape

Non-obvious JSON shapes

POST /api/teams/{id}/webhooks — create response (token-once)

json
{
  "config": {
    "id": "8e2…",
    "tenant_id": "team_acme",
    "name": "GitLab MR review",
    "provider": "gitlab",
    "sign_mode": "",
    "enabled": true,
    "token_last4": "Vp3a",
    "fingerprint": "sha256:…",
    "bot_ids": ["review-pr"],
    "wildcard_bots": false,
    "project_allowlist": ["acme/*"],
    "event_allowlist": [],
    "rate_limit": { "rate": 1.0, "burst": 10 },
    "monthly_call_limit": 0,
    "launch_vars": {},
    "key_overrides": {},
    "created_by": "user_…",
    "created_at": "2026-06-11T10:11:12Z",
    "updated_at": "2026-06-11T10:11:12Z"
  },
  "token": "iwh_…"
}

The token field is the only way to recover the plaintext. The same shape comes back from the rotate endpoint.

Launch-denial envelope

Every gate refusal — REST launch, resume, webhook publication — uses the same shape (pkg/server/launch_gate.go):

jsonc
{
  "error":    "monthly_run_quota_exceeded",   // stable token
  "detail":   "monthly run quota (1000) exhausted",
  "reset_at": "2026-07-01T00:00:00Z"          // monthly quotas
}

Plus the header Retry-After: <seconds> on concurrency_cap_exceeded and launch_rate_limited. Token list and HTTP semantics in quotas-and-limits.md.

GET /api/orgs/{id}/usage (also /api/admin/orgs/{id}/usage)

See quotas-and-limits.md → Reading usage for the full orgUsageView schema. Same shape on both routes — the admin endpoint is super-admin only, the member endpoint is any org member.

POST /api/me/tokens — create PAT

Request:

json
{ "name": "github-actions", "team_id": "team_…", "expires_in_days": 90 }

Response (plaintext shown once):

json
{
  "pat":   { "id": "…", "name": "github-actions", "token_last4": "Q9k2", "expires_at": "…",  },
  "token": "iap_…"
}

expires_in_days is clamped down to ITERION_PAT_MAX_TTL when the platform sets one. team_id is optional; without it the PAT inherits the user's default team and re-checks membership at every use.