Transactions
Name enquiry
Section titled “Name enquiry”Request
POST https://api-dev.spay.ng/api/v1/transactions/enquiryAuthorization: Bearer <access-token>Accept: application/jsonContent-Type: application/json{ "sourceAccountNumber": "8880000001", "destinationBankCode": "000013", "destinationAccountNumber": "0123456789"}curl -X POST 'https://api-dev.spay.ng/api/v1/transactions/enquiry' \ -H "Authorization: Bearer $SPAY_ACCESS_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "sourceAccountNumber": "8880000001", "destinationBankCode": "000013", "destinationAccountNumber": "0123456789"}'Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| sourceAccountNumber | string, 10 digits | Yes | Your account the transfer would be sent from. Not one of yours returns 404, code 25. |
| destinationBankCode | string, 3 to 6 digits | Yes | The destination institution’s institutionCode. |
| destinationAccountNumber | string, 10 digits | Yes | The account to look up. Not found returns 404, code 25. |
Response: 200 OK
{ "sourceAccountNumber": "8880000001", "destinationBankCode": "000013", "destinationAccountNumber": "0123456789", "destinationAccountName": "Example Beneficiary", "destinationBankName": "Example Bank"}Response fields
| Field | Type | Description |
|---|---|---|
| sourceAccountNumber | string, 10 digits | As sent. |
| destinationBankCode | string | As sent. |
| destinationAccountNumber | string, 10 digits | As sent. |
| destinationAccountName | string | The destination account’s name. |
| destinationBankName | string or null | The destination institution’s name. |
Errors
| HTTP status | Body |
|---|---|
| 400 | {"error":{"code":"30","message":"Invalid request"}} |
| 400 | {"error":{"code":"06","message":"Request refused"}} |
| 401 | {"error":{"code":"63","message":"Missing, invalid or expired access token"}} |
| 403 | {"error":{"code":"63","message":"Operation not permitted"}} |
| 404 | {"error":{"code":"25","message":"Resource not found"}} |
| 503 | {"error":{"code":"91","message":"Service unavailable"}} |
Transfer
Section titled “Transfer”Request
POST https://api-dev.spay.ng/api/v1/transactionsAuthorization: Bearer <access-token>Accept: application/jsonContent-Type: application/json{ "sourceAccountNumber": "8880000001", "destinationBankCode": "000013", "destinationAccountNumber": "0123456789", "amount": 1000.0, "narration": "POS payment", "reference": "pay-pos-0001"}curl -X POST 'https://api-dev.spay.ng/api/v1/transactions' \ -H "Authorization: Bearer $SPAY_ACCESS_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "sourceAccountNumber": "8880000001", "destinationBankCode": "000013", "destinationAccountNumber": "0123456789", "amount": 1000.0, "narration": "POS payment", "reference": "pay-pos-0001"}'Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| sourceAccountNumber | string, 10 digits | Yes | Your account to debit. |
| destinationBankCode | string, 3 to 6 digits | Yes | The destination institution’s institutionCode. |
| destinationAccountNumber | string, 10 digits, not the source account | Yes | The account to pay. |
| amount | number, naira, from 0.01, at most 2 decimal places | Yes | The amount to send. More than 2 decimal places returns 400, code 30. |
| narration | string, 1 to 100 characters | Yes | The transfer’s narration. |
| reference | string, 1 to 100 characters, not starting with REVERSED: | Yes | Your unique reference for this transfer. Reuse it on every retry and look the transfer up by it. |
Response: 202 Accepted
{ "reference": "pay-pos-0001", "originalReference": null, "ownerAccountNumber": "8880000001", "type": "INTERBANK_TRANSFER", "mode": "DEBIT", "status": "PENDING", "reviewRequired": false, "amount": 1000.0, "feeAmount": 1.25, "currency": "NGN", "source": { "accountNumber": "8880000001", "accountName": "Adunni Foods" }, "beneficiary": { "accountNumber": "0123456789", "accountName": "Example Beneficiary", "institutionCode": "000013", "institutionName": "Example Bank" }, "narration": "POS payment", "failureReason": null, "transactionDate": "2026-10-25T09:15:00+01:00", "updatedAt": "2026-10-25T09:15:00+01:00"}Response: 200 OK with the same body when the transfer is final within the call, with status SUCCESSFUL or FAILED.
Response fields
| Field | Type | Description |
|---|---|---|
| reference | string | Your reference. |
| originalReference | null | Always null for a transfer. |
| ownerAccountNumber | string, 10 digits | The source account. |
| type | enum: INTRABANK_TRANSFER, INTERBANK_TRANSFER | INTRABANK_TRANSFER when the destination is a Spay account, otherwise INTERBANK_TRANSFER. |
| mode | string, DEBIT | Money leaves the source account. |
| status | enum: PENDING, SUCCESSFUL, FAILED | The transfer’s status. |
| reviewRequired | boolean | true while Spay establishes the outcome. |
| amount | number, naira, at most 2 decimal places | The amount sent. |
| feeAmount | number, naira, at most 2 decimal places | The fee charged on the transfer, net of any refund. 0 when no fee was charged. |
| currency | string, NGN | The currency. |
| source.accountNumber | string, 10 digits | The source account. |
| source.accountName | string or null | The source account’s name. |
| beneficiary.accountNumber | string, 10 digits | The destination account. |
| beneficiary.accountName | string or null | The destination account’s name. |
| beneficiary.institutionCode | string, omitted when unknown | The destination institution’s code. |
| beneficiary.institutionName | string, omitted when unknown | The destination institution’s name. |
| narration | string | Your narration. |
| failureReason | enum: INSUFFICIENT_FUNDS, SOURCE_ACCOUNT_UNAVAILABLE, BENEFICIARY_UNAVAILABLE, CANCELLED, PROCESSING_ERROR, or null | Why a FAILED transfer failed; null otherwise. |
| transactionDate | timestamp | When the transfer was made. |
| updatedAt | timestamp | When the transfer last changed. |
feeAmountis charged in addition toamount.- Over the single or daily transfer limit returns 400, code
61. Transfers between a customer’s own accounts at Spay don’t count against limits. - A refusal made before the transfer is recorded leaves the
referencefree to reuse: 400 with code30,61,25(beneficiary not found at enquiry) or06, and 404 (source account). - 409, code
94: thereferencealready belongs to a transfer. Read Get transaction status byreferenceand follow itsstatus. - Once the transfer is recorded, a refusal answers 400 with code
51,57,07or96, and the transfer is recorded asFAILED. Thereferenceis used, and Get transaction status returns the transfer. - 503 means Spay couldn’t complete the call. The transfer may still have been recorded as
PENDING. - When the call gets no response or a 503, read Get transaction status by
reference. A 404 means the transfer wasn’t recorded: send it again with the samereference. Otherwise follow itsstatus. Never retry with a newreference. - When
reviewRequiredistrue, the outcome is being established by Spay andstatusstaysPENDING. Wait for the webhook, and don’t retry. - References are unique across all your API clients, per resource (an opening reference and a transfer reference don’t collide), and they are kept forever.
Errors
| HTTP status | Body |
|---|---|
| 400 | {"error":{"code":"30","message":"Invalid request"}} |
| 400 | {"error":{"code":"61","message":"Limit exceeded"}} |
| 400 | {"error":{"code":"25","message":"Resource not found"}} |
| 400 | {"error":{"code":"06","message":"Request refused"}} |
| 400 | {"error":{"code":"51","message":"Insufficient funds"}} |
| 400 | {"error":{"code":"57","message":"Source account unavailable"}} |
| 400 | {"error":{"code":"07","message":"Beneficiary account unavailable"}} |
| 400 | {"error":{"code":"96","message":"Processing error"}} |
| 401 | {"error":{"code":"63","message":"Missing, invalid or expired access token"}} |
| 403 | {"error":{"code":"63","message":"Operation not permitted"}} |
| 404 | {"error":{"code":"25","message":"Resource not found"}} |
| 409 | {"error":{"code":"94","message":"Reference has already been used; query the existing operation"}} |
| 503 | {"error":{"code":"91","message":"Service unavailable"}} |
Get transaction status
Section titled “Get transaction status”Request
GET https://api-dev.spay.ng/api/v1/transactions/pay-pos-0001Authorization: Bearer <access-token>Accept: application/jsoncurl 'https://api-dev.spay.ng/api/v1/transactions/pay-pos-0001' \ -H "Authorization: Bearer $SPAY_ACCESS_TOKEN" \ -H "Accept: application/json"No request body.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| reference (path) | string | Yes | The transaction’s reference: yours for a transfer you made. |
Response: 200 OK
{ "reference": "pay-pos-0001", "originalReference": null, "ownerAccountNumber": "8880000001", "type": "INTERBANK_TRANSFER", "mode": "DEBIT", "status": "SUCCESSFUL", "reviewRequired": false, "amount": 1000.0, "feeAmount": 1.25, "currency": "NGN", "source": { "accountNumber": "8880000001", "accountName": "Adunni Foods" }, "beneficiary": { "accountNumber": "0123456789", "accountName": "Example Beneficiary", "institutionCode": "000013", "institutionName": "Example Bank" }, "narration": "POS payment", "failureReason": null, "transactionDate": "2026-10-25T09:15:00+01:00", "updatedAt": "2026-10-25T09:15:30+01:00"}Response fields
| Field | Type | Description |
|---|---|---|
| reference | string | The transaction’s reference. |
| originalReference | string or null | Set on a reversal: the reference of the transaction it reverses. |
| ownerAccountNumber | string, 10 digits | The account the transaction belongs to. |
| type | enum: INTERBANK_TRANSFER, INTRABANK_TRANSFER, BILL_PAYMENT_DEBIT, BILL_PAYMENT_REVERSAL | What kind of transaction it is. |
| mode | enum: DEBIT, CREDIT | Whether money left or reached the account. |
| status | enum: PENDING, SUCCESSFUL, FAILED, REVERSED | The transaction’s status. |
| reviewRequired | boolean | true while Spay establishes the outcome of a PENDING transaction. |
| amount | number, naira, at most 2 decimal places | The amount. |
| feeAmount | number, naira, at most 2 decimal places | The fee charged on the transaction, net of any refund. 0 when no fee was charged. |
| currency | string, NGN | The currency. |
| source.accountNumber | string or null | The account the money came from. |
| source.accountName | string or null | That account’s name. |
| source.institutionCode | string, omitted for the owner account | The sending institution’s code. |
| source.institutionName | string, omitted for the owner account | The sending institution’s name. |
| beneficiary.accountNumber | string or null | The account the money went to. |
| beneficiary.accountName | string or null | That account’s name. |
| beneficiary.institutionCode | string, omitted for the owner account | The receiving institution’s code. |
| beneficiary.institutionName | string, omitted for the owner account | The receiving institution’s name. |
| narration | string, may be empty | The transaction’s narration. |
| failureReason | enum: INSUFFICIENT_FUNDS, SOURCE_ACCOUNT_UNAVAILABLE, BENEFICIARY_UNAVAILABLE, CANCELLED, PROCESSING_ERROR, or null | Why a FAILED transaction failed; null otherwise. |
| transactionDate | timestamp | When the transaction happened. |
| updatedAt | timestamp | When the transaction last changed. |
Errors
| HTTP status | Body |
|---|---|
| 401 | {"error":{"code":"63","message":"Missing, invalid or expired access token"}} |
| 403 | {"error":{"code":"63","message":"Operation not permitted"}} |
| 404 | {"error":{"code":"25","message":"Resource not found"}} |
| 503 | {"error":{"code":"91","message":"Service unavailable"}} |