Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.crewship.ai/llms.txt

Use this file to discover all available pages before exploring further.

Internal IPC

Routes mounted under /api/v1/internal/* are not part of the public API. They are the surface used by the sidecar (running as UID 1002 inside every crew container) and a small set of orchestrator helpers. The sidecar holds the only copy of CREWSHIP_INTERNAL_TOKEN inside a container; agents (UID 1001) cannot read it from disk or process memory. This page is here so integrators building their own sidecar image, runtime adapter, or replay tool understand the contract. End users should never call these endpoints directly.

Authentication

Every internal route is wrapped in internalAuth:
X-Internal-Token: <token>
The token is generated at server startup if CREWSHIP_INTERNAL_TOKEN is not set in the environment. For multi-host deployments, set it explicitly on every host so a sidecar restart on host A doesn’t suddenly fail when its request lands on host B. Workspace / crew / agent / mission scope is not taken from request bodies. The IPC layer reads it from the sidecar’s IPCConfig (set by the orchestrator at exec time and pinned to the container) and projects it onto the request. An agent that captured the token still cannot forge cross-tenant attribution because the auth context overrides whatever they put in the body.

Route catalog

Sourced from the per-domain internal/api/router_*.go files (split by domain in May 2026 — see router_internal.go for the internal IPC surface). Methods and paths are exact.

Cost ledger

MethodPathPurpose
POST/api/v1/internal/cost/recordSidecar reports parsed LLM usage. See Cost record below.

Journal

MethodPathPurpose
POST/api/v1/internal/journal/emitSidecar emits a journal entry. Same structure as the in-process journal.Entry.

Credentials

MethodPathPurpose
GET/api/v1/internal/credentialsList credentials this agent is allowed to use.
PATCH/api/v1/internal/credentials/{credentialId}Update credential status (e.g. mark expired).

Chats

MethodPathPurpose
POST/api/v1/internal/chatsCreate a chat row from inside a container (e.g. webhook-triggered).
GET/api/v1/internal/chats/{chatId}/resolveResolve a chat ID to its scope (workspace, agent, etc.).
PATCH/api/v1/internal/chats/{chatId}/message-countIncrement message counter.
PATCH/api/v1/internal/chats/{chatId}/titleSet chat title (used by webhook-created chats).

Agents and crews

MethodPathPurpose
GET/api/v1/internal/agents/{agentId}/resolveResolve agent slug/ID to its full record.
GET/api/v1/internal/agents/{agentId}/webhook-secretFetch the agent’s webhook HMAC secret (used by the webhook handler to verify signatures).
GET/api/v1/internal/crewsList crews (sidecar discovery).
POST/api/v1/internal/crewsCreate a crew programmatically.
POST/api/v1/internal/agentsCreate an agent programmatically.
GET/api/v1/internal/crew-connectionsList crew connections.

Runs

MethodPathPurpose
POST/api/v1/internal/runsCreate a run row. Idempotent on (workspace_id, trace_id).
PATCH/api/v1/internal/runs/{runId}Update run status / metrics.
Since PR #234, runs are reconstructed from journal entries; these endpoints exist for transitional callers and emit a run.* journal entry as a side effect. New integrations should emit journal entries directly via /api/v1/internal/journal/emit instead.

Crew messaging and files

MethodPathPurpose
POST/api/v1/internal/crew-messagesSend a peer message.
GET/api/v1/internal/crew-messagesList peer messages.
GET/api/v1/internal/crew-files/{crewId}Read a file in the crew’s shared volume.
POST/api/v1/internal/crew-files/{crewId}Write a file in the crew’s shared volume.

Assignments and queries

MethodPathPurpose
POST/api/v1/internal/assignmentsCreate an assignment from inside a container.
GET/api/v1/internal/assignments/{assignmentId}Fetch assignment status.
POST/api/v1/internal/queriesFree-form sub-query (e.g. agent asking another agent).
GET/api/v1/internal/standupFetch the standup digest the agent should read.
POST/api/v1/internal/escalationsOpen an escalation.
GET/api/v1/internal/escalations/{escalationId}/waitLong-poll for response.
POST/api/v1/internal/report-confidenceReport agent self-confidence after a turn.

Missions and issues

MethodPathPurpose
POST/api/v1/internal/missionsCreate a mission.
GET/api/v1/internal/missions/{missionId}Get mission details.
POST/api/v1/internal/missions/{missionId}/startTransition mission to running.
GET/api/v1/internal/issuesList issues.
GET/api/v1/internal/issues/{identifier}Get a specific issue.
POST/api/v1/internal/issuesCreate issue.
PATCH/api/v1/internal/issues/{identifier}Update issue status.
POST/api/v1/internal/issues/{identifier}/commentsComment on issue.

Keeper

MethodPathPurpose
POST/api/v1/internal/keeper/requestSidecar forwards a credential read request.
GET/api/v1/internal/keeper/request/{requestId}Fetch the status / decision of a previously submitted request.
POST/api/v1/internal/keeper/executeSidecar forwards a sealed credential-use request after gatekeeper approval. See the Keeper section for the full request flow.

MCP audit

MethodPathPurpose
POST/api/v1/internal/mcp-tool-callsRecord an MCP tool invocation for audit.

Port expose

MethodPathPurpose
POST/api/v1/internal/port-exposeSidecar requests a capability URL for an internal port.
User-facing port-expose lifecycle endpoints live on the public API — see Port Expose.

Cost record

POST /api/v1/internal/cost/record
The sidecar’s write target after parsing an LLM response. Validates the request, calls paymaster.Record (which inserts the cost_ledger row and emits llm.call + optionally cost.incurred), then paymaster.EnforceQuota (which emits budget.warning / budget.exceeded based on the parsed rate-limit headers). Request body: body cap 16 KiB.
{
  "workspace_id": "ws_…",
  "crew_id": "crw_…",
  "agent_id": "agt_…",
  "mission_id": "MIS-42",
  "provider": "anthropic",
  "model": "claude-opus-4-7",
  "input_tokens": 12483,
  "output_tokens": 4521,
  "cached_input_tokens": 1024,
  "cache_creation_tokens": 0,
  "billing_mode": "metered",
  "subscription_plan": "",
  "quota_remaining_pct": 0.42,
  "quota_window": "tokens",
  "had_status_429": false
}
FieldTypeNotes
workspace_idstringRequired. The sidecar fills this from its IPCConfig (set by crewshipd at container boot), so an agent that captures the token still cannot forge a row for a foreign workspace.
crew_id, agent_id, mission_idstringOptional scope. Same trust model — sidecar fills from IPCConfig.
provider, modelstringRequired. Used for rate-card lookup.
input_tokens, output_tokensintegerOptional, default 0. Providers occasionally omit usage blocks; we record the audit row anyway rather than dropping. Negative values clamped to 0 by Estimate.
cached_input_tokens, cache_creation_tokensintegerOptional, default 0.
billing_modestringmetered (default), flat_rate. Anything else → 400.
subscription_planstringRequired when billing_mode=flat_rate. Display label like "Anthropic Max".
quota_remaining_pctnumber0–1. Smallest of parsed quota windows.
quota_windowstringDisplay label: requests, tokens, input-tokens, … A non-empty value is the sentinel “rate-limit headers were present”, which gates EnforceQuota.
had_status_429boolWhen true, triggers budget.exceeded regardless of quota_remaining_pct.
Fields the sidecar has no authority over are derived server-side:
Server-derivedHow
Ledger IDGenerated in paymaster.Record.
TimestampServer now (UTC).
cost_usdpaymaster.Estimate(provider, model, ...) for metered; forced to 0 for flat_rate.
cost_confidenceprecise if input_tokens > 0 || output_tokens > 0 (heuristic — the sidecar saw a usage block), else estimate. Forced to unknown for flat_rate by paymaster.Record.
tagsAlways {"source": "sidecar"}. Caller cannot override.
Response: 202 Accepted
{
  "id": "cl_a1b2c3d4"
}
The handler writes the ledger row synchronously, then runs paymaster.EnforceQuota (best-effort — its result does not affect the response code, only the journal). The response body is intentionally minimal; the journal entries (llm.call, cost.incurred, optionally budget.warning / budget.exceeded) are where the operator-visible artifacts land. When had_status_429=true, the response is still 202 — the ledger row was written and budget.exceeded was emitted. The caller (sidecar) is responsible for propagating the upstream’s 429 back to the agent; this endpoint does not echo the 429 because it succeeded at recording the cost. Errors:
StatusCondition
400Invalid JSON, body exceeds 16 KiB, missing workspace_id / provider / model, billing_mode not metered / flat_rate, or any validation wrapped as paymaster.ErrInvalidRequest (e.g. flat_rate without subscription_plan).
401Bad or missing X-Internal-Token.
500DB error from paymaster.Record.