Skip to content

Voucher Issuance ​

Use this endpoint to issue a voucher for a recipient.

The recipient must have an active account. Issuance checks this on every request and rejects any recipient whose status is not ACTIVE. Call KYC Check first so the sender can confirm the recipient's name and you can stop before committing a request that will be refused.

Flow ​

Endpoint ​

POST /vouchers/issue

Headers ​

HeaderRequiredDescription
X-API-KeyYesPartner API key.
X-TimestampYesISO timestamp used in signature verification.
X-SignatureYesBase64 Ed25519 signature for the request.
Idempotency-KeyYesUnique retry-safe operation key.
User-AgentNoOptional client identifier.

Request Body ​

FieldTypeRequiredRules
denominationIdnumberYesMinimum 1.
merchantIdnumberYesMinimum 1.
recipientPhonestringYesMax 32 chars. Must belong to an active account; see KYC Check.
recipientNamestringNoMax 255 chars; nullable.
isRestrictedbooleanYesRestricts redemption to the originating merchant when true.
originatingCountrystringYesSender country (code or name), max 255 chars. Example: US.
foreignCurrencyAmountnumberYesSending amount in USD, must be greater than 0.
exchangeRatenumberYesUSD to ETB exchange rate, must be greater than 0.
senderNamestringYesSender name for reconciliation or recipient context, max 255 chars.

Voucher Code Format ​

Partner issuance returns two identifiers:

FieldFormatPurpose
voucherCodeExactly 16 decimal digits (0–9), no separatorsBearer credential the recipient uses for merchant redemption. Generated with cryptographically secure randomness (~53 bits of entropy).
serialNumberVCH-YYYYMMDD- plus 16 uppercase hex charactersPlatform audit/support reference. Not a redemption code.

Rules for voucherCode:

  • Always 16 characters, numeric only, stored and returned without dashes or spaces.
  • Partners may display the code to recipients grouped for readability (for example 9876-5432-1098-7654), but the API value is the plain 16-digit string.
  • Merchant redemption accepts the plain 16-digit code; hyphen separators in user input are stripped before validation.

Example Request ​

bash
curl -X POST "$API_BASE_URL/vouchers/issue" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $PARTNER_API_KEY" \
  -H "X-Timestamp: 2026-04-29T12:00:00.000Z" \
  -H "X-Signature: $PARTNER_SIGNATURE" \
  -H "Idempotency-Key: a3f0f55a-80f4-44d1-93dd-61035d4e6b72" \
  -d '{
    "denominationId": 4,
    "merchantId": 12,
    "recipientPhone": "+251911234567",
    "recipientName": "Abebe Kebede",
    "isRestricted": true,
    "originatingCountry": "US",
    "foreignCurrencyAmount": 8.85,
    "exchangeRate": 56.5,
    "senderName": "Abebe Kebede"
  }'
json
{
  "denominationId": 4,
  "merchantId": 12,
  "recipientPhone": "+251911234567",
  "recipientName": "Abebe Kebede",
  "isRestricted": true,
  "originatingCountry": "US",
  "foreignCurrencyAmount": 8.85,
  "exchangeRate": 56.5,
  "senderName": "Abebe Kebede"
}

Example Response ​

json
{
  "transactionStatus": "COMPLETED",
  "salesTransactionId": 12345,
  "correlationId": "8a2d7b6d-0c81-4b66-bdaf-46cfb9b8cce1",
  "voucherCode": "9876543210987654",
  "denominationAmountEtb": 500,
  "expirationDate": "2026-07-27T23:59:59.000Z",
  "serialNumber": "VCH-20260427-A1B2C3D4E5F67890",
  "merchantName": "Abyssinia Market",
  "isRestricted": true,
  "message": "Voucher issued successfully"
}

The response does not include a Telebirr reference. The voucher is issued by ODM after local GIFT wallet funding is confirmed.

Response Codes ​

CodeMeaning
200Voucher issuance request processed.
400Invalid request or missing idempotency key.
401Missing or invalid API key, timestamp, or signature.
403Access is forbidden.
404Referenced resource not found (for example merchant or denomination).
409Idempotency conflict or insufficient local GIFT balance for voucher issuance.
422Business-rule rejection—for example recipient account not active, account not enabled for Gift Cards, or partner limit exceeded.
500Unexpected processing error.
503A dependency was unavailable, including the recipient account check. Retry after a short delay.

Recipient rejection ​

When the recipient account is not active the response is:

json
{
  "statusCode": 422,
  "message": "recipientPhone does not belong to an active account",
  "error": "Unprocessable Entity"
}

The transaction.failed callback for the same request carries context.errorCode: "RECIPIENT_NOT_ACTIVE". No funds are moved and no sale is recorded.

Integration Notes ​

  • The recipient must be ACTIVE in KYC Check. Issuance re-checks on every request; a partner that skips the check or proceeds on INACTIVE receives the 422 above.
  • Your account must be enabled for Gift Cards before calling this endpoint. If it is not, the API returns 422 with message: "Partner is not entitled to purchase Gift Cards". Contact your ODM account contact to enable product lines.
  • Store both voucherCode and serialNumber. Use voucherCode for redemption and serialNumber for support/audit workflows.
  • Reuse the same Idempotency-Key only for retries of the same request payload.
  • Treat voucherCode as a secret bearer credential and avoid exposing it in logs.
  • ODM verifies gift funding availability before issuing a voucher. Idempotency replays return the original stored response and do not repeat processing.