Examples
Use these Trama examples to build saga definitions, run workflows inline or from stored versions, and test retries, compensation, and payload templating. This page is the fastest way to move from understanding Trama to using it.
Best starting points
If you are new to Trama, start with creating a stored definition, then run the inline example, and finally move to the robust sample with retries, compensation, callbacks, and templates.
Create and Store a Definition
curl -X POST http://localhost:8080/sagas/definitions \
-H 'Content-Type: application/json' \
-d '{
"name": "order-saga",
"version": "v1",
"failureHandling": {
"type": "retry",
"maxAttempts": 3,
"delayMillis": 500
},
"entrypoint": "reserve-inventory",
"nodes": [
{
"kind": "task",
"id": "reserve-inventory",
"action": {
"mode": "sync",
"request": {
"url": "http://inventory/reserve",
"verb": "POST",
"body": {
"orderId": "{{payload.orderId}}"
}
}
},
"compensation": {
"url": "http://inventory/release",
"verb": "POST",
"body": {
"orderId": "{{payload.orderId}}"
}
}
}
]
}'Robust Definition Using All Main Fields
This sample covers: backoff, multi-node flow, headers, body templates, explicit successStatusCodes, node response references, and success/failure callbacks.
{
"name": "order-fulfillment",
"version": "v1",
"failureHandling": {
"type": "backoff",
"maxAttempts": 5,
"initialDelayMillis": 200,
"maxDelayMillis": 5000,
"multiplier": 2.0,
"jitterRatio": 0.2
},
"entrypoint": "reserve-inventory",
"nodes": [
{
"kind": "task",
"id": "reserve-inventory",
"action": {
"mode": "sync",
"request": {
"url": "http://inventory/api/v1/reservations",
"verb": "POST",
"headers": {
"X-Correlation-Id": "{{payload.correlationId}}",
"Content-Type": "application/json"
},
"body": {
"orderId": "{{payload.orderId}}",
"items": "{{payload.items}}"
},
"successStatusCodes": [200, 201, 202]
}
},
"compensation": {
"url": "http://inventory/api/v1/reservations/{{nodes.reserve-inventory.response.body.reservationId}}/cancel",
"verb": "POST",
"headers": {
"X-Correlation-Id": "{{payload.correlationId}}"
},
"body": {
"reason": "compensation"
},
"successStatusCodes": [200, 204]
},
"next": "charge-payment"
},
{
"kind": "task",
"id": "charge-payment",
"action": {
"mode": "sync",
"request": {
"url": "http://payments/api/v1/charges",
"verb": "POST",
"headers": {
"Authorization": "Bearer {{payload.paymentToken}}",
"X-Correlation-Id": "{{payload.correlationId}}"
},
"body": {
"orderId": "{{payload.orderId}}",
"amount": "{{payload.amount}}",
"reservationId": "{{nodes.reserve-inventory.response.body.reservationId}}"
},
"successStatusCodes": [200, 201]
}
},
"compensation": {
"url": "http://payments/api/v1/charges/{{nodes.charge-payment.response.body.chargeId}}/refund",
"verb": "POST",
"headers": {
"Authorization": "Bearer {{payload.paymentToken}}",
"X-Correlation-Id": "{{payload.correlationId}}"
},
"body": {
"orderId": "{{payload.orderId}}",
"reason": "compensation"
},
"successStatusCodes": [200, 202]
}
}
],
"onSuccessCallback": {
"url": "http://notifications/api/v1/order-success",
"verb": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": {
"orderId": "{{payload.orderId}}",
"chargeId": "{{nodes.charge-payment.response.body.chargeId}}"
},
"successStatusCodes": [200, 202, 204]
},
"onFailureCallback": {
"url": "http://notifications/api/v1/order-failed",
"verb": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": {
"orderId": "{{payload.orderId}}",
"message": "saga failed or compensated"
},
"successStatusCodes": [200, 202, 204]
}
}Run Stored Definition
curl -X POST http://localhost:8080/sagas/definitions/order-saga/v1/run \
-H 'Content-Type: application/json' \
-d '{"payload": {"orderId": "ord-123", "amount": 99.5}}'Run Inline Definition
curl -X POST http://localhost:8080/sagas/run \
-H 'Content-Type: application/json' \
-d '{
"definition": {
"name": "inline",
"version": "v1",
"failureHandling": {
"type": "retry",
"maxAttempts": 1,
"delayMillis": 200
},
"entrypoint": "notify",
"nodes": [
{
"kind": "task",
"id": "notify",
"action": {
"mode": "sync",
"request": {
"url": "http://service/run",
"verb": "POST"
}
}
}
]
},
"payload": {}
}'Template Example
{
"url": "http://service/charge?reservation={{nodes.reserve-inventory.response.body.id}}",
"verb": "POST",
"body": {
"amount": "{{payload.amount}}"
}
}Switch Branching
Run an inline saga that routes to a different payment node based on paymentMethod. The choose-payment switch node evaluates a JSON Logic expression against the input payload and dispatches to pix-payment, card-payment, or a fallback.
curl -X POST http://localhost:8080/sagas/run \
-H 'Content-Type: application/json' \
-d '{
"definition": {
"name": "checkout-v2",
"version": "v1",
"failureHandling": { "type": "retry", "maxAttempts": 2, "delayMillis": 100 },
"entrypoint": "choose-payment",
"nodes": [
{
"kind": "switch",
"id": "choose-payment",
"cases": [
{
"name": "pix",
"when": { "==": [{ "var": "input.paymentMethod" }, "pix"] },
"target": "pix-payment"
},
{
"name": "card",
"when": { "==": [{ "var": "input.paymentMethod" }, "card"] },
"target": "card-payment"
}
],
"default": "fallback-payment"
},
{
"kind": "task",
"id": "pix-payment",
"action": {
"mode": "sync",
"request": {
"url": "http://payments/pix",
"verb": "POST",
"body": { "orderId": "{{payload.orderId}}" }
}
},
"compensation": { "url": "http://payments/pix/cancel", "verb": "POST" }
},
{
"kind": "task",
"id": "card-payment",
"action": {
"mode": "sync",
"request": {
"url": "http://payments/card",
"verb": "POST",
"body": { "orderId": "{{payload.orderId}}" }
}
},
"compensation": { "url": "http://payments/card/cancel", "verb": "POST" }
},
{
"kind": "task",
"id": "fallback-payment",
"action": {
"mode": "sync",
"request": { "url": "http://payments/fallback", "verb": "POST" }
}
}
]
},
"payload": { "orderId": "ord-456", "paymentMethod": "pix" }
}'Async Callback
An async task node fires its outbound request and pauses the workflow. Trama injects {{runtime.callback.url}} and {{runtime.callback.token}} into the request body. The external service must POST to the callback URL with the token header to resume execution.
curl -X POST http://localhost:8080/sagas/run \
-H 'Content-Type: application/json' \
-d '{
"definition": {
"name": "payment-async",
"version": "v1",
"failureHandling": { "type": "retry", "maxAttempts": 2, "delayMillis": 200 },
"entrypoint": "authorize",
"nodes": [
{
"kind": "task",
"id": "authorize",
"action": {
"mode": "async",
"request": {
"url": "http://payments/authorize",
"verb": "POST",
"body": {
"orderId": "{{payload.orderId}}",
"callbackUrl": "{{runtime.callback.url}}",
"callbackToken": "{{runtime.callback.token}}"
}
},
"acceptedStatusCodes": [202],
"callback": { "timeoutMillis": 30000 }
},
"compensation": { "url": "http://payments/authorize/cancel", "verb": "POST" },
"next": "capture"
},
{
"kind": "task",
"id": "capture",
"action": {
"mode": "sync",
"request": { "url": "http://payments/capture", "verb": "POST" }
}
}
]
},
"payload": { "orderId": "ord-789", "amount": 150.00 }
}'Once the external service is ready, it resumes the workflow by calling back:
curl -X POST http://localhost:8080/sagas/<executionId>/node/authorize/callback \
-H 'Content-Type: application/json' \
-H 'X-Callback-Token: <token>' \
-d '{ "status": "authorized", "authCode": "AUTH-001" }'Sleep Pause
A sleep node pauses the workflow for a configurable duration before advancing to the next node. Use it for time-based orchestration — e.g., wait 24 hours before sending a follow-up notification, or introduce a cooling-off period between steps.
curl -X POST http://localhost:8080/sagas/run \
-H 'Content-Type: application/json' \
-d '{
"definition": {
"name": "order-with-delay",
"version": "v1",
"failureHandling": { "type": "retry", "maxAttempts": 2, "delayMillis": 200 },
"entrypoint": "confirm",
"nodes": [
{
"kind": "task",
"id": "confirm",
"action": {
"mode": "sync",
"request": { "url": "http://service/confirm", "verb": "POST" }
},
"next": "wait-24h"
},
{
"kind": "sleep",
"id": "wait-24h",
"durationMillis": 86400000,
"next": "send-reminder"
},
{
"kind": "task",
"id": "send-reminder",
"action": {
"mode": "sync",
"request": { "url": "http://service/remind", "verb": "POST" }
}
}
]
},
"payload": { "orderId": "ord-999" }
}'The execution status becomes SLEEPING while waiting. To skip the remaining wait and resume immediately:
curl -X POST http://localhost:8080/sagas/<executionId>/wake
# 202 Accepted — execution re-enqueued at the next nodeDemo Scripts
scripts/saga_demo/run_saga.py— v1 linear saga scenariosscripts/saga_demo/locust_square_chain_run.py— load testscripts/workflow_demo/run_switch_demo.py— v2 switch branching scenariosscripts/workflow_demo/run_async_demo.py— v2 async callback demoscripts/saga_demo/README.md