Connect a client, route its model calls, or hand a task to Orkestera.
This guide walks through authentication, execution and the records you can read back.
Reviewed against Orkestera release 0.4.5 on October 1, 2026.
This reference covers implemented external interfaces. It is
deployment-aware: use discovery for service URLs and enabled surfaces. The
marketing domain is not an API base URL.
Build against a released v1 contract.
The external Client Contract is stable at v1. Released fields keep their
names and remain available within v1; releases may add optional fields and error codes.
Pin a released source revision and handle additions without assuming that every deployment
serves the newest feature. An unmerged draft carries no released compatibility promise.
Recent external API additions
Release
What became available in source
0.3.0
First released v1 contract: discovery, scoped identity, sessions,
tasks, client events, OTLP and read-back.
0.4.0
Native Responses and JSON compaction on pinned provider/model routes.
0.4.1
Separate telemetry-purpose credentials and Client Edge OTLP ingestion.
0.4.5
Current application release. Ork Chat's image and audio interfaces use
browser sessions; the external v1 operations remain the same.
For event batches, use telemetry.max_batch_bytes and
telemetry.max_batch_events from the session handshake. A draft-only
telemetry.max_event_bytes field was removed before the first v1 release;
do not require it. Accepted run state can be trimmed, so inspect each result's
truncated flag and the run's truncation metadata.
START HERE
From access to your first request.
You need access to an Orkestera organization before connecting a client. If you’re joining
an existing deployment, ask its administrator for an account, your Auth URL and a registered
public client ID. You do not need to install the server on your laptop.
Choose where Orkestera runs
Cloud
The current infrastructure deploys to AWS ECS using CloudFormation,
with MongoDB Atlas. This is an operator-managed deployment, not a one-command public installer.
Joining an existing cloud deployment only requires your account and client setup below.
Local development
Authorized source users can run the stack with Docker Compose.
From the repository root, run ./run.sh, follow its setup prompts and activate the
development account using the link it prints. Confirm health with docker compose ps.
On-premises or hybrid
Discuss your network, identity, model hosting and data
requirements with the team. The containerized services provide a starting point, but the current
repository does not ship a turnkey production on-premises or hybrid installation guide.
Public SaaS · coming soon
The public service is planned at
app.orkestera.com. It is not the beta API endpoint or an open signup flow today.
Tell us about your use case to express interest.
What do I need to run the stack locally?
Repository access, Docker Engine 24+, Compose 2.30+, Bash, OpenSSL and curl. Give Docker at
least 8 GB of memory on macOS or Windows. The setup script configures development DNS and TLS;
DNS setup may require administrator rights. Use WSL2 on Windows. A provider credential is needed
for inference; repository-writing tasks also need the Runner GitHub App configured.
Follow the repository README supplied with your checkout for credential provisioning and OS-specific
setup. Local development uses auth.orkestera.test and api.orkestera.test;
it is a development environment, not a production installation recipe. Trust the development CA
explicitly rather than disabling TLS verification.
INVITED BETA TESTERS
Connecting to ork.edos.io
Use the account and organization access arranged with the beta team. Ask for your registered
client ID and confirmation that external inference is enabled for your organization.
The portal is for people; api.ork.edos.io is for model requests. Anthropic clients
use the inference origin without /v1; OpenAI-compatible clients use the base URL
with /v1. Do not append that prefix twice.
These host mappings were checked against public discovery on September 25, 2026. Check discovery
again below: an advertised endpoint identifies a surface, not proof that your account or model route is ready.
1. Check access and discover endpoints
For another deployment, replace the Auth URL above with the one your administrator supplies.
You’ll need curl for these examples. This first check does not need a token or run a model.
Expect JSON containing the issuer, OAuth endpoints and service URLs. The contract’s
endpoints.inference_openai is the full Chat Completions URL, not a base URL.
An unavailable surface is null; stop and ask your administrator rather than guessing
a hostname. Set ORK_EDGE_URL to the inference origin for your deployment.
Before inference, an administrator must configure an organization model route backed by a
provider credential in Core, register the client with the device and refresh grants, permit audience
client_edge and scope inference:write, and allow an appropriate external
execution posture. A provider API key is not an Orkestera login token.
For routed external inference, Core owns provider credentials: the organization's credential
for the route's provider takes precedence; if that provider is absent, its operator-managed
deployment credential can be used.
Seeded routes can be visible before their provider is configured. Confirm the route's protocol,
model and credential readiness; a model listed by discovery is not a successful inference test.
Native Messages and Responses requests need a matching route and compatible dialect;
they do not silently substitute an unrelated model.
2. Install the ork credential helper
ork runs on your machine and refreshes short-lived tokens through your OS keychain.
The current installation path is a source build: request repository access from the team and install
Go 1.25 or newer. There is no published Homebrew package or release-binary installer documented yet.
# From an authorized Orkestera checkout:
cd ork
go install .
export PATH="$(go env GOPATH)/bin:$PATH"
ork --help
If you use a custom GOBIN, add that directory to your PATH instead. Alternatively,
build a binary with go build -o ork . and move it to a directory already on your PATH.
macOS uses Keychain, Windows uses Credential Manager, and Linux requires a running Secret Service
keyring and session bus. A headless Linux shell without a keyring cannot store this login.
Open the verification URL printed by ork login, sign in, check that the displayed
code matches and approve. The command completes after approval. ork status should show
the selected profile, audience and scope without printing your token. The requested posture is
bounded by organization policy; observed measures external execution without claiming
to enforce the client’s local tools.
Expect a JSON model list with IDs from your organization’s routes. This does not create a task or
request a model completion. An empty list means you need a configured route before continuing.
Keep tokens out of screenshots, logs and shared configuration.
Optional: try a small model request
This sends a prompt to the configured provider and may incur usage cost. Replace
YOUR_MODEL_ID with a listed model backed by a Chat Completions-compatible route.
Legacy route names such as FREE-EXEC do not mean free inference; configured
models and provider rates determine cost.
This prints configuration; it does not install or edit the client. Merge the snippet into your
client’s configuration, keep its command-backed authentication and select a served model with a
compatible protocol. For Codex, choose a native Responses route. See
Clients & tooling for tested versions, other client profiles and protocol
limits. To add session and tool observations, follow the optional
Codex telemetry setup after inference works. Client-side tools remain with the client; provider-hosted tools remain upstream.
Writing your own client instead of using ork?
Interactive public clients can use the device grant directly. Use a registered client ID,
then show the returned verification URL and user code.
Continue on authorization_pending, slow down on slow_down, and stop
on denial or expiry. Store tokens securely and replace refresh tokens after rotation. Confidential
server clients can use client credentials instead.
If something doesn’t work
Login denied: confirm organization membership, client ID, grants and allowed scopes with your administrator.
Keychain unavailable / exit 4: unlock your keyring or use a desktop session with the supported OS credential store.
401: run ork status --profile inference; sign in again if needed. Check the Auth issuer and token audience.
403: inspect the error code. Scope and posture are policy decisions; requesting a different value cannot bypass them.
404 or an HTML page: check the hostname and API prefix. The portal URL is not the inference API.
Empty models, dialect mismatch or upstream errors: ask the administrator to check the organization route, protocol and provider credential.
Later examples use ORK_TOKEN as a placeholder for a current access token.
Set it with ORK_TOKEN="$(ork token --profile inference)" immediately before those
requests; refresh it when it expires rather than saving it in client configuration.
Once inference works, use separate service tokens for tasks,
sessions or telemetry. An inference login does not authorize those services.
Use a token for the service you’re calling.
Send Authorization: Bearer <Service Token>. Service
Tokens are short-lived and issued for one audience. Mint or refresh the
appropriate token for each service; a token for inference does not authorize
task creation or telemetry.
External API audiences and scopes
Audience
Scopes
Use
client_edge
inference:write
Models, inference and reported usage
core
client:session
Session handshake
watch
client:ingest, watch:read
Telemetry writes and read-back, respectively
platform
tasks:read, tasks:write
Task reads and mutations, respectively
platform
workflows:read, workflows:run
Published workflows and their runs
The organization, client and user come from verified token claims. Do not
send attribution headers to choose another identity. Refresh tokens rotate:
replace the previous refresh token with the newly returned one.
When opening a client session, send a descriptor with contract_version,
session_id, client_kind, client_version,
harnesses, can_enforce_policy and requested_posture.
The kind and version must match the token's claims. Mint a token with the right version first;
a handshake cannot repair a mismatch or change the token's effective posture.
can_enforce_policy is a client hint, not proof of enforcement.
Keep credentials in your application’s secret store. Do not embed them in
a public client or browser application.
Posture describes who executes and enforces.
governed delegates execution to Orkestera.
client_enforced means the approved client is responsible for
enforcing policy. observed means external execution is measured
without a claim that Orkestera enforces its local tooling. The
organization’s policy and deployment settings determine which postures are
available. Missing posture resolves to governed.
A Client Ingest Credential has one immutable purpose.
Inference-purpose credentials serve eligible clients that cannot refresh a bearer token.
Telemetry-purpose credentials authorize only the Edge’s OTLP ingestion path and require
an explicit Watch ingest grant. Neither purpose authorizes tasks, sessions or read-back;
the credential is presented to Client Edge, never directly to Watch. Old inference credentials
do not gain telemetry access. See telemetry provisioning and rotation.
Your client keeps its tools.
Orkestera’s external interfaces are protocol-based. Integrations are not
limited to a named client. Use a client that speaks an implemented protocol
and authenticate under your organization’s policy.
Chat Completions carries tool definitions, tool calls and tool results
according to the selected route’s capabilities. Anthropic Messages supports
native Messages routes; other route dialects can require translation. A
route that cannot carry the request may return
dialect_mismatch. Model and provider capabilities still apply.
The client executes its local tools and reports their results. Sending a
tool definition does not make Orkestera execute a local shell, access client
files or host a client’s MCP server. Use client.tool telemetry
to report externally executed tools. Keep tool payloads intact; disabling
tools is not an integration requirement.
Native Responses is implemented. The current source supports
POST /v1/responses and POST /v1/responses/compact through Client Edge.
Check your deployment’s discovery document before using them.
Use a native Responses route
In Core, configure a Responses route pinned to one provider and one model. The upstream
must support the features your client requests. Existing Chat Completions routes keep their
dialect; a continuation never silently switches provider or model.
Native function, custom-grammar, MCP, deferred, namespaced and provider-hosted tool payloads
pass through without translation or tool-type filtering. Authentication, scope and posture
policy still apply. Keep the complete input history, including
tool results and opaque reasoning or compaction items. The client and upstream still own tool
execution and availability.
Use stateless replay with store: false. Stored response IDs, conversation
references, background jobs, retrieval/cancellation routes and WebSockets are outside this
interface. Close the HTTP request to cancel ongoing work. Compaction returns JSON for reuse
in later input; it does not stream. Treat failed, incomplete or missing terminal events as
an unsuccessful generation.
Codex CLI: what has been exercised
The integration evidence covers unmodified Codex CLI 0.156.1 through the actual
Client Edge and inference proxy: shell execution, file edits, custom patches, deferred MCP
tools, token refresh and forced compaction against protocol fixtures. A live-provider run
exercised patches, MCP tools and tests; it did not force compaction. Desktop and ChatGPT UI
compatibility require their own validation; the CLI result does not establish either.
After signing in with ork login for audience client_edge and
scope inference:write, use ork env codex to generate the provider
configuration. Merge it into your configuration and select a served Responses model with
codex --model MODEL. The generated auth helper refreshes the token. Prefer a model
ID the client recognizes; a custom alias needs compatible client model metadata.
Opt in to Codex session and tool telemetry
Codex CLI 0.156.1 can now export its own observations through Client Edge.
Inference continues to use the rotating token helper and native Responses tools. Telemetry
uses a separate, telemetry-purpose Client Ingest Credential; enabling it does
not widen your inference credential’s permissions.
This capability is in the released v1 surface from application release 0.4.1.
Before configuring the exporter, check your deployment’s discovery document
for non-null edge_otlp_logs and edge_otlp_metrics endpoints, and confirm
rollout with your administrator. The beta host list above does not establish telemetry readiness.
Ask your administrator or beta team to arrange provisioning for your account with purpose=telemetry.
Your client registration must explicitly permit audience watch and scope
client:ingest, and current membership and policy must allow observed execution.
Provisioning uses the administrator API; a Core provisioning page is not available yet.
Save the once-returned credential in an owner-private file outside your checkout. Never
paste it into a shell command, project configuration, screenshot or log. On Unix, use mode
0600; Windows uses owner-restricted ACLs. The CLI checks the file’s protection.
Generate a new private configuration file with the updated ork helper:
# ORK_EDGE_URL is the discovered Client Edge origin, without /v1.
ork env codex --profile inference --edge-url "$ORK_EDGE_URL" \
--telemetry-credential-file "$HOME/.config/ork/codex-telemetry" \
--output "$HOME/.config/ork/codex-config.toml"
The output parent directory must exist and be outside Git. The command refuses existing
output files, insecure input permissions and credential-bearing output inside a checkout.
It writes a private file without printing its credential. Merge it into your private user-level
~/.codex/config.toml, keep its permissions private and restart Codex. Project-local
telemetry settings are ignored by the tested CLI. Its static OTLP header does not expand an
environment-variable placeholder, so use the generated private configuration.
The generated profile sends JSON logs to the Edge’s
/client/v1/otlp/v1/logs endpoint, with metrics and traces disabled. The gateway also
accepts OTLP/HTTP JSON or protobuf logs and metrics from other registered clients with the
explicit telemetry grant. No gRPC or traces endpoint is provided; no client user-agent allowlist
is applied.
To rotate, issue a new telemetry credential, regenerate a new private configuration, restart
Codex, then revoke the old credential. The exporter cannot refresh a static header itself.
Edge exchanges it for a short-lived Watch token and rechecks authorization as that token expires;
its cache bounds revocation delay to five minutes. Credentials default to 30 days and can be
issued for at most 90 days.
What appears in Watch?
Conversation starts, tool decisions and tool results can now appear alongside per-response
token metadata. Prompt text, tool arguments and raw output are dropped before persistence.
Initial sandbox and approval settings are self-reported; they do not attest enforcement or a
complete permission-change history. Exports are best effort, and interruptions can lose observations.
Codex does not report a session-ending event or per-conversation monetary cost. Its observed
run can remain nonterminal. OTLP token counts are metadata, not an additive billed run total;
gateway accounting remains authoritative and enabling telemetry does not bill inference twice.
No guaranteed join from a conversation to gateway spend is claimed. Provider-hosted tool
surcharges are not separately included in the token ledger.
A local test kept one unmodified CLI process running for 15 minutes 31.8 seconds across four
actual token exchanges and 17 authenticated export batches. It used scripted inference and
fixture identity services; this proves the refresh path under test, not deployed availability
or desktop/ChatGPT compatibility.
Telemetry authentication and delivery errors
At the Client Edge OTLP origin, only a telemetry-purpose credential is accepted. An inference
credential or direct Service Token is refused. Watch’s direct OTLP origin still uses a
watch Service Token with client:ingest. Identity is rebuilt from
verified claims, never accepted from client identity headers.
The Edge bounds compressed and expanded requests to 8 MiB and limits concurrent exports and
per-principal request rate. Watch applies its own batch and record limits. Handle
429 using Retry-After, inspect OTLP partial-success responses even on
HTTP success, and treat authentication failures as a reason to check purpose, expiry and policy.
The logs partial-success field is rejectedLogRecords.
Edge can also return 400 invalid_batch, 413 batch_too_large,
415 unsupported_media_type for unsupported content encoding,
403 policy_rejected for identity/posture failure, or
503 upstream_unavailable.
Ork Chat: goals, images and optional audio.
Platform's browser conversation is now Ork Chat. It brings your messages and Ork's replies
into one thread, with task work groups, progress narration and a composer that keeps your draft.
The welcome greets you by first name when Auth has one; the header shows Platform's current state.
The Executive interprets the goal and hands tasks to Runner, which launches one GraphAgent
process per run to plan, execute and evaluate the graph.
The composer's Stop control interrupts the pending chat turn. If task submission was already
in flight, the task can still start and is shown when the result arrives. Check its task card and
use the task's halt control to stop governed execution; stopping a reply is not a task halt.
Attach an image
Select, paste or drop PNG, JPEG, GIF or WebP images into the composer: up to four per message,
5 MB each. The browser prepares images at up to 2048 pixels on their longest edge; a resized GIF
becomes a static image. Sent images appear as tiles with a lightbox when storage is available.
Image understanding depends on the selected model and provider route supporting vision.
Images stored in the configured artifacts bucket expire after 30 days. The local fallback
uses memory and does not promise persistence across a restart. An expired, missing or unsaved
image shows its attachment label instead of claiming the image is still available. Reads are
scoped to the signed-in user and organization; the media ID grants no independent access.
Image content is sent to the selected inference provider, and LLMProxy omits image bytes from
its stored inference previews. Chat storage and upstream provider retention still apply.
Dictate a draft or read a reply aloud
Dictation starts only when you choose the microphone and approve browser access. Recording
stops after two minutes; the server accepts at most 10 MiB of supported audio. It sends the clip
to the configured transcription provider and returns text to the composer for editing.
Nothing is submitted as a goal until you send the draft. Platform discards the request's audio
and does not store or log the transcript; a draft you later send becomes ordinary chat text.
Dictation requires a supported browser, a provider credential and an operator-selected
ORK_TRANSCRIPTION_MODEL (provider/model). It is off by default; the
checked-in cloud target settings do not select a transcription model. There is no microphone
prompt while it is off. Provider transcription charges are not represented in Platform's
token-priced Spend view. Read aloud uses browser speech synthesis, starts only on request,
skips code blocks and makes no Platform transcription or inference request.
Platform browser media interfaces
Method and path
Authentication
Response
GET /chat/media/{id}
Signed-in browser session; owner-scoped
200 image bytes with private caching; 404 for missing, expired or another owner's image;
503 when storage is unavailable. An unauthenticated browser is redirected to sign in.
POST /api/chat/transcriptions
Browser session and CSRF token
Raw audio body with its Content-Type; 200 {"text":"…"}. JSON errors include
400 empty/unreadable clip, 403 missing organization, 404 dictation disabled, 413 too large,
415 unsupported format, 422 no speech, 502 provider failure and 503 missing configuration.
These routes support the signed-in Platform interface. A Client Edge or Platform Service Token
does not replace their browser session or CSRF requirements. They are outside the downloadable
external Client Contract; the composer uploads images through LiveView, without a public image-upload API.
Where do I find longer-term history?
Watch’s client APIs show activity within its retention window. For longer-term usage,
authorized organization administrators use Platform’s Spend page and its exports.
Authorized administrators can export audit history through Core.
These views depend on the Analytics service being configured and the organization holding
the analytics entitlement.
Analytics keeps organization-partitioned copies of inference usage and audit records in
Parquet and queries them with DuckDB. The source service stores remain authoritative.
Reported usage and Orkestera-routed usage stay separate, and missing prices are not treated as zero.
Watch's GET /api/inference is an operational snapshot with mixed coverage:
by_task and by_agent select the caller's organization-owned slices,
while by_model, totals and latency are fleet aggregates without tenant keys.
Use the entitled organization's Spend views for organization usage estimates; do not read
Watch's fleet totals as your organization's billed spend. An older upstream snapshot with
no owned slices produces empty task and agent maps, not proof of zero usage.
Terminal governed tasks can also retain a bounded historical trace in Analytics for 30 days,
when configured and entitled. This is a compact record for historical investigation, not a
complete transcript or an extension of every live Watch event. It is capped at 128 events and
64 KiB, excludes prompts and tool content, and is used when current task events are absent. It does not create a new public
Client Edge query endpoint.
The Analytics query, export and purge interfaces are service interfaces inside the deployment.
They are not routes exposed by Client Edge or part of the downloadable external Client Contract.
A token scope alone does not make an internal service publicly reachable.
Streaming, telemetry and recovery.
For streaming inference, request "stream": true and consume
server-sent events incrementally. A successful HTTP connection is not proof
that a generation completed; handle in-stream errors and disconnects. Use
curl -N when trying streaming requests in a terminal.
Task progress uses GET /api/events with
Accept: text/event-stream. Save each event’s
id and reconnect with Last-Event-ID. On
stream_reset, re-read the tasks you follow before relying on
later events.
For external execution, open a client session and send
client.* event batches to Watch. A
client.run snapshot replaces the complete run state; sequenced
updates must follow consecutively. A sequence gap returns
snapshot_required. Partial reporters can use unsequenced merge
events, which do not claim complete history.
Inspect every result in an ingest response: acceptance is per event. Retry a
batch using its original event IDs. Accepted data can be trimmed to
retention and storage limits, exposed through truncation metadata; missing
usage or risk reports are not evidence of zero usage or safe execution.
Usage reported through /v1/usage is recorded separately from
inference purchased through Orkestera and does not debit the organization’s
inference budget.
Discover supported contract versions and service endpoints for this deployment. A surface that is not advertised is null. Use these URLs instead of guessing hostnames. A non-null URL does not establish your account permissions, provider configuration or service health.
Responses
Status
Meaning
200
OK
Full operation contract
{
"summary": "Client-contract discovery document",
"description": "Discover supported contract versions and service endpoints for this deployment. A surface that is not advertised is null. Use these URLs instead of guessing hostnames. A non-null URL does not establish your account permissions, provider configuration or service health.",
"x-orkestera-surface": {
"service": "auth",
"status": "live",
"issue": null,
"gate": null,
"audience": null
},
"x-orkestera-errors": [],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://auth.orkestera.test"
}
},
"description": "Auth: identity and discovery."
}
],
"security": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/client-contract-document.schema.json"
}
}
}
}
}
}
Obtain a short-lived Service Token for one resource using client credentials or an approved device code, or rotate a refresh token. The requested posture can only lower the access allowed by policy. Inspect the OAuth error code as well as the HTTP status; token errors currently use 401.
Start interactive sign-in for a registered public client. Show the returned verification URI and user code, then poll the token endpoint at the returned interval. Approval is bound to the client's organization and the approving user.
Revoke a token using confidential-client authentication. Public clients cannot call this revocation endpoint; a user or administrator can revoke their access through Auth.
Present the client’s session descriptor, including client_kind and client_version matching the token claims. The response carries the effective posture and, for client_enforced sessions, the policy snapshot and its hash. The handshake does not change the token’s posture. Use the reported batch limits and inspect truncation metadata; there is no released max_event_bytes field.
Request schema · application/json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Session descriptor",
"description": "What a client presents at the session handshake (POST /client/v1/sessions, ). Every field is the client's own statement. The server checks client_kind and client_version against the token's claims and answers a mismatch with a stable error code; it never raises or lowers the posture Auth stamped into the token. `can_enforce_policy` is a hint only: attestation is decided by the operator's Client Kind registry.",
"type": "object",
"required": [
"contract_version",
"session_id",
"client_kind",
"client_version",
"harnesses",
"can_enforce_policy",
"requested_posture"
],
"properties": {
"contract_version": {
"type": "string",
"enum": [
"v1"
]
},
"session_id": {
"description": "Chosen by the client, unique per session, and carried on every client.* event of the session. Unique only within the token's principal (org_id, client_id, user_id): the server keys the session under the principal, so the same value from another org, client or user is a different session. Use a UUID.",
"$ref": "common.schema.json#/$defs/identifier"
},
"client_kind": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$",
"maxLength": 64
},
"client_version": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"harnesses": {
"type": "array",
"maxItems": 32,
"items": {
"$ref": "common.schema.json#/$defs/harness"
}
},
"can_enforce_policy": {
"type": "boolean"
},
"requested_posture": {
"$ref": "common.schema.json#/$defs/posture"
}
},
"additionalProperties": false
}
Responses
Status
Meaning
200
OK
400
Error with a stable code.
401
Error with a stable code.
403
Error with a stable code.
409
Error with a stable code.
503
Error with a stable code.
Full operation contract
{
"summary": "Session handshake",
"description": "Present the client\u2019s session descriptor, including client_kind and client_version matching the token claims. The response carries the effective posture and, for client_enforced sessions, the policy snapshot and its hash. The handshake does not change the token\u2019s posture. Use the reported batch limits and inspect truncation metadata; there is no released max_event_bytes field.",
"x-orkestera-surface": {
"service": "core",
"status": "live",
"issue": null,
"gate": "client:session",
"audience": "core"
},
"x-orkestera-errors": [
"unauthorized",
"malformed",
"untrusted_issuer",
"unknown_key",
"bad_signature",
"expired",
"not_yet_valid",
"key_source_unavailable",
"wrong_audience",
"insufficient_scope",
"policy_rejected",
"unsupported_contract_version",
"invalid_descriptor",
"client_kind_mismatch",
"client_version_mismatch",
"policy_unavailable"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://core.orkestera.test"
}
},
"description": "Core: session handshake and org policy."
}
],
"security": [
{
"serviceToken": [
"client:session"
]
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/session-descriptor.schema.json"
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/handshake-response.schema.json"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"409": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}
}
List model IDs from your organization’s route table. Accepts a Service Token or an eligible inference-purpose Client Ingest Credential. A seeded model can appear before its provider credential is configured; the list does not prove that a completion will succeed.
Responses
Status
Meaning
200
OK
401
Client Edge error: the stable code is `error.code`.
403
Client Edge error: the stable code is `error.code`.
429
Client Edge error: the stable code is `error.code`.
502
Client Edge error: the stable code is `error.code`.
503
Client Edge error: the stable code is `error.code`.
Full operation contract
{
"summary": "Models this org may call (OpenAI dialect)",
"description": "List model IDs from your organization\u2019s route table. Accepts a Service Token or an eligible inference-purpose Client Ingest Credential. A seeded model can appear before its provider credential is configured; the list does not prove that a completion will succeed.",
"x-orkestera-surface": {
"service": "client_edge",
"status": "live",
"issue": null,
"gate": "inference:write",
"audience": "client_edge"
},
"x-orkestera-errors": [
"invalid_token",
"insufficient_scope",
"client_identity_required",
"posture_not_permitted",
"rate_limited",
"upstream_unavailable"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://edge.orkestera.test"
}
},
"description": "The Client Edge; location and host decided by."
}
],
"security": [
{
"serviceToken": [
"inference:write"
]
},
{
"clientIngestCredential": []
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"401": {
"$ref": "#/components/responses/EdgeError"
},
"403": {
"$ref": "#/components/responses/EdgeError"
},
"429": {
"$ref": "#/components/responses/EdgeError"
},
"502": {
"$ref": "#/components/responses/EdgeError"
},
"503": {
"$ref": "#/components/responses/EdgeError"
}
}
}
Send an OpenAI Chat Completions request, streaming or JSON, using an available model route. Organization, user and client attribution come from the token. Inference is charged against the organization's budget in every external-execution posture.
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
401
Client Edge error: the stable code is `error.code`.
402
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
403
Client Edge error: the stable code is `error.code`.
404
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
429
Client Edge error: the stable code is `error.code`.
502
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
503
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
Send an Anthropic Messages request, streaming or JSON. Use a bearer credential or X-Api-Key, and the Anthropic-Version header required by your provider. Native Messages routes preserve the protocol; other routes may translate it or reject an unsupported dialect. Inference is charged against the organization's budget.
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code. llmproxy writes the Anthropic shape on this path too, with its own wider `type` vocabulary.
401
Client Edge error on /v1/messages, Anthropic-shaped: the stable code is `error.code`.
402
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code. llmproxy writes the Anthropic shape on this path too, with its own wider `type` vocabulary.
403
Client Edge error on /v1/messages, Anthropic-shaped: the stable code is `error.code`.
404
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code. llmproxy writes the Anthropic shape on this path too, with its own wider `type` vocabulary.
413
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code. llmproxy writes the Anthropic shape on this path too, with its own wider `type` vocabulary.
429
The Edge's rate_limited (a Client Ingest Credential exchanged too often), or llmproxy's upstream_rate_limited, which carries the provider's Retry-After. Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
502
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code. llmproxy writes the Anthropic shape on this path too, with its own wider `type` vocabulary.
503
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code. llmproxy writes the Anthropic shape on this path too, with its own wider `type` vocabulary.
Full operation contract
{
"summary": "Messages through Orkestera (Anthropic dialect)",
"description": "Send an Anthropic Messages request, streaming or JSON. Use a bearer credential or X-Api-Key, and the Anthropic-Version header required by your provider. Native Messages routes preserve the protocol; other routes may translate it or reject an unsupported dialect. Inference is charged against the organization's budget.",
"x-orkestera-surface": {
"service": "client_edge",
"status": "live",
"issue": null,
"gate": "inference:write",
"audience": "client_edge"
},
"x-orkestera-errors": [
"dialect_mismatch",
"invalid_token",
"insufficient_scope",
"client_identity_required",
"posture_not_permitted",
"rate_limited",
"upstream_unavailable",
"budget_exceeded",
"model_not_available",
"route_unavailable",
"invalid_request",
"upstream_rejected",
"upstream_rate_limited"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://edge.orkestera.test"
}
},
"description": "The Client Edge; location and host decided by."
}
],
"security": [
{
"serviceToken": [
"inference:write"
]
},
{
"clientIngestCredential": []
},
{
"clientApiKey": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"model",
"messages",
"max_tokens"
],
"description": "Anthropic Messages request, unchanged."
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"400": {
"$ref": "#/components/responses/MessagesOrUpstreamError"
},
"401": {
"$ref": "#/components/responses/MessagesError"
},
"402": {
"$ref": "#/components/responses/MessagesOrUpstreamError"
},
"403": {
"$ref": "#/components/responses/MessagesError"
},
"404": {
"$ref": "#/components/responses/MessagesOrUpstreamError"
},
"413": {
"$ref": "#/components/responses/MessagesOrUpstreamError"
},
"429": {
"description": "The Edge's rate_limited (a Client Ingest Credential exchanged too often), or llmproxy's upstream_rate_limited, which carries the provider's Retry-After. Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.",
"x-orkestera-error-dialect": "anthropic",
"headers": {
"Retry-After": {
"description": "Present on llmproxy's upstream_rate_limited; the Edge's own rate_limited sets none.",
"required": false,
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/edge-error.schema.json#/$defs/anthropic"
}
}
}
},
"502": {
"$ref": "#/components/responses/MessagesOrUpstreamError"
},
"503": {
"$ref": "#/components/responses/MessagesOrUpstreamError"
}
}
}
Report inference purchased directly from another provider. Usage is attributed to the authenticated principal, recorded as reported and not debited against Orkestera's inference budget. The body limit is 64 KiB; oversized reports currently return 502 upstream_unavailable.
Request schema · application/json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Reported usage",
"description": "`POST /v1/usage` at the Client Edge : inference a client bought from a provider directly, reported with the same token it would call the Edge with. The Edge caps the body at 64 KiB and forwards it to llmproxy's `/v1/client/usage`, which records it as `usage_source: reported` under the token's org, client and user. It is never debited, never counted as a request llmproxy served and never priced as Orkestera spend. Org, client, user and posture come from the token; a body field naming them is ignored like any other unknown field.",
"type": "object",
"required": [
"model"
],
"properties": {
"model": {
"description": "Trimmed of surrounding whitespace, then 1 to 256 bytes.",
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "\\S"
},
"provider": {
"description": "The provider the client called, as the client names it.",
"type": "string"
},
"prompt_tokens": {
"description": "Absent or null is not reported, and is stored absent, never as zero.",
"type": [
"integer",
"null"
],
"minimum": 0
},
"completion_tokens": {
"description": "Absent or null is not reported, and is stored absent, never as zero.",
"type": [
"integer",
"null"
],
"minimum": 0
},
"total_tokens": {
"description": "Absent or 0 means prompt_tokens + completion_tokens.",
"type": "integer",
"minimum": 0
},
"duration_ms": {
"type": "integer",
"minimum": 0
}
}
}
Responses
Status
Meaning
202
Recorded, not debited.
400
Client Edge error: the stable code is `error.code`.
401
Client Edge error: the stable code is `error.code`.
403
Client Edge error: the stable code is `error.code`.
429
Client Edge error: the stable code is `error.code`.
500
llmproxy could not store the report; nothing was recorded. Its `storage_unavailable`, in the Edge dialect.
502
Client Edge error: the stable code is `error.code`.
503
Client Edge error: the stable code is `error.code`.
Full operation contract
{
"summary": "Report usage bought beside Orkestera",
"description": "Report inference purchased directly from another provider. Usage is attributed to the authenticated principal, recorded as reported and not debited against Orkestera's inference budget. The body limit is 64 KiB; oversized reports currently return 502 upstream_unavailable.",
"x-orkestera-surface": {
"service": "client_edge",
"status": "live",
"issue": null,
"gate": "inference:write",
"audience": "client_edge"
},
"x-orkestera-errors": [
"invalid_usage_report",
"invalid_token",
"insufficient_scope",
"client_identity_required",
"posture_not_permitted",
"rate_limited",
"upstream_unavailable",
"storage_unavailable"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://edge.orkestera.test"
}
},
"description": "The Client Edge; location and host decided by."
}
],
"security": [
{
"serviceToken": [
"inference:write"
]
},
{
"clientIngestCredential": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/usage-report.schema.json"
}
}
}
},
"responses": {
"202": {
"description": "Recorded, not debited.",
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/usage-response.schema.json"
}
}
}
},
"400": {
"$ref": "#/components/responses/EdgeError"
},
"401": {
"$ref": "#/components/responses/EdgeError"
},
"403": {
"$ref": "#/components/responses/EdgeError"
},
"429": {
"$ref": "#/components/responses/EdgeError"
},
"500": {
"description": "llmproxy could not store the report; nothing was recorded. Its `storage_unavailable`, in the Edge dialect.",
"x-orkestera-error-dialect": "edge",
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/edge-error.schema.json#/$defs/edge"
}
}
}
},
"502": {
"$ref": "#/components/responses/EdgeError"
},
"503": {
"$ref": "#/components/responses/EdgeError"
}
}
}
Send a native Responses request on an explicitly pinned Responses route. Tool definitions, calls, results and reasoning items pass through without translation or client-specific filtering. Use stateless input replay with store:false; stored conversation references and background jobs are refused. JSON and SSE are supported. Inference uses the organization’s budget, and the route does not switch providers or models during a continuation.
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
401
Client Edge error: the stable code is `error.code`.
402
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
403
Client Edge error: the stable code is `error.code`.
404
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
429
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
502
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
503
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
413
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
Full operation contract
{
"summary": "Native Responses with full tool payloads (JSON or SSE)",
"description": "Send a native Responses request on an explicitly pinned Responses route. Tool definitions, calls, results and reasoning items pass through without translation or client-specific filtering. Use stateless input replay with store:false; stored conversation references and background jobs are refused. JSON and SSE are supported. Inference uses the organization\u2019s budget, and the route does not switch providers or models during a continuation.",
"x-orkestera-surface": {
"service": "client_edge",
"status": "live",
"issue": null,
"gate": "inference:write",
"audience": "client_edge"
},
"x-orkestera-errors": [
"dialect_mismatch",
"invalid_token",
"insufficient_scope",
"client_identity_required",
"posture_not_permitted",
"rate_limited",
"upstream_unavailable",
"budget_exceeded",
"model_not_available",
"route_unavailable",
"invalid_request",
"upstream_rejected",
"upstream_rate_limited",
"request_too_large"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://edge.orkestera.test"
}
},
"description": "The Client Edge; location and host decided by."
}
],
"security": [
{
"serviceToken": [
"inference:write"
]
},
{
"clientIngestCredential": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"model",
"input"
],
"properties": {
"model": {
"type": "string"
},
"input": {},
"stream": {
"type": "boolean"
},
"store": {
"type": "boolean",
"enum": [
false
]
},
"background": {
"type": "boolean",
"enum": [
false
]
}
},
"additionalProperties": true,
"description": "Native tool/content fields are preserved. Select a responses-dialect model route."
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object"
}
},
"text/event-stream": {
"schema": {
"type": "string",
"description": "Native Responses SSE events, including tool/continuation payloads and terminal usage."
}
}
}
},
"400": {
"$ref": "#/components/responses/EdgeOrUpstreamError"
},
"401": {
"$ref": "#/components/responses/EdgeError"
},
"402": {
"$ref": "#/components/responses/EdgeOrUpstreamError"
},
"403": {
"$ref": "#/components/responses/EdgeError"
},
"404": {
"$ref": "#/components/responses/EdgeOrUpstreamError"
},
"429": {
"$ref": "#/components/responses/EdgeOrUpstreamError"
},
"502": {
"$ref": "#/components/responses/EdgeOrUpstreamError"
},
"503": {
"$ref": "#/components/responses/EdgeOrUpstreamError"
},
"413": {
"$ref": "#/components/responses/EdgeOrUpstreamError"
}
}
}
Compact native Responses history on the same pinned provider/model route. The result is a response.compaction JSON object for later input replay. Streaming, stored histories and background work are not supported on this endpoint. Full native content and tool payloads are preserved.
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
401
Client Edge error: the stable code is `error.code`.
402
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
403
Client Edge error: the stable code is `error.code`.
404
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
429
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
502
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
503
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
413
Two writers answer here: the Client Edge, and llmproxy, whose refusals the Edge relays byte for byte (it intercepts only an upstream 401). Since both write this one dialect at `application/json`, with a stable code from errors.json. Branch on the code.
List the org's tasks (governed work), a page at a time
Audience: platform · tasks:read
List organization-owned tasks, newest first. Filter by comma-separated status and page with limit and cursor. The X-Next-Cursor response header identifies the next page. Keep the same filters when reusing a cursor. Storage failure returns 503 rather than an empty list.
Parameters
Name
Location
Required
Description
limit
query
Optional
Rows per page.
status
query
Optional
Comma-separated task statuses (contracts/task_statuses.golden.json); only tasks in one of them are listed.
cursor
query
Optional
The previous page's `X-Next-Cursor`. Opaque.
Responses
Status
Meaning
200
OK
400
Error with a stable code.
401
Error with a stable code.
403
Error with a stable code.
503
A stable code; the response carries Retry-After.
Full operation contract
{
"summary": "List the org's tasks (governed work), a page at a time",
"description": "List organization-owned tasks, newest first. Filter by comma-separated status and page with limit and cursor. The X-Next-Cursor response header identifies the next page. Keep the same filters when reusing a cursor. Storage failure returns 503 rather than an empty list.",
"x-orkestera-surface": {
"service": "platform",
"status": "live",
"issue": null,
"gate": "tasks:read",
"audience": "platform"
},
"x-orkestera-errors": [
"unauthorized",
"malformed",
"untrusted_issuer",
"unknown_key",
"bad_signature",
"expired",
"not_yet_valid",
"key_source_unavailable",
"wrong_audience",
"insufficient_scope",
"policy_rejected",
"invalid_request",
"invalid_cursor",
"org_required",
"temporarily_unavailable"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://platform.orkestera.test"
}
},
"description": "Platform: the Task API (governed work)."
}
],
"security": [
{
"serviceToken": [
"tasks:read"
]
}
],
"responses": {
"200": {
"description": "OK",
"headers": {
"X-Next-Cursor": {
"schema": {
"type": "string"
},
"description": "Present when another page follows; pass it back as `cursor`."
}
},
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Task"
}
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"503": {
"description": "A stable code; the response carries Retry-After.",
"headers": {
"Retry-After": {
"schema": {
"type": "integer"
},
"description": "Seconds to wait before retrying."
}
},
"content": {
"application/json": {
"schema": {
"$ref": "./schemas/error.schema.json"
}
}
}
}
},
"parameters": [
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 100
},
"description": "Rows per page."
},
{
"name": "status",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "Comma-separated task statuses (contracts/task_statuses.golden.json); only tasks in one of them are listed."
},
{
"name": "cursor",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "The previous page's `X-Next-Cursor`. Opaque."
}
]
}
Submit a goal for governed execution. The response identifies the task and its initial status. The organization comes from the token; policy escape flags are refused.
Read task status, issues and available results. Artifact URLs are temporary signed links; re-read the task when they expire. Missing tasks and tasks outside the caller's organization both return task_not_found.
Delete an organization-owned task that is not running. A running task returns task_status_conflict. Deleting a task is distinct from halting its execution.
Start a planned task. The response reports planning; policy escape flags are refused. Check status after any ambiguous transport failure before repeating the request.
Stop one unfinished task and mark it halted. A finished task returns task_status_conflict. A failed state write returns 503 without announcing a successful halt.
Subscribe to your organization's task progress with tasks:read or tasks:write. Save event IDs and reconnect with Last-Event-ID. When replay is unavailable the stream emits stream_reset; re-read the tasks you follow. IDs represent stream positions, not a count of your own events.
Parameters
Name
Location
Required
Description
Last-Event-ID
header
Optional
The `id:` of the last event received, sent on reconnect.
Responses
Status
Meaning
200
text/event-stream
401
Error with a stable code.
403
Error with a stable code.
Full operation contract
{
"summary": "Server-sent task progress events",
"description": "Subscribe to your organization's task progress with tasks:read or tasks:write. Save event IDs and reconnect with Last-Event-ID. When replay is unavailable the stream emits stream_reset; re-read the tasks you follow. IDs represent stream positions, not a count of your own events.",
"x-orkestera-surface": {
"service": "platform",
"status": "live",
"issue": null,
"gate": ":any_authenticated",
"audience": "platform"
},
"x-orkestera-errors": [
"unauthorized",
"malformed",
"untrusted_issuer",
"unknown_key",
"bad_signature",
"expired",
"not_yet_valid",
"key_source_unavailable",
"wrong_audience",
"insufficient_scope",
"policy_rejected"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://platform.orkestera.test"
}
},
"description": "Platform: the Task API (governed work)."
}
],
"security": [
{
"serviceToken": [
"tasks:read"
]
},
{
"serviceToken": [
"tasks:write"
]
}
],
"responses": {
"200": {
"description": "text/event-stream",
"content": {
"text/event-stream": {
"schema": {
"type": "string"
}
}
}
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
}
},
"parameters": [
{
"name": "Last-Event-ID",
"in": "header",
"required": false,
"schema": {
"type": "string"
},
"description": "The `id:` of the last event received, sent on reconnect."
}
]
}
Ingest client.* event batches under the token's organization, client and user. Requires client_enforced or observed posture. Event IDs make retries idempotent. Sequenced state updates require consecutive sequence numbers; gaps request a snapshot. Merge events carry partial unsequenced state. Inspect each event result, including accepted events marked truncated. Reported policy drift is recorded; it does not block work.
Request schema · application/json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "client.* ingest batch",
"description": "Body of POST /client/v1/events. Each event is validated on its own: one bad event is rejected in the response and does not fail the batch. Events are applied in array order.",
"type": "object",
"required": [
"events"
],
"properties": {
"events": {
"type": "array",
"minItems": 1,
"maxItems": 500,
"items": {
"oneOf": [
{
"$ref": "events/client.run.schema.json"
},
{
"$ref": "events/client.task.schema.json"
},
{
"$ref": "events/client.inference.schema.json"
},
{
"$ref": "events/client.tool.schema.json"
},
{
"$ref": "events/client.risk_posture.schema.json"
},
{
"$ref": "events/client.lifecycle.schema.json"
}
]
}
}
},
"additionalProperties": false
}
Ingest OTLP/HTTP logs as JSON or protobuf. At Watch, use a watch/client:ingest Service Token. At Client Edge, use a dedicated telemetry-purpose Client Ingest Credential; inference credentials and direct Service Tokens are refused there. Discover edge_otlp_logs for the Edge URL. Identity comes from verified claims. Raw log bodies and unsafe attributes are omitted. Inspect partialSuccess.rejectedLogRecords and honor Retry-After on 429. Edge bounds identity/gzip input and expanded data to 8 MiB; Watch retains its own record limits. No gRPC or traces endpoint is served.
Request schema · application/x-protobuf
{
"type": "string",
"format": "binary"
}
Request schema · application/json
{
"type": "object"
}
Responses
Status
Meaning
200
Accepted. The body is OTLP's own export response in the encoding the request used: empty on full success, and otherwise a partial success naming how many records were not accepted and why.
400
Error with a stable code.
401
Error with a stable code.
403
Error with a stable code.
413
Error with a stable code.
415
Error with a stable code.
503
Error with a stable code.
Full operation contract
{
"summary": "Ingest an OTLP/HTTP logs export",
"description": "Ingest OTLP/HTTP logs as JSON or protobuf. At Watch, use a watch/client:ingest Service Token. At Client Edge, use a dedicated telemetry-purpose Client Ingest Credential; inference credentials and direct Service Tokens are refused there. Discover edge_otlp_logs for the Edge URL. Identity comes from verified claims. Raw log bodies and unsafe attributes are omitted. Inspect partialSuccess.rejectedLogRecords and honor Retry-After on 429. Edge bounds identity/gzip input and expanded data to 8 MiB; Watch retains its own record limits. No gRPC or traces endpoint is served.",
"x-orkestera-surface": {
"service": "watch",
"status": "live",
"issue": null,
"gate": "client:ingest",
"audience": "watch"
},
"x-orkestera-errors": [
"unauthorized",
"malformed",
"untrusted_issuer",
"unknown_key",
"bad_signature",
"expired",
"not_yet_valid",
"key_source_unavailable",
"wrong_audience",
"insufficient_scope",
"policy_rejected",
"posture_governed",
"invalid_batch",
"unsupported_media_type",
"batch_too_large",
"temporarily_unavailable"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://watch.orkestera.test"
}
},
"description": "Watch: client.* ingest and read-back."
},
{
"url": "{base}",
"variables": {
"base": {
"default": "https://api.orkestera.test"
}
},
"description": "Client Edge: telemetry-purpose credential only."
}
],
"security": [
{
"serviceToken": [
"client:ingest"
]
},
{
"clientIngestCredential": []
}
],
"requestBody": {
"required": true,
"description": "An OTLP ExportLogsServiceRequest (opentelemetry-proto v1), protobuf-encoded as application/x-protobuf or JSON-encoded as application/json. Any other Content-Type is unsupported_media_type.",
"content": {
"application/x-protobuf": {
"schema": {
"type": "string",
"format": "binary"
}
},
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Accepted. The body is OTLP's own export response in the encoding the request used: empty on full success, and otherwise a partial success naming how many records were not accepted and why.",
"content": {
"application/x-protobuf": {
"schema": {
"type": "string",
"format": "binary"
}
},
"application/json": {
"schema": {
"type": "object",
"properties": {
"partialSuccess": {
"type": "object",
"required": [
"rejectedLogRecords",
"errorMessage"
],
"properties": {
"rejectedLogRecords": {
"type": "integer",
"minimum": 1
},
"errorMessage": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"413": {
"$ref": "#/components/responses/Error"
},
"415": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
},
"x-orkestera-edge-credential-purpose": "telemetry"
}
Ingest OTLP/HTTP metrics as JSON or protobuf. Watch accepts a watch/client:ingest Service Token; Client Edge accepts only a telemetry-purpose Client Ingest Credential at the discovered edge_otlp_metrics URL. Cumulative sums and gauges report running totals per series; delta sums are not accumulated across exports. The resulting run is unsequenced and does not claim complete history. Inspect partial-success responses. Codex’s generated profile disables metrics and uses logs only.
Request schema · application/x-protobuf
{
"type": "string",
"format": "binary"
}
Request schema · application/json
{
"type": "object"
}
Responses
Status
Meaning
200
Accepted. The body is OTLP's own export response in the encoding the request used: empty on full success, and otherwise a partial success naming how many records were not accepted and why.
400
Error with a stable code.
401
Error with a stable code.
403
Error with a stable code.
413
Error with a stable code.
415
Error with a stable code.
503
Error with a stable code.
Full operation contract
{
"summary": "Ingest an OTLP/HTTP metrics export",
"description": "Ingest OTLP/HTTP metrics as JSON or protobuf. Watch accepts a watch/client:ingest Service Token; Client Edge accepts only a telemetry-purpose Client Ingest Credential at the discovered edge_otlp_metrics URL. Cumulative sums and gauges report running totals per series; delta sums are not accumulated across exports. The resulting run is unsequenced and does not claim complete history. Inspect partial-success responses. Codex\u2019s generated profile disables metrics and uses logs only.",
"x-orkestera-surface": {
"service": "watch",
"status": "live",
"issue": null,
"gate": "client:ingest",
"audience": "watch"
},
"x-orkestera-errors": [
"unauthorized",
"malformed",
"untrusted_issuer",
"unknown_key",
"bad_signature",
"expired",
"not_yet_valid",
"key_source_unavailable",
"wrong_audience",
"insufficient_scope",
"policy_rejected",
"posture_governed",
"invalid_batch",
"unsupported_media_type",
"batch_too_large",
"temporarily_unavailable"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://watch.orkestera.test"
}
},
"description": "Watch: client.* ingest and read-back."
},
{
"url": "{base}",
"variables": {
"base": {
"default": "https://api.orkestera.test"
}
},
"description": "Client Edge: telemetry-purpose credential only."
}
],
"security": [
{
"serviceToken": [
"client:ingest"
]
},
{
"clientIngestCredential": []
}
],
"requestBody": {
"required": true,
"description": "An OTLP ExportMetricsServiceRequest (opentelemetry-proto v1), protobuf-encoded as application/x-protobuf or JSON-encoded as application/json. Any other Content-Type is unsupported_media_type.",
"content": {
"application/x-protobuf": {
"schema": {
"type": "string",
"format": "binary"
}
},
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Accepted. The body is OTLP's own export response in the encoding the request used: empty on full success, and otherwise a partial success naming how many records were not accepted and why.",
"content": {
"application/x-protobuf": {
"schema": {
"type": "string",
"format": "binary"
}
},
"application/json": {
"schema": {
"type": "object",
"properties": {
"partialSuccess": {
"type": "object",
"required": [
"rejectedDataPoints",
"errorMessage"
],
"properties": {
"rejectedDataPoints": {
"type": "integer",
"minimum": 1
},
"errorMessage": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"413": {
"$ref": "#/components/responses/Error"
},
"415": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
},
"x-orkestera-edge-credential-purpose": "telemetry"
}
Page through the organization's external runs, newest first by creation. Updates do not move runs between pages. Use the returned cursor; invalid cursors are rejected. Optional filters narrow the current organization only.
Read reduced run state, attribution and reporting status. Client-chosen run IDs may collide; narrow the lookup with client_id and session_id. Ambiguous matches return run_ambiguous. Runs outside the organization return run_not_found.
Analyze external and governed runs by client kind, harness, model, posture or user within retained history. Figures are available, partial or unavailable. A partial value is a lower bound; absent data is never reported as zero. Inspect the sources and window metadata before comparing totals.
Parameters
Name
Location
Required
Description
group_by
query
Optional
The one breakdown to answer. Absent: all five.
since
query
Optional
RFC 3339 with an offset; runs that started at or after it. Earlier than Watch's retention window is clamped to the window's start. In the future is invalid_request.
user_id
query
Optional
Only runs this user of the token's org started. A user of another org matches nothing.
Responses
Status
Meaning
200
OK
400
Error with a stable code.
401
Error with a stable code.
403
Error with a stable code.
503
Error with a stable code.
Full operation contract
{
"summary": "Cross-client analysis for the org",
"description": "Analyze external and governed runs by client kind, harness, model, posture or user within retained history. Figures are available, partial or unavailable. A partial value is a lower bound; absent data is never reported as zero. Inspect the sources and window metadata before comparing totals.",
"x-orkestera-surface": {
"service": "watch",
"status": "live",
"issue": null,
"gate": "watch:read",
"audience": "watch"
},
"x-orkestera-errors": [
"unauthorized",
"malformed",
"untrusted_issuer",
"unknown_key",
"bad_signature",
"expired",
"not_yet_valid",
"key_source_unavailable",
"wrong_audience",
"insufficient_scope",
"policy_rejected",
"org_required",
"invalid_request",
"temporarily_unavailable"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://watch.orkestera.test"
}
},
"description": "Watch: client.* ingest and read-back."
}
],
"security": [
{
"serviceToken": [
"watch:read"
]
}
],
"parameters": [
{
"name": "group_by",
"in": "query",
"schema": {
"type": "string",
"enum": [
"client_kind",
"harness",
"model",
"posture",
"user"
]
},
"description": "The one breakdown to answer. Absent: all five."
},
{
"name": "since",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
},
"description": "RFC 3339 with an offset; runs that started at or after it. Earlier than Watch's retention window is clamped to the window's start. In the future is invalid_request."
},
{
"name": "user_id",
"in": "query",
"schema": {
"type": "string",
"minLength": 1
},
"description": "Only runs this user of the token's org started. A user of another org matches nothing."
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AnalysisReport"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}
}
Inference statistics: fleet aggregates and organization-owned slices
Audience: watch · watch:read
Read an operational inference snapshot with mixed coverage. by_task and by_agent use the token’s organization-owned slices; by_model, totals and latency are fleet aggregates with no tenant key. Older snapshots without owned splits return empty tenant maps. Distinguish lifetime counters from rolling-window metrics and inspect availability. Fleet totals are not your organization’s billed spend; use its entitled Spend views for organization usage estimates.
Responses
Status
Meaning
200
OK
401
Error with a stable code.
403
Error with a stable code.
Full operation contract
{
"summary": "Inference statistics: fleet aggregates and organization-owned slices",
"description": "Read an operational inference snapshot with mixed coverage. by_task and by_agent use the token\u2019s organization-owned slices; by_model, totals and latency are fleet aggregates with no tenant key. Older snapshots without owned splits return empty tenant maps. Distinguish lifetime counters from rolling-window metrics and inspect availability. Fleet totals are not your organization\u2019s billed spend; use its entitled Spend views for organization usage estimates.",
"x-orkestera-surface": {
"service": "watch",
"status": "live",
"issue": null,
"gate": "watch:read",
"audience": "watch"
},
"x-orkestera-errors": [
"unauthorized",
"malformed",
"untrusted_issuer",
"unknown_key",
"bad_signature",
"expired",
"not_yet_valid",
"key_source_unavailable",
"wrong_audience",
"insufficient_scope",
"policy_rejected"
],
"servers": [
{
"url": "{base}",
"variables": {
"base": {
"default": "https://watch.orkestera.test"
}
},
"description": "Watch: client.* ingest and read-back."
}
],
"security": [
{
"serviceToken": [
"watch:read"
]
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
}
}
}
Use a Platform token with workflows:read to discover published revisions and follow runs. Starting or halting a run requires workflows:run. Run submission uses the approving user from the token; the user must remain an active organization member with permission to execute the workflow.
Submit a JSON body such as {"goal":"Review the checkout flow"} to a positive revision number. A successful submission returns 202 with a data wrapper; ordinary reads and halt return 200. Errors return {"error":"…"}: access denied (403), missing workflow (404), halted or finished run (409), invalid goal or non-executable revision (422), or unavailable storage (503). These routes are verified against the Platform router and controller and are not yet part of the downloadable Client Contract.
The status response includes task_id, run_id, status, the published revision digest, node states and evaluations, and the workflow result when available. Node progress reflects the events received; it is not a complete execution transcript. Failure reads expose a stage and safe headline rather than raw provider failure text.
Follow /api/workflow_runs/{task_id}/events with Accept: text/event-stream. Keep each event ID, reconnect with Last-Event-ID, and re-read run status on stream_reset. This stream is limited to that run in your organization. Halting an unfinished run returns its halted status; a finished run returns 409. After an ambiguous submission, inspect existing runs before retrying: there is no documented create-run idempotency key.
Errors & retries
Inspect both the HTTP status and the endpoint’s error code. Identity uses OAuth errors; Client Edge uses an OpenAI-style error object, and Messages uses an Anthropic-style error envelope. Workflow errors use a text error field. Do not assume a single error schema across services.
Recovery guidance
Failure
What to do
401 · invalid_token
Refresh or obtain a token for the correct audience; verify its expiry.
403 · insufficient_scope / posture_not_permitted
Check the registration’s grants and organization policy. Repeated retries will not add access.
402 · budget_exceeded
Review the organization’s inference budget before retrying.
429
Honor Retry-After when present and back off with jitter.
503 · temporarily_unavailable
Retry safe reads with backoff. Replay event batches with their original event IDs. Do not blindly repeat task creation or inference after an ambiguous failure.
snapshot_required / stream_reset
Send a fresh run snapshot or re-read task state, respectively.