Appearance
Callbacks
Callbacks notify partner systems when asynchronous transaction outcomes are available. Delivery depends on the active webhook configuration and subscribed event types.
Delivery
ODM sends callbacks as HTTPS POST requests to the configured callback URL.
| Header | Description |
|---|---|
Content-Type | Always application/json. |
X-Signature | Hex-encoded HMAC-SHA256 signature. |
X-Timestamp | ISO timestamp used in signature generation. |
X-Correlation-Id | Present when the originating operation has a correlation id. |
Signature Verification
Compute the signature using your webhook signing secret:
text
HMAC_SHA256_HEX(JSON.stringify(payload) + X-Timestamp)Compare your computed hex digest with the X-Signature header.
Payload Envelope
json
{
"version": "v1",
"eventType": "transaction.completed",
"occurredAt": "2026-04-27T08:03:24.000Z",
"entity": {
"saleId": 98421,
"correlationId": "airtime_01HWJ7S8E4Y9G7E4F6N5Q2P3Z8",
"recipientPhone": "+251911234567",
"amountEtb": 100,
"transactionStatus": "COMPLETED",
"telebirrRef": "TB-240427-000123"
},
"context": {
"apiAccessId": 101,
"endpoint": "/airtime/purchase"
}
}Partner Events
| Event | Description |
|---|---|
transaction.completed | Airtime or data package transaction completed successfully. |
transaction.failed | Airtime or data package transaction failed after processing. |
Data Package Entity
Data package callbacks include offerId in the entity object.
json
{
"saleId": 98422,
"correlationId": "data_01HWJ7VHHZ37VZB6E7N2C9F4Q1",
"recipientPhone": "+251911234567",
"offerId": "DP-ETHIO-1GB-7D",
"amountEtb": 100,
"transactionStatus": "COMPLETED",
"telebirrRef": "TB-240427-000124"
}Failure Entity
Failed callbacks use the same envelope and include failure details in context.
json
{
"version": "v1",
"eventType": "transaction.failed",
"occurredAt": "2026-04-27T08:04:10.000Z",
"entity": {
"saleId": null,
"correlationId": "airtime_01HWJ8K1Y3H2N9M7X4B6R5C2Q0",
"recipientPhone": "+251911234567",
"amountEtb": 100,
"transactionStatus": "FAILED",
"telebirrRef": null
},
"context": {
"apiAccessId": 101,
"endpoint": "/airtime/purchase",
"statusCode": 422,
"errorCode": "PROCESSING_FAILED",
"message": "Transaction could not be completed."
}
}Retries
ODM treats 2xx responses as delivered.
Retry behavior:
- Maximum attempts:
3total deliveries per event - Retry on transient failure:
- HTTP
408,429, and5xx - transport/network failures (for example timeout or connection reset)
- HTTP
- Exponential backoff: ~
2s, then4s
After the final failed attempt, the event expires and is not retried.
Integration Notes
- Return a
2xxresponse only after your system has persisted or safely queued the event. - Use
eventType+correlationIdfor idempotent processing on your side. - For
transaction.failed, inspectcontext.statusCode,context.errorCode, andcontext.messagefor troubleshooting.