API Reference Overview
NullSpend exposes a REST API for cost tracking, budget management, and human-in-the-loop workflows. All endpoints live under `https://nullspend.dev/api/`.
NullSpend exposes a REST API for cost tracking, budget management, and human-in-the-loop workflows. All endpoints live under https://nullspend.dev/api/.
Authentication
NullSpend uses two authentication modes depending on the endpoint. For key lifecycle and caching details, see Authentication.
API Key (agent/SDK use)
Pass your API key in the X-NullSpend-Key header:
X-NullSpend-Key: ns_live_sk_...API keys are created in the dashboard and identified by a ns_key_ prefix. The raw key is shown only once at creation. See API Keys API for management endpoints.
Session (dashboard use)
Session-authenticated endpoints require a browser session cookie from the NullSpend dashboard. These endpoints power the dashboard UI and are not callable from external scripts without a valid session.
Endpoint Auth Summary
| Endpoint | Method | Auth |
|---|---|---|
POST /api/cost-events | Ingest event | API key |
POST /api/cost-events/batch | Batch ingest | API key |
GET /api/cost-events | List events | Session |
GET /api/cost-events/:id | Get event | Session |
GET /api/cost-events/sessions/:sessionId | Get session | Session |
GET /api/cost-events/summary | Analytics | Session |
GET /api/budgets/status | Budget status | API key |
GET /api/auth/introspect | Key introspection | API key |
POST /api/actions | Create action | API key |
POST /api/actions/:id/result | Mark result | API key |
GET /api/actions/:id | Get action | API key or Session |
GET /api/actions/:id/costs | Action costs | API key or Session |
GET /api/budgets | List budgets | Session |
POST /api/budgets | Create/update budget | Session |
DELETE /api/budgets/:id | Delete budget | Session |
POST /api/budgets/:id | Reset budget spend | Session |
GET /api/budgets/velocity-status | Velocity state | Session |
GET /api/keys | List keys | Session |
POST /api/keys | Create key | Session |
DELETE /api/keys/:id | Revoke key | Session |
POST /api/actions/:id/approve | Approve action | Session |
POST /api/actions/:id/reject | Reject action | Session |
GET /api/actions | List actions | Session |
GET /api/webhooks | List webhooks | Session |
POST /api/webhooks | Create webhook | Session |
PATCH /api/webhooks/:id | Update webhook | Session |
DELETE /api/webhooks/:id | Delete webhook | Session |
POST /api/webhooks/:id/test | Test webhook | Session |
POST /api/webhooks/:id/rotate-secret | Rotate secret | Session |
GET /api/webhooks/:id/deliveries | Delivery history | Session |
GET /api/orgs | List user's orgs | Session |
POST /api/orgs | Create team org | Session |
GET /api/orgs/:orgId | Get org details | Session (member+) |
PATCH /api/orgs/:orgId | Update org | Session (admin+) |
DELETE /api/orgs/:orgId | Delete org | Session (owner) |
GET /api/orgs/:orgId/members | List members | Session (member+) |
PATCH /api/orgs/:orgId/members/:userId | Change role | Session (admin+) |
DELETE /api/orgs/:orgId/members/:userId | Remove member | Session (admin+) |
GET /api/orgs/:orgId/invitations | List invitations | Session (admin+) |
POST /api/orgs/:orgId/invitations | Invite member | Session (admin+) |
DELETE /api/orgs/:orgId/invitations/:id | Revoke invitation | Session (admin+) |
POST /api/invite/accept | Accept invitation | Session |
GET /api/cost-events/attribution | Cost attribution | Session (viewer) |
GET /api/cost-events/attribution/:key | Attribution detail | Session (viewer) |
GET /api/cost-events/tag-keys | Distinct tag keys | Session (viewer) |
GET /api/cost-events/export | CSV export | Session (viewer) |
GET /api/cost-events/:id/bodies | Request/response bodies | Session (viewer) |
GET /api/audit-log | Audit log | Session (admin) |
GET /api/tool-costs | List tool costs | API key or Session (viewer) |
POST /api/tool-costs | Create/update tool cost | Session (admin) |
DELETE /api/tool-costs/:id | Reset tool cost | Session (admin) |
POST /api/tool-costs/discover | Discover tools | API key |
PATCH /api/keys/:id | Update key | Session (admin) |
POST /api/orgs/:orgId/leave | Leave org | Session (member) |
POST /api/orgs/:orgId/transfer | Transfer ownership | Session (owner) |
GET /api/auth/session | Current session | Session |
POST /api/auth/switch-org | Switch active org | Session |
GET /api/slack/config | Get Slack config | Session (admin) |
POST /api/slack/config | Set Slack config | Session (admin) |
POST /api/slack/test | Test Slack webhook | Session (admin) |
POST /api/stripe/checkout | Create checkout | Session (owner) |
POST /api/stripe/portal | Billing portal | Session (owner) |
GET /api/stripe/subscription | Get subscription | Session (viewer) |
POST /api/stripe/subscription/sync | Sync subscription | Session |
GET /api/health | Health check | Public |
ID Formats
All NullSpend IDs use the ns_ prefix followed by a type identifier and a UUID:
| Prefix | Resource |
|---|---|
ns_act_ | Action |
ns_key_ | API key |
ns_evt_ | Cost event |
ns_bgt_ | Budget |
ns_wh_ | Webhook endpoint |
ns_del_ | Webhook delivery |
ns_usr_ | User |
ns_tc_ | Tool cost |
ns_org_ | Organization |
Error Format
All errors follow a consistent shape:
{
"error": {
"code": "machine_readable_code",
"message": "Human-readable description.",
"details": null
}
}Validation errors include field-level issues in details:
{
"error": {
"code": "validation_error",
"message": "Request validation failed.",
"details": {
"issues": [
{ "path": ["fieldName"], "message": "Expected number, received string" }
]
}
}
}See Error Reference for the full error catalog.
Pagination
List endpoints use cursor-based pagination. The response includes a cursor field — pass it as the cursor query parameter to fetch the next page.
{
"data": [...],
"cursor": { "createdAt": "2026-03-15T10:00:00.000Z", "id": "ns_evt_..." }
}limitcontrols page size (default varies by endpoint, max 100)cursoris a JSON-encoded object containingcreatedAtandid- When
cursorisnull, there are no more results - Pagination is forward-only, sorted by
createdAt DESC
Rate Limiting
For enforcement order and failure modes, see Rate Limits.
API-key-authenticated endpoints return rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1711036800000When the limit is exceeded, the endpoint returns 429 Too Many Requests with a Retry-After header (in seconds).
Idempotency
POST endpoints that accept API key auth support idempotent requests. Pass an Idempotency-Key header or include idempotencyKey in the request body. Duplicate requests return 200 with the original result instead of creating a new resource.
Versioning
For the version resolution chain and strategy, see Versioning.
Responses include a NullSpend-Version header indicating the API version:
NullSpend-Version: 2026-04-01