Skip to content

API Contract

REST API reference for the AI Workloads Platform. Every endpoint with request/response shapes, query parameters, status codes, and usage notes.

API version: 1.0

Related documents

  • Design - Component design behind these endpoints
  • Domain Specs - BDD scenarios that exercise these endpoints
  • Quickstart - Smoke tests using these endpoints

Health

Base path: /health | Auth: None

GET /health

System health check. Verifies database connectivity, Redis availability, and last poll recency.

{
  "status": "healthy",
  "checks": {
    "database": { "status": "ok", "latency_ms": 2 },
    "redis": { "status": "ok", "latency_ms": 1 },
    "last_poll": {
      "status": "ok",
      "last_poll_at": "2026-03-10T12:00:00Z",
      "minutes_ago": 15
    }
  },
  "version": "1.0.0"
}
{
  "status": "degraded",
  "checks": {
    "database": { "status": "ok", "latency_ms": 2 },
    "redis": { "status": "error", "error": "Connection refused" },
    "last_poll": {
      "status": "warning",
      "last_poll_at": "2026-03-10T08:00:00Z",
      "minutes_ago": 240
    }
  },
  "version": "1.0.0"
}

Note

  • Returns 200 if all checks pass, 503 if any check fails (for load balancer integration).
  • last_poll status is "warning" if > 90 min ago, "error" if > 180 min.

Connections

Base path: /api/v1/connections | Auth: Clerk JWT

POST /api/v1/connections

Register a new provider API key.

Request body:

{
  "provider": "openai | anthropic | openrouter",
  "api_key": "sk-admin-...",
  "project_id": "uuid (optional, defaults to Default project)"
}
{
  "id": "uuid",
  "provider": "openai",
  "status": "active",
  "last_polled_at": null,
  "project_id": "uuid",
  "project_name": "Default",
  "created_at": "2026-03-10T00:00:00Z"
}
{
  "detail": "API key validation failed: insufficient permissions. OpenAI admin keys must start with sk-admin-."
}
{
  "detail": "A connection for openai already exists in this organization."
}

Warning

  • The API key is validated against the provider's usage API, then stored in AWS Secrets Manager. Only the ARN is persisted in the database.
  • The response never includes the api_key.
  • If project_id is omitted, a "Default" project is auto-created and the connection is mapped to it.

GET /api/v1/connections

List all connections for the authenticated org.

{
  "connections": [
    {
      "id": "uuid",
      "provider": "openai",
      "status": "active",
      "last_polled_at": "2026-03-10T12:00:00Z",
      "consecutive_failures": 0,
      "project_id": "uuid",
      "project_name": "Default",
      "created_at": "2026-03-10T00:00:00Z"
    }
  ]
}

GET /api/v1/connections/{id}

Get a single connection by ID. Returns the same shape as a list item, or 404 if not found / not owned by this org.

DELETE /api/v1/connections/{id}

Remove a connection. Deletes the Secrets Manager secret and deactivates the workload.

Status Description
204 No content
404 Connection not found

Note

  • Historical telemetry events linked to this connection's workloads are not deleted.
  • The Secrets Manager secret is scheduled for deletion (30-day recovery window).

POST /api/v1/connections/{id}/sync

Trigger an immediate sync (manual poll) for this connection.

{
  "message": "Sync job enqueued",
  "job_id": "arq-job-uuid"
}

Connection is in "error" or "disabled" status.

Note

Rate-limited to 1 manual sync per connection per 5 minutes.

PUT /api/v1/connections/{id}/project

Re-map a connection to a different project.

Request body:

{
  "project_id": "uuid"
}
{
  "id": "uuid",
  "provider": "openai",
  "project_id": "uuid",
  "project_name": "Production App"
}

Connection or project not found.

Note

  • Creates a new Workload under the target project and marks it active.
  • The previous workload is deactivated (is_active = false).
  • Historical telemetry stays on the old workload - only future events route to the new one.

Telemetry

Base path: /api/v1/telemetry | Auth: Clerk JWT

GET /api/v1/telemetry/summary

Dashboard summary for the authenticated org.

Query parameters:

Parameter Type Default
start_date ISO date 30 days ago
end_date ISO date today
project_id UUID (optional)
{
  "total_co2_kg": 12.45,
  "total_co2_lower_bound_kg": 10.58,
  "total_co2_upper_bound_kg": 14.32,
  "total_tokens": 5420000,
  "cached_vs_uncached": {
    "uncached_tokens": 4200000,
    "cached_tokens": 1020000,
    "cache_creation_tokens": 200000,
    "cached_co2_savings_kg": 2.10
  },
  "per_model": [
    {
      "model": "gpt-4o",
      "total_tokens": 3000000,
      "co2_kg": 8.20,
      "percentage": 65.9
    },
    {
      "model": "claude-3-haiku-20240307",
      "total_tokens": 2420000,
      "co2_kg": 4.25,
      "percentage": 34.1
    }
  ],
  "daily_chart": [
    { "date": "2026-03-01", "co2_kg": 0.42, "tokens": 180000 }
  ],
  "factors_version": "v1.0",
  "period": {
    "start": "2026-02-08",
    "end": "2026-03-10"
  }
}

GET /api/v1/telemetry/events

Paginated list of raw telemetry events.

Query parameters:

Parameter Type Default
start_date ISO date (optional)
end_date ISO date (optional)
model string (optional)
project_id UUID (optional)
page integer 1
page_size integer 50 (max 200)
{
  "events": [
    {
      "id": "uuid",
      "model": "gpt-4o",
      "provider": "openai",
      "bucket_start": "2026-03-10T12:00:00Z",
      "bucket_end": "2026-03-10T13:00:00Z",
      "input_tokens_uncached": 50000,
      "input_tokens_cached": 0,
      "input_tokens_cache_creation": 0,
      "output_tokens": 12000,
      "co2_kg": 0.015,
      "factors_version": "v1.0",
      "project_name": "Default",
      "event_timestamp": "2026-03-10T12:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 50,
    "total_items": 234,
    "total_pages": 5
  }
}

GET /api/v1/telemetry/models

List distinct models with aggregated usage for the org.

{
  "models": [
    {
      "model": "gpt-4o",
      "provider": "openai",
      "total_tokens": 3000000,
      "total_co2_kg": 8.20,
      "event_count": 720,
      "first_seen": "2026-02-15T00:00:00Z",
      "last_seen": "2026-03-10T12:00:00Z"
    }
  ]
}

Projects

Base path: /api/v1/projects | Auth: Clerk JWT

POST /api/v1/projects

Create a new project.

Request body:

{ "name": "Production App" }
{
  "id": "uuid",
  "name": "Production App",
  "is_default": false,
  "workload_count": 0,
  "total_co2_kg": 0,
  "created_at": "2026-03-10T00:00:00Z"
}

Project with this name already exists in the org.

GET /api/v1/projects

List all projects for the authenticated org.

{
  "projects": [
    {
      "id": "uuid",
      "name": "Default",
      "is_default": true,
      "workload_count": 2,
      "total_co2_kg": 12.45,
      "created_at": "2026-03-01T00:00:00Z"
    },
    {
      "id": "uuid",
      "name": "Production App",
      "is_default": false,
      "workload_count": 1,
      "total_co2_kg": 3.20,
      "created_at": "2026-03-10T00:00:00Z"
    }
  ]
}

GET /api/v1/projects/{id}

Get project detail with telemetry summary.

Query parameters: start_date (ISO date), end_date (ISO date)

{
  "id": "uuid",
  "name": "Production App",
  "is_default": false,
  "connections": [
    { "id": "uuid", "provider": "openai", "status": "active" }
  ],
  "summary": {
    "total_co2_kg": 3.20,
    "total_tokens": 1200000,
    "per_model": ["..."],
    "daily_chart": ["..."]
  },
  "created_at": "2026-03-10T00:00:00Z"
}

PATCH /api/v1/projects/{id}

Update project name. The Default project cannot be renamed.

DELETE /api/v1/projects/{id}

Delete a project. All workloads must be re-mapped first.

Status Description
204 No content
400 Cannot delete Default project
409 Project still has active workloads

Carbon Factors

Base path: /api/v1/carbon-factors | Auth: Clerk JWT

GET /api/v1/carbon-factors/current

Get the current (latest) carbon factors version with all tier definitions.

{
  "version": "v1.0",
  "tiers": [
    {
      "model_tier": "tier_1",
      "model_patterns": ["claude-3-haiku*", "gpt-4o-mini*"],
      "energy_per_token_prefill_j": 0.0001,
      "energy_per_token_decode_j": 0.0003,
      "energy_per_token_cached_j": 0.00001,
      "pue": 1.3,
      "grid_intensity_kg_per_kwh": 0.00035,
      "uncertainty_pct": 30
    }
  ],
  "created_at": "2026-03-01T00:00:00Z"
}

GET /api/v1/carbon-factors/{version}

Get a specific carbon factors version by version string. Returns 404 if version not found.

GET /api/v1/carbon-factors

List all available carbon factors versions.

{
  "versions": [
    {
      "version": "v1.0",
      "tier_count": 4,
      "created_at": "2026-03-01T00:00:00Z"
    }
  ]
}

Methodology (Public)

Base path: /api/v1/methodology | Auth: None

GET /api/v1/methodology

Public methodology endpoint describing the emissions calculation approach.

{
  "version": "v1.0",
  "pipeline": "tokens → energy (J) → kWh → CO2 (kg)",
  "phases": [
    "prefill (uncached input)",
    "decode (output)",
    "cached (cache_read)"
  ],
  "pue_values": {
    "known_hyperscalers": 1.3,
    "unknown_providers": 1.55
  },
  "grid_intensity": {
    "value_kg_per_kwh": 0.00035,
    "source": "EPA eGRID2023 U.S. national average"
  },
  "uncertainty": {
    "default_pct": 30,
    "basis": "Combined uncertainty from model energy estimates, PUE variation, and grid intensity variation"
  },
  "sources": [
    "EPA eGRID2023",
    "IEA Data Centers and Data Transmission Networks Report",
    "Published model architecture papers"
  ]
}

Note

This endpoint is public (no auth) to support transparency and third-party auditing. The methodology version corresponds to the Carbon Factors version used in calculations.


Billing

Base path: /api/v1/billing | Auth: Clerk JWT

GET /api/v1/billing/status

Get current billing status for the authenticated org.

{
  "plan_tier": "starter",
  "stripe_customer_id": "cus_...",
  "current_period": {
    "id": "uuid",
    "period_start": "2026-03-01",
    "period_end": "2026-03-31",
    "status": "open",
    "total_co2_kg": 8.75,
    "credits_retired": null
  },
  "past_periods": [
    {
      "id": "uuid",
      "period_start": "2026-02-01",
      "period_end": "2026-02-28",
      "status": "closed",
      "total_co2_kg": 12.45,
      "credits_retired": 12.45,
      "receipt_serial": "CL-202602-00001"
    }
  ]
}

POST /api/v1/billing/upgrade

Initiate a plan upgrade via Stripe Checkout.

Request body:

{ "plan": "starter | growth | scale | enterprise" }
{
  "checkout_url": "https://checkout.stripe.com/c/pay/...",
  "session_id": "cs_..."
}

Already on requested plan or downgrade not supported via this endpoint.

Note

  • After Stripe checkout completes, the invoice.payment_succeeded webhook updates the org's plan_tier.
  • Enterprise tier requires sales contact - returns a contact form URL instead of checkout.

POST /api/v1/billing/portal

Get a Stripe Customer Portal link for managing subscription.

{
  "portal_url": "https://billing.stripe.com/p/session/..."
}

Receipts

Authenticated base path: /api/v1/receipts | Public base path: /public/receipts

GET /api/v1/receipts

List receipts for the authenticated org. Auth: Clerk JWT.

Query parameters:

Parameter Type Default
start_date ISO date (optional)
end_date ISO date (optional)
page integer 1
page_size integer 20 (max 100)
{
  "receipts": [
    {
      "id": "uuid",
      "serial_number": "CL-202603-00001",
      "co2_retired_kg": 12.45,
      "credit_serial_numbers": ["VCS-2024-001234", "VCS-2024-001235"],
      "period_start": "2026-02-01",
      "period_end": "2026-02-28",
      "pdf_url": "/api/v1/receipts/uuid/pdf",
      "verification_url": "https://api.carbonlabs.ai/public/receipts/verify/CL-202603-00001",
      "created_at": "2026-03-03T03:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_items": 3,
    "total_pages": 1
  }
}
{
  "detail": "Receipts require a paid plan. Upgrade at /api/v1/billing/upgrade.",
  "upgrade_url": "/api/v1/billing/upgrade"
}

GET /api/v1/receipts/{id}/pdf

Download the branded PDF receipt. Auth: Clerk JWT.

Status Description
200 Content-Type: application/pdf - file download
404 Receipt not found or PDF not yet generated

GET /public/receipts/verify/{serial_number}

Public receipt verification endpoint. Auth: None.

{
  "serial_number": "CL-202603-00001",
  "co2_retired_kg": 12.45,
  "credit_serial_numbers": ["VCS-2024-001234", "VCS-2024-001235"],
  "period_start": "2026-02-01",
  "period_end": "2026-02-28",
  "payload_hash": "a1b2c3d4e5f6...",
  "signature": "f6e5d4c3b2a1...",
  "public_key": "1a2b3c4d5e6f...",
  "key_version": 1,
  "verification_instructions": "To verify: 1) SHA-256 hash the payload fields (serial_number, co2_retired_kg, credit_serial_numbers, period_start, period_end) as canonical JSON. 2) Verify the Ed25519 signature against the hash using the provided public_key.",
  "verified": true
}
{
  "detail": "No receipt found with serial number CL-202603-99999."
}

Note

  • The verified field is the server-side verification result. Third parties should independently verify using the raw signature and public key.
  • Rate-limited (60 req/min per IP) to prevent abuse.
  • No org-specific data (org name, user info) is exposed on this public endpoint.

Export

Base path: /api/v1/export | Auth: Clerk JWT

GET /api/v1/export/telemetry

Export telemetry data as CSV or JSON.

Query parameters:

Parameter Type Default
format csv or json (required)
start_date ISO date 30 days ago
end_date ISO date today
project_id UUID (optional)
model string (optional)

Headers: Content-Type: text/csv, Content-Disposition: attachment; filename="telemetry-2026-03-10.csv"

Columns: date, model, provider, project_name, input_tokens_uncached, input_tokens_cached, input_tokens_cache_creation, output_tokens, co2_kg, factors_version

{
  "events": ["..."],
  "export_metadata": {
    "exported_at": "2026-03-10T15:00:00Z",
    "total_records": 234,
    "filters_applied": {
      "start_date": "2026-02-08",
      "end_date": "2026-03-10"
    }
  }
}

Note

  • Available on all tiers (free and paid).
  • Maximum export window: 90 days per request.
  • Large exports (>10k rows) are streamed.

GET /api/v1/export/audit-pack/{year}/{month}

Download the monthly audit pack zip. Paid tiers only.

Status Description
200 Content-Type: application/zip - file download
403 Free-tier org
404 Audit pack not yet generated for this period

Note

Audit packs are generated on the 3rd of each month for the prior month. Contents: receipts (PDF), calculation breakdowns (JSON), methodology version, manifest.


Webhooks (Inbound)

Inbound webhook endpoints consumed by external services.

POST /api/v1/billing/webhook

Auth: Stripe webhook signature verification (not Clerk JWT).

Header: Stripe-Signature - HMAC signature for payload verification.

invoice.payment_succeeded

Triggers billing period close flow.

  1. Verify Stripe signature
  2. Extract customer ID → look up org
  3. Find current open billing period
  4. Transition period to "closing" status
  5. Schedule ARQ job close_billing_period with _defer_by=timedelta(hours=48)

invoice.payment_failed

Marks billing period as failed.

  1. Verify Stripe signature
  2. Find current billing period
  3. Transition period to "failed" status
  4. Log failure, send user notification (future: email/webhook)

customer.subscription.deleted

Downgrades org to free tier.

  1. Verify Stripe signature
  2. Update org plan_tier to "free"
  3. Clear Stripe subscription metadata

customer.subscription.updated

Updates org plan tier to match Stripe subscription.

  1. Verify Stripe signature
  2. Map Stripe price ID → plan_tier enum
  3. Update org plan_tier

Response

Always 200 OK with {"received": true}. Internal errors are logged and the event is acknowledged to Stripe. Stripe's retry mechanism handles transient failures.

POST /api/v1/auth/webhook

Auth: Clerk webhook signature verification.

Event Action
organization.created Create corresponding Organization record with Free tier default
user.created (Phase 2) Auto-create org if first user
organization.membership.created (Phase 2) Sync membership