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": "/airtime/purchase"
  }
}

Partner Events

EventDescription
transaction.completedAirtime or data package transaction completed successfully.
transaction.failedAirtime 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: 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.