REST API

The orchex cloud REST API lets you run orchestrations, check status, and manage provider keys programmatically — without going through MCP.

Authentication

All API requests require authentication via one of:

  • API KeyX-API-Key: orchex_sk_... header (for programmatic access)
  • Session Cookie — Set by the dashboard login flow (for browser-based access)
curl https://api.orchex.dev/api/v1/status \
  -H "X-API-Key: orchex_sk_your-key-here"

Endpoints

POST /api/v1/execute

Start a new orchestration execution.

Request:

{
  "streamId": "auth-middleware",
  "prompt": "Create Express middleware that validates JWT tokens...",
  "model": "claude-sonnet-4-20250514",
  "maxTokens": 16384,
  "timeoutMs": 660000
}
Field Type Required Description
streamId string Yes Unique identifier for this stream
prompt string Yes Full prompt including context and instructions
model string Yes Model ID (e.g., claude-sonnet-4-20250514)
maxTokens number Yes Maximum output tokens
timeoutMs number No Job timeout (default: 660000ms / 11 minutes)

Response:

{
  "jobId": "job_abc123def456"
}

BYOK Support: Include your own API key for the LLM provider. orchex encrypts it with AES-256-GCM, uses it for the request, and discards it afterward. Manage BYOK keys via the Provider Keys endpoints.

GET /api/v1/job/:id

Poll for job status and results.

Response (running):

{
  "status": "running"
}

Response (completed):

{
  "status": "completed",
  "output": "...",
  "artifact": {
    "streamId": "auth-middleware",
    "status": "complete",
    "operations": [
      { "type": "create", "path": "src/middleware/auth.ts", "content": "..." }
    ],
    "filesChanged": ["src/middleware/auth.ts"],
    "summary": "Created JWT auth middleware"
  },
  "tokensUsed": { "input": 4200, "output": 1800 }
}

Response (failed):

{
  "status": "failed",
  "error": "Rate limit exceeded"
}
Status Description
pending Job queued, not yet started
running LLM is processing the prompt
completed Job finished successfully
failed Job failed with an error

POST /api/v1/job/:id/cancel

Cancel a running job. Stops further token consumption.

Response:

{
  "cancelled": true
}

GET /api/v1/history

List recent orchestration runs for the authenticated user.

Query Parameters:

Parameter Type Default Description
offset number 0 Pagination offset
limit number 10 Results per page (max 100)
organizationId string Filter by organization

Response:

{
  "items": [
    {
      "id": "run-abc123",
      "feature": "user-auth",
      "status": "complete",
      "streamCount": 5,
      "waveCount": 3,
      "durationMs": 180000,
      "startedAt": "2026-02-24T10:00:00Z",
      "totalCostUsd": 0.42
    }
  ],
  "total": 47,
  "offset": 0,
  "limit": 10
}

GET /api/v1/history/:id

Get detailed information about a specific run, including all streams.

Response:

{
  "run": {
    "id": "run-abc123",
    "feature": "user-auth",
    "status": "complete",
    "streamCount": 5,
    "waveCount": 3,
    "durationMs": 180000,
    "startedAt": "2026-02-24T10:00:00Z"
  },
  "streams": [
    {
      "streamId": "auth-types",
      "name": "Auth Types",
      "status": "complete",
      "waveNumber": 1,
      "filesChanged": ["src/types/auth.ts"],
      "tokensInput": 2000,
      "tokensOutput": 800
    }
  ]
}

Provider Keys

BYOK key management endpoints. All keys are encrypted with AES-256-GCM at rest.

GET /api/v1/keys

List stored provider keys (key values are masked).

Response:

{
  "keys": [
    {
      "provider": "anthropic",
      "maskedKey": "sk-ant-...xxxx",
      "createdAt": "2026-02-20T10:00:00Z"
    },
    {
      "provider": "openai",
      "maskedKey": "sk-...xxxx",
      "createdAt": "2026-02-21T14:00:00Z"
    }
  ]
}

POST /api/v1/keys

Store a new provider key. The key is validated against the provider's API before storage.

Request:

{
  "provider": "anthropic",
  "key": "sk-ant-your-api-key",
  "tosAccepted": true
}
Field Type Required Description
provider string Yes Provider name: anthropic, openai, gemini, deepseek
key string Yes Raw API key
tosAccepted boolean Yes Must be true — acknowledges BYOK terms

Response:

{
  "provider": "anthropic",
  "maskedKey": "sk-ant-...xxxx",
  "valid": true,
  "createdAt": "2026-02-24T10:00:00Z"
}

Validation: orchex makes a lightweight test call to the provider's API (10-second timeout) to verify the key works before storing it.

DELETE /api/v1/keys/:provider

Delete a stored provider key.

Response:

{
  "deleted": true,
  "provider": "anthropic"
}

Error Responses

All endpoints return errors in a consistent format:

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired API key"
  }
}

Common Error Codes

Code HTTP Status Description
UNAUTHORIZED 401 Invalid or missing API key
FORBIDDEN 403 Not authorized for this resource
NOT_FOUND 404 Resource not found
RATE_LIMITED 429 Too many requests — check Retry-After header
QUOTA_EXCEEDED 402 Monthly run quota exceeded
VALIDATION_ERROR 400 Invalid request body
INTERNAL_ERROR 500 Server error — retry with backoff

Rate Limiting

API requests are rate-limited per user:

Tier Requests/minute
Trial 10
Pro 60
Team 120
Enterprise Custom

When rate-limited, the response includes a Retry-After header with the number of seconds to wait.

Polling Best Practices

When polling /api/v1/job/:id for results:

  1. Start at 1-second intervals for quick jobs
  2. Back off exponentially — 1s, 2s, 4s, 8s... up to 10s max
  3. Add jitter — Randomize intervals slightly to prevent thundering herd
  4. Set a total timeout — Don't poll forever; cancel and retry if needed
async function pollJob(jobId: string, maxMs = 660000): Promise<JobResult> {
  const start = Date.now();
  let interval = 1000;

  while (Date.now() - start < maxMs) {
    const res = await fetch(`/api/v1/job/${jobId}`);
    const data = await res.json();

    if (data.status === 'completed' || data.status === 'failed') {
      return data;
    }

    await sleep(interval + Math.random() * 200);
    interval = Math.min(interval * 1.5, 10000);
  }

  await fetch(`/api/v1/job/${jobId}/cancel`, { method: 'POST' });
  throw new Error('Job timed out');
}