API reference
Event pipeline
Operator view of the outbox relay, consumers, dead-letter queue and projection replays.
GET /v1/admin/events/dead-letters
List dead-lettered events
Events a consumer gave up on after its maximum attempts, with the last error. Requires admin:read.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
consumer | query | string | |
include_replayed | query | boolean |
Responses
- 200 Dead letters.
Request
curl -X GET "$KULPAY_URL/v1/admin/events/dead-letters" \ -H "Authorization: Bearer $KULPAY_KEY"
200 Response application/json
{
"data": [
{
"attempts": 1,
"consumer": "test.flaky",
"created_at": "2026-10-10T22:15:09.502740134Z",
"error": "downstream unavailable",
"event_id": "3KWPmSNckmxcSVVyjYjwiyknc9S",
"event_type": "transaction.stage.notify.skipped",
"id": "3KWPmXrzwUTODrmptisEQ6dOEIE",
"tenant_id": "3KWPmY5oeJFG8Ct0XNDvwiGGOhG"
}
]
}
POST /v1/admin/events/dead-letters/{id}/replay
Replay a dead-lettered event
Runs the event through its consumer again, once, recorded under the caller. A second failure dead-letters it again. Requires admin:write.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string |
Responses
- 200 Replayed.
- 404 No such dead letter (
not_found). - 409 Already replayed (
invalid_state). - 502 The consumer failed again (
replay_failed).internal_error
Request
curl -X POST "$KULPAY_URL/v1/admin/events/dead-letters/{id}/replay" \
-H "Authorization: Bearer $KULPAY_KEY"
200 Response application/json
{
"replayed": true
}
404 Error · not_found application/problem+json
{
"code": "not_found",
"detail": "No such dead letter (not_found).",
"error": {
"code": "not_found",
"message": "No such dead letter (not_found)."
},
"status": 404,
"title": "Not Found",
"type": "about:blank"
}
409 Error · invalid_state application/problem+json
{
"code": "invalid_state",
"detail": "Already replayed (invalid_state).",
"error": {
"code": "invalid_state",
"message": "Already replayed (invalid_state)."
},
"status": 409,
"title": "Conflict",
"type": "about:blank"
}
502 Error · internal_error application/problem+json
{
"code": "internal_error",
"detail": "The consumer failed again (replay_failed).",
"error": {
"code": "internal_error",
"message": "The consumer failed again (replay_failed)."
},
"status": 502,
"title": "Bad Gateway",
"type": "about:blank"
}
GET /v1/admin/events/pipeline
Outbox, consumers and dead-letter summary
Requires admin:read.
Responses
- 200 Pipeline status.
Request
curl -X GET "$KULPAY_URL/v1/admin/events/pipeline" \ -H "Authorization: Bearer $KULPAY_KEY"
200 Response application/json
{
"bus": "db",
"consumers": [
{
"consumer": "projection.event_stats",
"cursor_at": "2026-10-10T22:22:46.913204Z",
"cursor_event_id": "3KWQht6ShKw2GvsxLg91zBsjsyX",
"started_at": "2026-10-08T10:38:02.854371Z"
}
],
"metrics": {
"dead_lettered": 0,
"duplicates": 0,
"lag_alert": false,
"oldest_unpublished_age": 0,
"processed": 147,
"publish_failures": 0,
"publish_lag_p99": 596568000,
"published": 147,
"retries": 0,
"stale_gaps": 0,
"unpublished": 0
},
"oldest_unpublished_age_seconds": 0,
"open_dead_letters": 0,
"unpublished": 0
}
GET /v1/admin/events/replays
List projection replays
Requires admin:read.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
projection | query | string |
Responses
- 200 Replays, newest first.
Request
curl -X GET "$KULPAY_URL/v1/admin/events/replays" \ -H "Authorization: Bearer $KULPAY_KEY"
200 Response application/json
{
"data": []
}
POST /v1/admin/events/replays
Rebuild a projection from the event store
Rebuilds whole closed UTC days into a shadow table and swaps it in only if its row count and checksum match an independent count of the event store. Never sends webhooks or calls providers. Requires admin:write.
Request body application/json required
| Field | Type | Description |
|---|---|---|
from required | string (date) | |
projection | string | |
to required | string (date) | Inclusive; must be before today (UTC). |
Responses
- 201 The completed replay.
string - 400 Bad range, today included, or unknown projection (
invalid_request). - 409 The rebuild did not match the event store; nothing was swapped (
invalid_state).
Request
curl -X POST "$KULPAY_URL/v1/admin/events/replays" \
-H "Authorization: Bearer $KULPAY_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "2026-10-01",
"projection": "event_stats",
"to": "2026-10-07"
}'
201 Response · string application/json
{
"checksum": "string",
"completed_at": "2026-10-10T09:30:00Z",
"error": "string",
"events_read": 1,
"from": "2026-10-10T09:30:00Z",
"id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
"projection": "event_stats",
"rows_written": 1,
"started_at": "2026-10-10T09:30:00Z",
"started_by": "string",
"status": "running",
"to": "2026-10-10T09:30:00Z"
}
400 Error · invalid_request application/problem+json
{
"code": "invalid_request",
"detail": "eventbus: a replay can only cover closed days (up to yesterday, UTC)",
"error": {
"code": "invalid_request",
"message": "eventbus: a replay can only cover closed days (up to yesterday, UTC)"
},
"status": 400,
"title": "Invalid replay",
"type": "about:blank"
}
409 Error · invalid_state application/problem+json
{
"code": "invalid_state",
"detail": "The rebuild did not match the event store; nothing was swapped (invalid_state).",
"error": {
"code": "invalid_state",
"message": "The rebuild did not match the event store; nothing was swapped (invalid_state)."
},
"status": 409,
"title": "Conflict",
"type": "about:blank"
}
GET /v1/admin/jobs
Singleton jobs and their leaders
Settlement sync, retention, the audit anchor and the audit verifier run on one node per region. This lists each job, the node leading it and this node's run statistics; every run is also recorded as job.run.started and job.run.completed or job.run.failed. Requires admin:read.
Responses
- 200 The jobs.
- 503 No scheduler on this node (
jobs_unavailable).
Request
curl -X GET "$KULPAY_URL/v1/admin/jobs" \ -H "Authorization: Bearer $KULPAY_KEY"
200 Response application/json
{
"data": [
{
"every": "30s",
"failures_on_this_node": 0,
"job": "settlement_sync",
"last_run_at": "2026-10-10T22:15:13.567193217Z",
"last_success_at": "2026-10-10T22:15:13.567210404Z",
"leader_alive": true,
"leader_node": "node-a",
"lease_until": "2026-10-10T22:15:33.548606907Z",
"runs_on_this_node": 1,
"this_node_leads": true
}
],
"node": "node-a",
"region": "mz"
}
503 Error · jobs_unavailable application/problem+json
{
"code": "jobs_unavailable",
"detail": "No scheduler on this node (jobs_unavailable).",
"error": {
"code": "jobs_unavailable",
"message": "No scheduler on this node (jobs_unavailable)."
},
"status": 503,
"title": "Service Unavailable",
"type": "about:blank"
}
POST /v1/admin/jobs/{name}/run
Run a job now
Makes the job due at once: the node leading it runs it on its next tick, with its lease and run record as usual (QA T-501). Requires admin:write.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
name required | path | string |
Responses
- 202 Scheduled.
- 404 No job of that name.
not_found
Request
curl -X POST "$KULPAY_URL/v1/admin/jobs/{name}/run" \
-H "Authorization: Bearer $KULPAY_KEY"
202 Response application/json
{
"job": "string",
"scheduled": true
}
404 Error · not_found application/problem+json
{
"code": "not_found",
"detail": "no job named nightly (GET /v1/admin/jobs lists them)",
"error": {
"code": "not_found",
"message": "no job named nightly (GET /v1/admin/jobs lists them)"
},
"status": 404,
"title": "Not Found",
"type": "about:blank"
}
POST /v1/admin/test/clock/advance
Move a test deployment's time forward
In a deployment with test_apis on (never in live mode), moves the service's time forward by duration (a Go duration: 90m, 26h), so windows and deadlines pass without backdating rows (QA T-501). Elsewhere 404. Requires admin:write.
Request body application/json required
| Field | Type | Description |
|---|---|---|
duration required | string |
Responses
- 200 The new time.
- 404 test_apis is off.
not_found
Request
curl -X POST "$KULPAY_URL/v1/admin/test/clock/advance" \
-H "Authorization: Bearer $KULPAY_KEY" \
-H "Content-Type: application/json" \
-d '{
"duration": "26h"
}'
200 Response application/json
{
"now": "2026-10-10T09:30:00Z"
}
404 Error · not_found application/problem+json
{
"code": "not_found",
"detail": "payments: this deployment's clock cannot be moved (test_apis is off)",
"error": {
"code": "not_found",
"message": "payments: this deployment's clock cannot be moved (test_apis is off)"
},
"status": 404,
"title": "Not Found",
"type": "about:blank"
}
KulPay