Skip to main content

Observability API

Sagaweaw exposes a complete REST API for monitoring sagas, querying metrics, and managing dead letters. All endpoints require token authentication.


Security

By default, all endpoints return 403 if the token is not configured.

Configuration

sagaweaw:
observability:
enabled: true
token: ${SAGAWEAW_TOKEN} # required
previous-token: ${SAGAWEAW_PREVIOUS_TOKEN} # optional — zero-downtime rotation
auth:
max-attempts: 10 # failed logins before IP lockout (0 = disabled)
lockout-minutes: 15 # lockout duration

To completely disable the API:

sagaweaw:
observability:
enabled: false

Authentication

Pass the token in either of these two formats:

# Bearer token (standard)
Authorization: Bearer <token>

# Alternative header
X-Sagaweaw-Token: <token>

Rate limiting

After max-attempts consecutive failures from the same IP, all endpoints return 429 Too Many Requests with a Retry-After header indicating when the lockout expires. Expired lockouts are cleaned up lazily on the next request.

Token rotation

Set previous-token alongside token to accept both credentials in parallel. Any request authenticated with the previous token is logged as a WARN. Once all clients have switched to the new token, remove previous-token.


Saga Endpoints

GET /api/sagas

Lists sagas with pagination and optional filters.

Query params:

ParameterTypeDefaultDescription
pageint0Page (0-indexed)
sizeint50Items per page
statusstringFilter by status (FAILED, COMPLETED, etc.)
namestringSearch by saga name (contains, case-insensitive)
idstringSearch by saga ID prefix
idempotencyKeystringExact search by idempotency key
fromISO 8601Start date (2024-01-01T00:00:00Z)
toISO 8601End date

Examples:

# Last 50 sagas
curl -H "Authorization: Bearer $TOKEN" /api/sagas

# Sagas that failed today
curl -H "Authorization: Bearer $TOKEN" \
"/api/sagas?status=FAILED&from=2024-01-15T00:00:00Z"

# Search by name (contains)
curl -H "Authorization: Bearer $TOKEN" \
"/api/sagas?name=pix"

# Search by ID prefix
curl -H "Authorization: Bearer $TOKEN" \
"/api/sagas?id=01934abc"

# Search by idempotency key
curl -H "Authorization: Bearer $TOKEN" \
"/api/sagas?idempotencyKey=order-abc-123"

# Pagination
curl -H "Authorization: Bearer $TOKEN" \
"/api/sagas?page=2&size=25"

Response — array of SagaInstance:

[
{
"id": "01934...",
"name": "pix-payment",
"status": { "type": "COMPLETED", "completedAt": "2024-01-15T10:05:00Z", "durationMs": 312 },
"contextJson": "{\"transactionId\":\"...\"}",
"steps": [
{
"name": "validate-dict",
"order": 0,
"status": { "type": "COMPLETED" },
"attempt": 1,
"maxAttempts": 3,
"durationMs": 45,
"executedAt": "2024-01-15T10:04:59Z",
"completedAt": "2024-01-15T10:05:00Z"
}
],
"createdAt": "2024-01-15T10:04:58Z",
"updatedAt": "2024-01-15T10:05:00Z",
"version": 4
}
]

GET /api/sagas/{id}

Returns a specific saga by ID.

curl -H "Authorization: Bearer $TOKEN" /api/sagas/01934abc-...
  • 200 — saga found
  • 404 — saga does not exist

GET /api/sagas/{id}/events

Returns the immutable audit log of the saga — all state transitions in chronological order.

curl -H "Authorization: Bearer $TOKEN" /api/sagas/01934abc-.../events

Response:

[
{
"id": "...",
"sagaId": "01934abc-...",
"stepName": null,
"eventType": "SAGA_STARTED",
"payload": null,
"createdAt": "2024-01-15T10:04:58Z"
},
{
"id": "...",
"sagaId": "01934abc-...",
"stepName": "validate-dict",
"eventType": "STEP_COMPLETED",
"payload": "{\"durationMs\":45}",
"createdAt": "2024-01-15T10:05:00Z"
}
]
  • 200 — event list (may be empty)
  • 404 — saga does not exist

POST /api/sagas/{id}/reprocess

Re-executes a saga from the step that failed.

