Skip to content

Merchant API Redemption

Use this flow for server-to-server voucher redemption:

  1. Validate voucher
  2. Create claim and trigger OTP delivery
  3. Verify OTP to complete settlement

Required Headers

All merchant redemption endpoints require signed requests.

HeaderRequiredDescription
X-API-KeyYesMerchant API credential.
X-TimestampYesISO timestamp used in signature verification; allowed skew is 5 minutes.
X-SignatureYesBase64 Ed25519 signature of the canonical request string.
Idempotency-KeyYesRequired for all merchant redemption POST endpoints.
X-Correlation-IdNoOptional correlation value for tracing.
User-AgentNoOptional client identifier.

Flow

Validate Voucher

POST /merchant-api/vouchers/validate

Request Body

FieldTypeRequiredRules
voucherCodestringYesIssued voucher code. API accepts up to 32 chars, strips -, then validates the normalized 16-digit code.

Voucher Code Rules

  • Use the 16-digit voucher code from issuance, for example 9876543210987654.
  • Hyphens in input are ignored before validation.
  • Do not use the issuance serialNumber (VCH-...) as a redemption code.

Example Request

bash
curl -X POST "$API_BASE_URL/merchant-api/vouchers/validate" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $MERCHANT_API_KEY" \
  -H "X-Timestamp: 2026-04-29T12:00:00.000Z" \
  -H "X-Signature: $MERCHANT_SIGNATURE" \
  -H "Idempotency-Key: 5b1ee3c0-4b28-4f68-9be8-b86ec2b7d4a1" \
  -H "X-Correlation-Id: red_01HWJ9S8E4Y9G7E4F6N5Q2P3Z8" \
  -d '{
    "voucherCode": "9876543210987654"
  }'

Example Response

json
{
  "id": 123,
  "code": "9876543210987654",
  "ownerNumber": "+251911234567",
  "ownerName": "Abebe Kebede",
  "originalAmount": "500.00",
  "remainingBalance": "350.00",
  "totalRedeemed": "150.00",
  "status": "PARTIALLY_REDEEMED",
  "isRestricted": true,
  "originatingMerchantId": 12,
  "originatingMerchantName": "Abyssinia Market",
  "expirationDate": "2026-07-27T23:59:59.000Z"
}

Initiate Claim

POST /merchant-api/claims

Request Body

FieldTypeRequiredRules
voucherIdnumberYesInteger, minimum 1.
amountstringYesPositive decimal amount to redeem. JSON numbers are accepted and normalized to string.

Example Request

bash
curl -X POST "$API_BASE_URL/merchant-api/claims" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $MERCHANT_API_KEY" \
  -H "X-Timestamp: 2026-04-29T12:00:00.000Z" \
  -H "X-Signature: $MERCHANT_SIGNATURE" \
  -H "Idempotency-Key: 7afc7c5f-4470-4a74-8a8f-f53ac9f2b81a" \
  -H "X-Correlation-Id: red_01HWJ9S8E4Y9G7E4F6N5Q2P3Z8" \
  -d '{
    "voucherId": 123,
    "amount": "50.00"
  }'

Example Response

json
{
  "claimId": 7721,
  "otpExpiresAt": "2026-04-27T08:05:00.000Z",
  "maskedPhone": "+2519*****67"
}

Verification Step

Use Claim Verification to submit OTP and finalize the claim.

Response Codes

CodeMeaning
200Voucher validation succeeded.
201Claim created and OTP issued.
400Invalid payload or missing/invalid required header (including Idempotency-Key).
401Missing or invalid auth/signature headers.
403Access forbidden (for example IP allowlist policy).
404Voucher or claim not found.
409Idempotency conflict or request with same key still in progress.
429Retry limit or cooldown reached for voucher attempts.
500Unexpected server error.

Redemption Checks

FieldMeaning
remainingBalanceMaximum redeemable amount for the voucher.
statusVoucher must be redeemable (ACTIVE or PARTIALLY_REDEEMED).
isRestrictedWhen true, redemption is limited to the originating merchant.
expirationDateVoucher expiration timestamp when configured.