Skip to content

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.

HeaderDescription
Content-TypeAlways application/json.
X-SignatureHex-encoded HMAC-SHA256 signature.
X-TimestampISO timestamp used in signature generation.
X-Correlation-IdPresent 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 lineExample context.endpoint
Airtime/api/v1/airtime/purchase
Data package/api/v1/data-packages/purchase
Voucher issuance/api/v1/vouchers/issue

Partner Events ​

EventDescription
transaction.completedAirtime, data package, or voucher issuance completed successfully.
transaction.failedAirtime, 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: 3 total deliveries per event
  • Retry on transient failure:
    • HTTP 408, 429, and 5xx
    • transport/network failures (for example timeout or connection reset)
  • Exponential backoff: ~2s, then 4s

After the final failed attempt, the event expires and is not retried.

Integration Notes ​

  • Return a 2xx response only after your system has persisted or safely queued the event.
  • Use eventType + correlationId for idempotent processing on your side.
  • For transaction.failed, inspect context.statusCode, context.errorCode, and context.message for troubleshooting.