Selling PINs
Sell a PIN
Sell a new PIN to a customer. The face value is debited from your balance in the product currency, and the response carries the full PIN code, which is shown only here.
HMAC-signed request with a unique Idempotency-Key header. Authentication
$afropin->reseller->pins->sell($params)afropin.reseller.pins.sell(params)afropin.reseller.pins.sell(params)afropin.reseller().pins().sell(params)Body parameters
An active product id from Products you can sell.
Face value to load onto the PIN, in the product currency. For any_amount products it must be between min and max; for set_amounts products it must be one of the denominations.
Your own reference for this sale, unique across all your issues and top-ups. A reused reference fails with duplicate_external_reference, so a retried request can never sell a second PIN.
Customer first name.
Customer last name.
Customer email. The domain must accept mail (checked by DNS).
Country of the phone number as an ISO 3166-1 alpha-2 code, for example NG.
Customer phone number, valid for phone_code. Stored in E.164 format.
Whether codes we deliver to the customer also go to phone on WhatsApp. Defaults to true when a phone is given; send false to use email only. It applies when we deliver codes (handle_delivery); the customer can turn it off from their card page. Only live PINs get WhatsApp messages.
ID of one of your webhooks in the same mode. Events for this PIN (issued, topped up, expiry) are sent to it. Change it later with Change the webhook on a PIN.
true to have us send the customer their code: now, and the new code or balance after every top-up, spend and new code for as long as the PIN lasts, by email (and WhatsApp with phone). Defaults to false: you deliver the code yourself. It works with or without a webhook_id. Change it later with Choose who delivers a PIN. How delivery works
ID of the active purchase location making the sale, from Your stores. Optional unless purchase-location enforcement is turned on for your account.
true when the customer chooses to keep one code: it then won't change after each spend or top-up, so they can share it, for example with their children. Only for products with allow_keep_code in the catalog; others fail with keep_code_not_allowed. Before sending it, show the customer this warning and get their agreement: "This card keeps the same code: it won't change after each spend or top-up. Anyone who has the code can spend the balance, at any time, until it runs out. If it's lost or shared by mistake, the money can't be recovered. You can get a new code at any time, which switches back to a new code after every spend." The response returns it as keep_code_notice. Giving the PIN a new code switches it back to a new code after each spend.
The most that can be spent from the PIN each day (midnight to midnight, Lagos time), in the product currency. Redemptions over what is left of it fail with pin_daily_limit_reached. Only for products with allow_daily_limit; others fail with daily_limit_not_allowed.
Send true to get a google_wallet_url and apple_wallet_url in the response: links your customer opens to add the PIN to Google Wallet or Apple Wallet. Same as calling Add to phone wallets after the sale.
Response fields
pin_idpin_code0000-0000-0000-0000. We store only a one-way fingerprint of it, so this is the only time you can read it. Save it securely and give it to the customer, but do not log it: a lost code can never be recovered.pin_masktransaction_refamount, balancestatusactive for a new PIN.handle_deliverykeep_code, daily_limit, keep_code_noticegoogle_wallet_url, apple_wallet_urlexpires_atduration in days. Expired PINs cannot be topped up or re-activated.pin_code is the only time you will see this code. Save it securely before you respond to your customer: we keep only a one-way fingerprint of it, so a lost code can never be shown again. If your customer loses it, reissue the PIN to give it a new code; the balance is kept.
Storing PIN codes
amount. Your purchase commission is credited to your commission balance at the same time. In live mode you get an email when a wallet drops below its low-balance warning.rate_limited (429). Wait a moment and retry with the same external_reference and a new Idempotency-Key.Request
{
"catalog_id": "2c3ec3c4-b729-4d40-9a0a-80e55d683ee6",
"amount": 5000,
"external_reference": "order-7f3a91",
"first_name": "Ada",
"last_name": "Okafor",
"email": "[email protected]",
"phone_code": "NG",
"phone": "08031234567",
"webhook_id": "3693382f-d2f0-4966-88f7-26d03c454940",
"handle_delivery": false,
"wallet_links": true
}
Response 200
{
"ok": true,
"data": {
"pin_id": "a41cabf8-f667-43d3-9c0b-d28b39e18993",
"webhook_id": "3693382f-d2f0-4966-88f7-26d03c454940",
"store_id": null,
"transaction_ref": "98d90ca6-b842-4596-af08-bce3c47c9857",
"pin_code": "3093-0622-2855-3097",
"pin_mask": "3093-****-****-3097",
"amount": 5000,
"balance": 5000,
"currency": "NGN",
"mode": "test",
"customer": {
"first_name": "Ada",
"last_name": "Okafor",
"email": "[email protected]",
"phone": "+2348031234567"
},
"status": "active",
"external_reference": "order-7f3a91",
"expires_at": "2027-10-27T09:14:22.000000Z",
"sold_at": "2026-10-27T09:14:22.000000Z",
"keep_code": false,
"daily_limit": null,
"keep_code_notice": null,
"handle_delivery": false,
"google_wallet_url": "https://pay.google.com/gp/v/save/eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3Mi...",
"apple_wallet_url": "https://afropin.com/api/wallet/apple/add/0fbdf173-db9d-4efa-837b-42e621e8af98/SFkz3ZWoF6Ph0d0cZi_JVv3fyl3UU5hkvbPrvqxwcsw"
}
}
Errors
Failed requests return a stable code with a translated message. Branch on code, never on the message.
validation_error
Some fields are missing or invalid
wrong_account_type
Your account type cannot use this endpoint
issuing_disabled
Issuing PINs is not switched on for your account. Contact support
rate_limited
Too many requests. Wait a moment and try again
duplicate_external_reference
You have sent this external_reference before
invalid_webhook_id
No webhook of yours has this webhook_id
invalid_store_id
No store of yours has this store_id
catalog_item_not_found
No item in your catalog has this catalog_id
pin_type_inactive
This PIN type is paused
amount_not_offered
This product is only sold in set amounts, and this is not one of them
amount_too_small
The amount is below this product's minimum
amount_too_large
The amount is above this product's maximum
keep_code_not_allowed
This product does not let customers keep the same code (allow_keep_code in the catalog)
daily_limit_not_allowed
This product is spent in one go, so a PIN of it cannot have a daily limit
currency_not_supported
Your account has no wallet in this currency
wallet_balance_low
Your wallet balance does not cover this amount
pin_generation_failed
A new PIN code could not be created. Try again
internal_error
Something went wrong on our side. Try again; if it keeps happening, contact support
{
"ok": false,
"error": {
"code": "catalog_item_not_found",
"message": "No item in your catalog has this catalog_id",
"fields": null
}
}
All error codes →