API Reference
REST API for event ingestion, analytics, and data export.
All requests require a Bearer API key issued from app.dexcost.io.
Authorization: Bearer dx_live_...
Content-Type: application/jsonBase URL: https://api.dexcost.io (illustrative — your workspace’s actual ingest host is shown on the API Keys page in the dashboard)
Ingestion
POST /v1/ingest
Batch-ingest events and tasks in a single request. This is the primary endpoint used by SDK sync workers.
POST /v1/ingest
Authorization: Bearer dx_live_...
{
"tasks": [...],
"events": [...]
}Request body
| Field | Type | Description |
|---|---|---|
tasks | Task[] | Array of task objects (see schema below). |
events | Event[] | Array of event objects. |
Both arrays are optional — you can send tasks only, events only, or both.
Response
{
"accepted": 12,
"rejected": 0,
"errors": []
}POST /v1/events
Ingest a single event.
POST /v1/events
Authorization: Bearer dx_live_...
{
"event_id": "evt_abc123",
"task_id": "task_xyz456",
"event_type": "llm_call",
"customer_id": "acme-corp",
"project_id": "chatbot-v2",
"provider": "openai",
"model": "gpt-4o",
"input_tokens": 1200,
"output_tokens": 480,
"cost_usd": "0.0096",
"cost_confidence": "exact",
"pricing_source": "openai_api",
"is_retry": false,
"created_at": "2026-04-06T12:00:00Z"
}Event types: llm_call, external_cost, compute_cost, retry_marker
Cost confidence values: exact, computed, estimated, unknown
Response: 201 Created with the stored event object.
POST /v1/tasks
Create or upsert a task record.
POST /v1/tasks
Authorization: Bearer dx_live_...
{
"task_id": "task_xyz456",
"task_type": "resolve_ticket",
"customer_id": "acme-corp",
"project_id": "chatbot-v2",
"status": "success",
"started_at": "2026-04-06T12:00:00Z",
"ended_at": "2026-04-06T12:00:03Z"
}Status values: pending, success, failed
Response: 201 Created with the stored task object.
Reports
All report endpoints support the following common query parameters:
| Parameter | Type | Description |
|---|---|---|
from | ISO 8601 date | Start of time range (inclusive). |
to | ISO 8601 date | End of time range (inclusive). |
customer_id | string | Filter to a single customer. |
project_id | string | Filter to a single project. |
granularity | day | week | month | Time bucket size. Default: day. |
GET /v1/api/reports/cost
Total cost broken down by time bucket and dimension.
GET /v1/api/reports/cost?from=2026-04-01&to=2026-04-06&granularity=day
Authorization: Bearer dx_live_...Response
{
"data": [
{
"date": "2026-04-01",
"total_cost_usd": "12.4500",
"llm_cost_usd": "10.2000",
"external_cost_usd": "1.8000",
"compute_cost_usd": "0.4500",
"total_tokens": 1240000
}
],
"summary": {
"total_cost_usd": "74.7000",
"period_days": 6
}
}GET /v1/api/reports/waste — Cost Health
Retry cost analysis — cost spent on failed attempts that were retried.
GET /v1/api/reports/waste?from=2026-04-01&to=2026-04-06
Authorization: Bearer dx_live_...Response
{
"data": [
{
"date": "2026-04-01",
"retry_cost_usd": "0.8400",
"retry_count": 42,
"retry_cost_pct": "6.74"
}
],
"summary": {
"total_retry_cost_usd": "5.0400",
"total_retries": 252,
"avg_retry_cost_pct": "6.74"
}
}GET /v1/api/reports/profitability
Per-customer margin analysis. Requires revenue data to be configured.
GET /v1/api/reports/profitability?from=2026-04-01&to=2026-04-06
Authorization: Bearer dx_live_...Response
{
"data": [
{
"customer_id": "acme-corp",
"revenue_usd": "500.00",
"cost_usd": "42.80",
"margin_usd": "457.20",
"margin_pct": "91.44"
}
]
}Customers
GET /v1/api/customers
List all customers with aggregated cost and usage.
GET /v1/api/customers?from=2026-04-01&to=2026-04-06&limit=50&offset=0
Authorization: Bearer dx_live_...Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Maximum records to return. Max: 500. |
offset | integer | 0 | Records to skip (for pagination). |
sort | cost_usd | tokens | retry_cost_usd | cost_usd | Sort field. |
order | asc | desc | desc | Sort direction. |
Response
{
"data": [
{
"customer_id": "acme-corp",
"total_cost_usd": "42.80",
"llm_cost_usd": "38.20",
"retry_cost_usd": "2.10",
"total_tokens": 428000,
"task_count": 312,
"first_seen": "2026-03-01T00:00:00Z",
"last_seen": "2026-04-06T11:45:00Z"
}
],
"pagination": {
"total": 148,
"limit": 50,
"offset": 0,
"has_more": true
}
}Error format
All errors follow a consistent structure:
{
"error": {
"code": "invalid_api_key",
"message": "The API key provided is invalid or has been revoked.",
"status": 401
}
}Common error codes
| Code | Status | Description |
|---|---|---|
invalid_api_key | 401 | Missing or invalid Bearer token. |
insufficient_permissions | 403 | Key does not have access to this resource. |
not_found | 404 | Resource does not exist. |
validation_error | 422 | Request body failed schema validation. |
rate_limited | 429 | Too many requests. Retry after the Retry-After header. |
internal_error | 500 | Unexpected server error. |
Pagination
All list endpoints use offset pagination:
GET /v1/api/customers?limit=50&offset=50The response always includes a pagination object:
{
"pagination": {
"total": 148,
"limit": 50,
"offset": 50,
"has_more": true
}
}Iterate by incrementing offset by limit until has_more is false.