API Reference
Complete reference for the Nexus Broker REST API. All endpoints accept and return JSON. The base URL is https://nexus-broker.dev.
Authentication
The API uses two authentication methods depending on the endpoint:
Firebase ID Token
Used for user-scoped management operations (creating API keys, viewing stats). Pass the token in the Authorization header:
Authorization: Bearer <firebase-id-token>
Obtain the ID token from the Firebase Auth client SDK after sign-in. Tokens expire after one hour and must be refreshed.
API Key
Used for webhook management and queue operations. Pass the API key in the x-api-key header:
x-api-key: nxb_your_api_key
API keys are prefixed with nxb_. They are stored hashed (SHA-256) on the server. The full key is only visible at creation time.
API Keys
Create API Key
POST /api/keys — Generate a new API key. Requires Firebase authentication.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable name (max 64 chars) |
curl -X POST https://nexus-broker.dev/api/keys \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name": "production"}'Response (201)
{
"id": "key_2kf9x7",
"name": "production",
"key": "nxb_Kj3m9xRq7wYpNhBvT5cF...Dg2e",
"createdAt": "2025-01-15T10:30:00Z"
}List API Keys
GET /api/keys — List all API keys for the authenticated user. Requires Firebase authentication. Returns keys with last 4 characters of the key for identification.
curl https://nexus-broker.dev/api/keys \ -H "Authorization: Bearer <token>"
{
"keys": [
{
"id": "key_2kf9x7",
"name": "production",
"prefix": "nxb_...Dg2e",
"createdAt": "2025-01-15T10:30:00Z",
"lastUsedAt": "2025-01-20T14:22:00Z"
}
]
}Delete API Key
DELETE /api/keys/{id} — Permanently delete an API key. Requires Firebase authentication.
curl -X DELETE https://nexus-broker.dev/api/keys/key_2kf9x7 \ -H "Authorization: Bearer <token>"
Response (204 No Content)
Rotate API Key
POST /api/keys/{id}/rotate — Generate a new key value for an existing key. The old key is immediately invalidated. Requires Firebase authentication.
curl -X POST https://nexus-broker.dev/api/keys/key_2kf9x7/rotate \ -H "Authorization: Bearer <token>"
{
"id": "key_2kf9x7",
"name": "production",
"key": "nxb_Mw8pRt4xBnKq...Yj6c",
"createdAt": "2025-01-15T10:30:00Z",
"rotatedAt": "2025-02-01T09:00:00Z"
}Webhooks
Create Webhook
POST /api/webhooks — Create a new webhook endpoint. Requires API key authentication.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable name (max 128 chars) |
| publicKey | string | Yes | PEM-encoded public key (RSA-2048 or X25519) |
| algorithm | string | No | "RSA-2048" (default) or "X25519" |
| lockTimeout | number | No | Lock timeout in seconds (default: 300) |
| maxRetries | number | No | Max retry attempts (default: per plan) |
| ttl | number | No | Message TTL in seconds (default: per plan) |
curl -X POST https://nexus-broker.dev/api/webhooks \
-H "x-api-key: nxb_Kj3m9x...Rq7w" \
-H "Content-Type: application/json" \
-d '{
"name": "stripe-webhooks",
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBg...",
"algorithm": "RSA-2048",
"lockTimeout": 600,
"maxRetries": 5
}'{
"id": "wh_a1b2c3",
"name": "stripe-webhooks",
"slug": "f8k2m9x4p7",
"url": "https://nexus-broker.dev/wh/f8k2m9x4p7",
"algorithm": "RSA-2048",
"lockTimeout": 600,
"maxRetries": 5,
"createdAt": "2025-01-15T11:00:00Z"
}List Webhooks
GET /api/webhooks — List all webhooks for the authenticated user. Requires API key authentication.
curl https://nexus-broker.dev/api/webhooks \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
{
"webhooks": [
{
"id": "wh_a1b2c3",
"name": "stripe-webhooks",
"slug": "f8k2m9x4p7",
"url": "https://nexus-broker.dev/wh/f8k2m9x4p7",
"algorithm": "RSA-2048",
"messageCount": 142,
"queueDepth": 3,
"deadLetterCount": 1,
"createdAt": "2025-01-15T11:00:00Z"
}
]
}Get Webhook
GET /api/webhooks/{id} — Get details for a specific webhook. Requires API key authentication.
curl https://nexus-broker.dev/api/webhooks/wh_a1b2c3 \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
Update Webhook
PATCH /api/webhooks/{id} — Update webhook configuration. Only included fields are updated. Requires API key authentication.
Request Body
| Field | Type | Description |
|---|---|---|
| name | string | New name |
| publicKey | string | New PEM public key (triggers key rotation) |
| lockTimeout | number | New lock timeout in seconds |
| maxRetries | number | New max retry count |
curl -X PATCH https://nexus-broker.dev/api/webhooks/wh_a1b2c3 \
-H "x-api-key: nxb_Kj3m9x...Rq7w" \
-H "Content-Type: application/json" \
-d '{"name": "stripe-webhooks-v2", "lockTimeout": 600}'Delete Webhook
DELETE /api/webhooks/{id} — Permanently delete a webhook and all its queued messages. This action is irreversible. Requires API key authentication.
curl -X DELETE https://nexus-broker.dev/api/webhooks/wh_a1b2c3 \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
Response (204 No Content)
Ingestion
Send Webhook
POST /wh/{slug} — Public ingestion endpoint. No authentication required. Accepts any valid JSON body. The payload is encrypted and queued.
curl -X POST https://nexus-broker.dev/wh/f8k2m9x4p7 \
-H "Content-Type: application/json" \
-d '{
"event": "payment.completed",
"data": {
"id": "evt_123",
"amount": 4999
}
}'{
"id": "msg_x9y8z7",
"sequence": 42,
"status": "queued",
"createdAt": "2025-01-15T12:00:00Z"
}Status Codes
| Code | Meaning |
|---|---|
| 202 | Accepted and queued |
| 400 | Invalid JSON or missing content-type |
| 404 | Webhook slug not found |
| 413 | Payload exceeds plan size limit |
| 429 | Rate limit exceeded |
| 507 | Queue depth limit reached |
Queue Operations
Dequeue Message
POST /api/webhooks/{id}/dequeue — Fetch and lock the next available message in FIFO order. The message is locked until the lock timeout expires or you commit/error it. Requires API key authentication.
curl -X POST https://nexus-broker.dev/api/webhooks/wh_a1b2c3/dequeue \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
{
"message": {
"id": "msg_x9y8z7",
"sequence": 42,
"status": "locked",
"lockExpiresAt": "2025-01-15T12:05:00Z",
"createdAt": "2025-01-15T12:00:00Z"
},
"encrypted": {
"ciphertext": "Base64...",
"iv": "Base64...",
"tag": "Base64...",
"encryptedKey": "Base64...",
"algorithm": "RSA-2048-AES-256-GCM"
},
"headers": {
"content-type": "application/json"
}
}Status Codes
| Code | Meaning |
|---|---|
| 200 | Message dequeued and locked |
| 204 | No messages available |
| 401 | Invalid or missing API key |
| 404 | Webhook not found |
Commit Message
POST /api/webhooks/{id}/messages/{msgId}/commit — Acknowledge successful processing. Removes the message from the active queue. Requires API key authentication.
curl -X POST https://nexus-broker.dev/api/webhooks/wh_a1b2c3/messages/msg_x9y8z7/commit \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
Response (204 No Content)
Error Message
POST /api/webhooks/{id}/messages/{msgId}/error — Report a processing failure. The message is released for retry. If the retry count exceeds the maximum, the message moves to the dead letter queue. Requires API key authentication.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| reason | string | No | Description of the failure (max 1024 chars) |
curl -X POST https://nexus-broker.dev/api/webhooks/wh_a1b2c3/messages/msg_x9y8z7/error \
-H "x-api-key: nxb_Kj3m9x...Rq7w" \
-H "Content-Type: application/json" \
-d '{"reason": "Database connection timeout"}'{
"id": "msg_x9y8z7",
"status": "queued",
"retryCount": 2,
"maxRetries": 10
}Messages
List Messages
GET /api/webhooks/{id}/messages — List messages for a webhook with optional filtering. Returns message metadata (not encrypted payloads). Requires API key authentication.
| Param | Type | Description |
|---|---|---|
| status | string | Filter by status: queued, locked, committed, error, dead |
| limit | number | Max results (default: 50, max: 200) |
| cursor | string | Pagination cursor from previous response |
curl "https://nexus-broker.dev/api/webhooks/wh_a1b2c3/messages?status=queued&limit=20" \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
{
"messages": [
{
"id": "msg_x9y8z7",
"sequence": 42,
"status": "queued",
"retryCount": 0,
"createdAt": "2025-01-15T12:00:00Z"
}
],
"nextCursor": "eyJzZXF1ZW5jZSI6NDJ9"
}Dead Letters
List Dead Letters
GET /api/webhooks/{id}/errors — List messages in the dead letter queue. Supports the same query parameters as the messages endpoint. Requires API key authentication.
curl https://nexus-broker.dev/api/webhooks/wh_a1b2c3/errors \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
{
"messages": [
{
"id": "msg_p3q4r5",
"sequence": 38,
"status": "dead",
"retryCount": 10,
"maxRetries": 10,
"lastError": "Connection refused",
"createdAt": "2025-01-14T08:00:00Z",
"diedAt": "2025-01-14T09:30:00Z"
}
]
}Retry Dead Letter
POST /api/webhooks/{id}/errors/{msgId}/retry — Move a dead letter message back to the queue for reprocessing. Resets the retry counter. Requires API key authentication.
curl -X POST https://nexus-broker.dev/api/webhooks/wh_a1b2c3/errors/msg_p3q4r5/retry \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
{
"id": "msg_p3q4r5",
"status": "queued",
"retryCount": 0,
"message": "Message requeued for processing"
}Discard Dead Letter
DELETE /api/webhooks/{id}/errors/{msgId} — Permanently delete a dead letter message. This action is irreversible. Requires API key authentication.
curl -X DELETE https://nexus-broker.dev/api/webhooks/wh_a1b2c3/errors/msg_p3q4r5 \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
Response (204 No Content)
Error Responses
All errors follow a consistent JSON format:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Retry after 60 seconds.",
"details": {
"retryAfter": 60,
"limit": 100,
"window": "60s"
}
}
}| HTTP Status | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body failed validation |
| 401 | UNAUTHORIZED | Missing or invalid authentication |
| 403 | FORBIDDEN | Authenticated but not authorized for this resource |
| 404 | NOT_FOUND | Resource does not exist |
| 409 | CONFLICT | Message is not in the expected state |
| 413 | PAYLOAD_TOO_LARGE | Payload exceeds plan size limit |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests |
| 500 | INTERNAL_ERROR | Unexpected server error |
| 507 | QUEUE_FULL | Queue depth limit reached |