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": "/api/v1/airtime/purchase"
}
}context.endpoint is the full request path recorded in API access logs (including the /api/v1 prefix). Use it to correlate callbacks with log entries.
| Product line | Example context.endpoint |
|---|---|
| Airtime | /api/v1/airtime/purchase |
| Data package | /api/v1/data-packages/purchase |
| Voucher issuance | /api/v1/vouchers/issue |
Partner Events
| Event | Description |
|---|---|
transaction.completed | Airtime, data package, or voucher issuance completed successfully. |
transaction.failed | Airtime, data package, or voucher issuance 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"
}Voucher Issuance Entity
Voucher issuance callbacks include merchantId and denominationId in the entity object. telebirrRef is always null for voucher issuance.
json
{
"saleId": 98423,
"correlationId": "voucher_01HWJ8M2Q4R5T6Y7U8V9W0X1Z2",
"recipientPhone": "+251911234567",
"merchantId": 12,
"denominationId": 4,
"amountEtb": 500,
"transactionStatus": "COMPLETED",
"telebirrRef": null
}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": "/api/v1/airtime/purchase",
"statusCode": 422,
"errorCode": "PRODUCT_NOT_ENTITLED",
"message": "Partner is not entitled to purchase Airtime."
}
}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.