curl -X POST -H "Authorization: Bearer $TOKEN" /api/sagas/01934abc-.../reprocess
  • 202 — reprocessing started
  • 404 — saga does not exist

Metrics Endpoints

GET /api/sagas/metrics

Returns aggregated metrics for all sagas.

curl -H "Authorization: Bearer $TOKEN" /api/sagas/metrics

Response:

{
"total": 1520,
"started": 3,
"executing": 12,
"completed": 1480,
"compensated": 18,
"failed": 7,
"deadLetters": 2,
"successRate": 98.7,
"byName": [
{
"name": "pix-payment",
"total": 900,
"completed": 885,
"failed": 5
}
]
}
FieldDescription
successRatecompleted / (completed + failed + compensated) * 100, rounded to 1 decimal
deadLettersPending dead letters (not reprocessed)
byNameBreakdown by saga name, ordered by volume

GET /api/sagas/stats/by-name

Returns statistics by saga type, including average duration.

curl -H "Authorization: Bearer $TOKEN" /api/sagas/stats/by-name

Response:

[
{
"name": "pix-payment",
"total": 900,
"completed": 885,
"failed": 5,
"avgDurationMs": 312
},
{
"name": "order-processing",
"total": 620,
"completed": 595,
"failed": 2,
"avgDurationMs": 1840
}
]
FieldDescription
avgDurationMsAverage duration in milliseconds (only COMPLETED sagas)

GET /api/sagas/steps/stats

Returns step statistics grouped by saga name. Used by the dashboard to identify bottlenecks.

curl -H "Authorization: Bearer $TOKEN" /api/sagas/steps/stats

Response:

[
{
"stepName": "transmit-to-bacen",
"sagaName": "pix-payment",
"avgDurationMs": 245,
"total": 900,
"failed": 12
},
{
"stepName": "validate-dict",
"sagaName": "pix-payment",
"avgDurationMs": 18,
"total": 900,
"failed": 0
}
]

Ordered by avgDurationMs descending. avgDurationMs may be null if no step has finished yet.

Time proportion

The dashboard uses this endpoint to calculate the time proportion of each step relative to the saga total. A step consuming >40% of the saga's total time appears in red — regardless of the absolute value in ms.


CSV Export Endpoints

GET /api/dead-letters/export?format=csv
GET /api/sagas/export?status=FAILED&from=2025-01-01

Returns text/csv. No dependencies on iText/PDFBox — generated with plain Java.


Dead Letter Endpoints

Dead letters are steps that exhausted all retry attempts and need manual intervention. The API exposes only pending ones — reprocessed items leave the queue and their history remains in the saga's event log.

GET /api/dead-letters

Lists pending dead letters. Optionally filter by saga.

# all pending
curl -H "Authorization: Bearer $TOKEN" /api/dead-letters

# only for a specific saga
curl -H "Authorization: Bearer $TOKEN" "/api/dead-letters?sagaId=01934abc-..."
ParameterDescription
sagaIdFilter by saga ID (optional)
includeReprocessedInclude already-reprocessed items — default false (ignored when sagaId is set)

Response:

[
{
"id": "...",
"sagaId": "01934abc-...",
"sagaName": "pix-payment",
"stepName": "transmit-to-bacen",
"errorMessage": "Connection timeout after 3 attempts",
"errorTrace": "java.net.SocketTimeoutException: ...",
"contextSnapshot": "{\"transactionId\":\"...\"}",
"createdAt": "2024-01-15T10:10:00Z"
}
]
FieldDescription
errorMessageSummary message from the last exception
errorTraceFull stack trace
contextSnapshotJSON of the saga context at the time of failure

GET /api/dead-letters/{id}

Returns a specific dead letter by ID.

curl -H "Authorization: Bearer $TOKEN" /api/dead-letters/abc-...
  • 200 — dead letter found
  • 404 — does not exist

POST /api/dead-letters/{id}/reprocess

Marks the dead letter as reprocessed and restarts the corresponding step in the saga.

curl -X POST -H "Authorization: Bearer $TOKEN" /api/dead-letters/abc-.../reprocess
  • 202 — reprocessing started
  • 404 — dead letter does not exist
What happens after reprocessing

The dead letter disappears from the pending queue. The step is re-executed with the configured retry policy. The result (success or new failure) appears in the saga event log at /api/sagas/{id}/events.


Next Steps