Appearance
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
| Header | Required | Description |
|---|---|---|
X-API-Key | Yes | Partner API key. |
X-Timestamp | Yes | ISO timestamp used in signature verification. |
X-Signature | Yes | Base64 Ed25519 signature for the request. |
Idempotency-Key | Yes | Unique retry-safe operation key. |
User-Agent | No | Optional client identifier. |
Request Body
| Field | Type | Required | Rules |
|---|---|---|---|
denominationId | number | Yes | Minimum 1. |
merchantId | number | Yes | Minimum 1. |
recipientPhone | string | Yes | Max 32 chars. Must belong to an active account; see KYC Check. |
recipientName | string | No | Max 255 chars; nullable. |
isRestricted | boolean | Yes | Restricts redemption to the originating merchant when true. |
originatingCountry | string | Yes | Sender country (code or name), max 255 chars. Example: US. |
foreignCurrencyAmount | number | Yes | Sending amount in USD, must be greater than 0. |
exchangeRate | number | Yes | USD to ETB exchange rate, must be greater than 0. |
senderName | string | Yes | Sender name for reconciliation or recipient context, max 255 chars. |
Voucher Code Format
Partner issuance returns two identifiers:
| Field | Format | Purpose |
|---|---|---|
voucherCode | Exactly 16 decimal digits (0–9), no separators | Bearer credential the recipient uses for merchant redemption. Generated with cryptographically secure randomness (~53 bits of entropy). |
serialNumber | VCH-YYYYMMDD- plus 16 uppercase hex characters | Platform 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
| Code | Meaning |
|---|---|
200 | Voucher issuance request processed. |
400 | Invalid request or missing idempotency key. |
401 | Missing or invalid API key, timestamp, or signature. |
403 | Access is forbidden. |
404 | Referenced resource not found (for example merchant or denomination). |
409 | Idempotency conflict or insufficient local GIFT balance for voucher issuance. |
422 | Business-rule rejection—for example recipient account not active, account not enabled for Gift Cards, or partner limit exceeded. |
500 | Unexpected processing error. |
503 | A 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
ACTIVEin KYC Check. Issuance re-checks on every request; a partner that skips the check or proceeds onINACTIVEreceives the422above. - Your account must be enabled for Gift Cards before calling this endpoint. If it is not, the API returns
422withmessage: "Partner is not entitled to purchase Gift Cards". Contact your ODM account contact to enable product lines. - Store both
voucherCodeandserialNumber. UsevoucherCodefor redemption andserialNumberfor support/audit workflows. - Reuse the same
Idempotency-Keyonly for retries of the same request payload. - Treat
voucherCodeas 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.