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.
Note
- Returns 200 if all checks pass, 503 if any check fails (for load balancer integration).
last_pollstatus 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)"
}
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_idis 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.
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:
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:
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.
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:
Note
- After Stripe checkout completes, the
invoice.payment_succeededwebhook 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.
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
}
}
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
}
Note
- The
verifiedfield 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
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.
- Verify Stripe signature
- Extract customer ID → look up org
- Find current open billing period
- Transition period to "closing" status
- Schedule ARQ job
close_billing_periodwith_defer_by=timedelta(hours=48)
invoice.payment_failed¶
Marks billing period as failed.
- Verify Stripe signature
- Find current billing period
- Transition period to "failed" status
- Log failure, send user notification (future: email/webhook)
customer.subscription.deleted¶
Downgrades org to free tier.
- Verify Stripe signature
- Update org plan_tier to "free"
- Clear Stripe subscription metadata
customer.subscription.updated¶
Updates org plan tier to match Stripe subscription.
- Verify Stripe signature
- Map Stripe price ID → plan_tier enum
- 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 |