Payment & Withdrawal Management
Overview
This skill enables AI agents and applications to initiate and manage withdrawals and beneficiaries using Eclipse APIs.
It supports:
- Initiating payments and withdrawals — e.g. ATM withdrawals, EFT withdrawals
- Retrieving withdrawal history
- Displaying withdrawal details, e.g. tokens (OTP), expiry, and amount
- Creating, listing, updating, and deleting payment beneficiaries
All responses shown below use anonymised sample data.
1. Initiate ATM Withdrawal
Create a withdrawal request and generate a token (OTP) for collection.
Notes
externalUniqueIdmust be unique per transaction- Duplicate requests with the same
externalUniqueIdwill be rejected
Request
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenant_id}/wallets/{wallet_id}/withdrawalsExample Request
{
"amount": 100,
"description": "ATM withdrawal",
"type": "ZA_PAYCORP_ATM",
"externalUniqueId": "uuid-redacted",
"deliverToPhone": "27XXXXXXXXX"
}Example Response
{
"withdrawalId": 50001,
"externalUniqueId": "uuid-redacted",
"status": "PENDING",
"token": "123456",
"expires": "2026-03-30T10:00:00.000Z",
"amount": 100.00,
"currency": "ZAR",
"gateway": "ZA_PAYCORP_ATM",
"type": "ATM",
"subType": "NORMAL",
"walletId": 20001,
"fee": 0.00,
"created": "2026-03-24T10:00:00.000Z",
"description": "ATM withdrawal",
"deliverToPhone": "27XXXXXXXXX",
"lastModified": "2026-03-24T10:00:00.000Z",
"tracingContext": "trace-redacted"
}API Reference: POST /wallets/{walletId}/withdrawals
2. Get Withdrawals
Retrieve withdrawals for a wallet within a date range.
Request
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenant_id}/wallets/{wallet_id}/withdrawals?dateFromIncl={ISO_DATE}&dateToExcl={ISO_DATE}&limit={limit}&offset={offset}Notes
- Dates must be in ISO 8601 format:
2026-03-01T00:00:00.000Z
Example Response
[
{
"withdrawalId": 50001,
"externalUniqueId": "uuid-redacted",
"status": "PENDING",
"token": "123456",
"expires": "2026-03-30T10:00:00.000Z",
"amount": 100.00,
"currency": "ZAR",
"gateway": "ZA_PAYCORP_ATM",
"type": "ATM",
"subType": "NORMAL",
"walletId": 20001,
"fee": 0.00,
"created": "2026-03-24T10:00:00.000Z",
"extraInfo": "{}",
"description": "ATM withdrawal",
"deliverToPhone": "27XXXXXXXXX",
"lastModified": "2026-03-24T10:00:00.000Z",
"tracingContext": "trace-redacted"
},
{
"withdrawalId": 50002,
"externalUniqueId": "uuid-redacted-2",
"status": "TIMEOUT",
"errorDescription": "Expired",
"token": "654321",
"expires": "2026-03-20T10:00:00.000Z",
"amount": 100.00,
"currency": "ZAR",
"gateway": "ZA_PAYCORP_ATM",
"type": "ATM",
"subType": "NORMAL",
"walletId": 20001,
"fee": 0.00,
"created": "2026-03-18T10:00:00.000Z",
"extraInfo": "{}",
"description": "ATM withdrawal",
"deliverToPhone": "27XXXXXXXXX",
"lastModified": "2026-03-20T10:00:00.000Z",
"tracingContext": "trace-redacted"
}
]3. EFT Withdrawal
Trigger: "process withdrawal", "send money to bank", "EFT payment", "withdraw funds to bank account"
Required inputs: tenantId, walletId, bank account details (or existing beneficiaryId), amount, currency
EFT withdrawals send funds from an Eclipse wallet to an external bank account via a payment beneficiary.
Step 1 — Check Wallet Balance
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenant_id}/wallets/{wallet_id}Verify availableBalance is sufficient before proceeding.
Step 2 — Create Beneficiary (if not already on file)
Optionally, save the destination bank account as a beneficiary for future reference. Retrieve existing beneficiaries via GET {baseUrl}/rest/v1/beneficiaries?walletId={walletId}.
POST {baseUrl}/rest/v1/beneficiaries{
"walletId": 1092847,
"accountHolderName": "Sipho Dlamini",
"bankName": "STANDARD_BANK",
"branchCode": "051001",
"bankAccountNumber": "123456789",
"accountType": "CURRENT",
"reference": "Salary Payout",
"provider": "RTC"
}Response:
{
"beneficiaryId": 4829104,
"walletId": 1092847,
"accountHolderName": "Sipho Dlamini",
"bankName": "STANDARD_BANK",
"bankAccountNumber": "123456789",
"branchCode": "051001",
"accountType": "CURRENT",
"provider": "RTC",
"status": "ACTIVE",
"created": "2026-05-19T10:00:00.000Z"
}Step 3 — Create Withdrawal
Bank account details are passed directly in the withdrawal request — there is no beneficiaryId field on the withdrawal.
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals{
"amount": 1200.00,
"type": "ZA_SB_EFT",
"externalUniqueId": "eft-9f8e7d6c-5b4a-3210-fedc-ba9876543210",
"accountNumber": "123456789",
"branchCode": "051001",
"bank": "STANDARD_BANK",
"accountName": "Sipho Dlamini",
"reference": "Salary Payout",
"description": "EFT payout"
}Response:
{
"withdrawalId": 9384716,
"walletId": 1092847,
"amount": 1200.00,
"currency": "ZAR",
"status": "PENDING",
"created": "2026-05-19T10:05:00.000Z"
}Step 4 — Check Withdrawal Status
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals/{withdrawalId}Response:
{
"withdrawalId": 9384716,
"status": "SUCCESSFUL",
"lastModified": "2026-05-19T10:07:30.000Z"
}Notes
- Possible statuses:
BUILDING,PENDING,SUCCESSFUL,ERROR,TIMEOUT,CANCELLED,REVERSED,HOLD. providervalues:RTC(near-instant),DTB(same-day),EFT(1–2 business days). Confirm which providers are enabled for the tenant.- Never retry with the same
externalUniqueId. If uncertain, query withdrawal status first.
API Reference: GET /wallets/{walletId} · POST /beneficiaries · POST /withdrawals
4. Beneficiary Management
Trigger: "add beneficiary", "create payment destination", "add bank account", "manage beneficiaries", "list saved accounts"
Required inputs: full bank account details for creation
Beneficiaries are saved bank accounts or mobile money destinations used when initiating EFT withdrawals and payments.
4.1 Create Beneficiary
POST {baseUrl}/rest/v1/beneficiaries
Authorization: Bearer {jwt}
Content-Type: application/json{
"walletId": 1092847,
"accountHolderName": "Nomsa Khumalo",
"bankName": "ABSA",
"branchCode": "632005",
"bankAccountNumber": "987654321",
"accountType": "SAVINGS",
"reference": "Loan repayment",
"provider": "RTC"
}Response:
{
"beneficiaryId": 4829104,
"walletId": 1092847,
"accountHolderName": "Nomsa Khumalo",
"bankName": "ABSA",
"bankAccountNumber": "987654321",
"branchCode": "632005",
"accountType": "SAVINGS",
"reference": "Loan repayment",
"provider": "RTC",
"status": "ACTIVE",
"created": "2026-05-19T10:00:00.000Z"
}4.2 List Beneficiaries
GET {baseUrl}/rest/v1/beneficiaries
?walletId={walletId}
&limit=20
&offset=0
Authorization: Bearer {jwt}Additional filter query params: customerId, organisationId, bankAccountNumber, mobileNumber.
4.3 Get a Beneficiary
GET {baseUrl}/rest/v1/beneficiaries/{beneficiaryId}
Authorization: Bearer {jwt}4.4 Update Beneficiary
PUT {baseUrl}/rest/v1/beneficiaries/{beneficiaryId}
Authorization: Bearer {jwt}
Content-Type: application/json{ "beneficiaryId": 4829104, "reference": "Updated reference — June 2026" }4.5 Delete Beneficiary
DELETE {baseUrl}/rest/v1/beneficiaries/{beneficiaryId}
Authorization: Bearer {jwt}Returns 204 No Content on success.
Notes
bankNameis a free-text string — common values:ABSA,STANDARD_BANK,FNB,NEDBANK,CAPITEC.accountTypevalues:CURRENT,SAVINGS,TRANSMISSION.providervalues:RTC(near-instant),DTB(same-day),EFT(1–2 business days). Availability depends on tenant configuration.statusvalues:ACTIVE,BLOCKED,PENDING,REJECTED.onlyCheck: truevalidates the bank account via AVS without creating a beneficiary record.saveOnPass: truecreates the beneficiary only if AVS validation passes.- Before creating a beneficiary, check whether one already exists:
GET /rest/v1/beneficiaries?walletId={walletId}and match bybankAccountNumberto avoid duplicates.
API Reference: POST /beneficiaries · GET /beneficiaries · PUT /beneficiaries/{id} · DELETE /beneficiaries/{id}
5. Pick n Pay / Boxer Cash In (Top-up)
Trigger: "cash in at PnP", "top up at Pick n Pay", "deposit at Boxer", "PnP cash top-up", "retailer cash deposit"
Required inputs: tenantId, walletId, amount (whole ZAR only), externalUniqueId
The customer receives a token from Eclipse, presents it along with cash to any Pick n Pay or Boxer teller, and the wallet is credited when the teller confirms the deposit.
Prerequisites:
- Tenant config
pnp.configmust be enabled for the tenant (contact EFT Corporation to configure) - Currency must be
ZAR - Amount must be a whole number — cents are not accepted by default
- Minimum deposit: R50. Default daily limit: R3,000. Default monthly limit: R24,000
Step 1 — Request a cash-in token
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/topups
Authorization: Bearer {jwt}
Content-Type: application/json
{
"amount": 200,
"externalUniqueId": "unique-uuid-per-request",
"type": "ZA_PNP_CASH"
}Response (200 OK):
{
"topupId": 74012,
"completionToken": "152050197",
"status": "PENDING"
}Display the completionToken to the customer — this is the reference number they present at the teller. The token expires at the end of the day after it was generated.
Step 2 — Customer deposits at the teller
Guide the customer through the Pick n Pay / Boxer teller steps:
- Select Value Added Services from the menu
- Select Mobile
- Select Topup
- Select EFT Corporation
- Provide the amount and the token/reference number
Step 3 — Confirm top-up status
Eclipse credits the wallet as soon as the teller confirms the deposit. Poll the top-up until status is no longer PENDING:
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/topups/{topupId}
Authorization: Bearer {jwt}Status values:
| Status | Meaning |
|---|---|
PENDING | Token issued; customer has not yet deposited |
SUCCESSFUL | Deposit confirmed at teller; wallet credited |
EXPIRED | Token expired before the customer deposited |
CANCELLED | Token cancelled |
NoteIf fraud is detected when the deposit is confirmed, a 24-hour reservation is applied — the funds are visible but unavailable until the reservation lifts.
Sandbox simulation: Use amount: 101 to trigger an automatic SUCCESSFUL callback after 10 seconds. Use amount: 102 to simulate an EXPIRED callback after 10 seconds.
6. Pick n Pay / Boxer Cash Out (Withdrawal)
Trigger: "cash out at PnP", "withdraw at Pick n Pay", "PnP cash withdrawal", "withdraw at Boxer", "retailer cash withdrawal"
Required inputs: tenantId, walletId, amount (whole ZAR only), externalUniqueId
The customer receives a withdrawal token from Eclipse, presents it to any Pick n Pay or Boxer teller, and receives cash. The wallet is debited when the token is issued.
Prerequisites:
- Tenant config
pnp.configmust be enabled for the tenant - Currency must be
ZAR - Amount must be a whole number — cents are not accepted by default
- Minimum withdrawal: R50. Maximum per transaction: R3,000
Step 1 — Check wallet balance
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}
Authorization: Bearer {jwt}Verify availableBalance is sufficient before proceeding.
Step 2 — Request a cash-out token
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals
Authorization: Bearer {jwt}
Content-Type: application/json
{
"amount": 200,
"type": "ZA_PNP_CASH",
"externalUniqueId": "unique-uuid-per-request",
"description": "PnP cash withdrawal"
}Response (200 OK):
{
"withdrawalId": 2761462,
"externalUniqueId": "unique-uuid-per-request",
"status": "PENDING",
"token": "144745417",
"expires": "2026-06-19T21:59:59.000Z",
"amount": 200,
"currency": "ZAR",
"gateway": "ZA_PNP",
"type": "CASH",
"subType": "NORMAL",
"walletId": 1371283,
"fee": 10,
"created": "2026-06-18T10:00:00.000Z",
"description": "PnP cash withdrawal"
}Display the token to the customer. The wallet is debited immediately when the token is issued.
By default, tokens expire at the end of the day after generation. You can set an earlier expires in the request, but it cannot exceed the default end-of-next-day limit.
Step 3 — Customer collects cash at the teller
Guide the customer through the Pick n Pay / Boxer teller steps:
- Select Value Added Services from the menu
- Select Mobile
- Select Withdrawals
- Select EFT Corporation
- Provide the reference number/token
Step 4 — Check withdrawal status
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals/{withdrawalId}
Authorization: Bearer {jwt}| Status | Meaning |
|---|---|
PENDING | Token issued; customer has not yet collected |
SUCCESSFUL | Cash collected at teller |
TIMEOUT | Token expired before the customer collected; wallet is refunded |
CANCELLED | Token cancelled; wallet is refunded |
Notes
externalUniqueIdmust be unique per request — never reuse. Query status before retrying if uncertain.- Only whole ZAR amounts are accepted. Submitting a decimal amount returns a
400error. ZA_PNP_TENDERfollows the same flow asZA_PNP_CASHbut the token settles a purchase at a Pick n Pay till instead of dispensing cash.- Tokens for both cash-in and cash-out are redeemable at all Pick n Pay and Boxer stores in South Africa.
Common Patterns for AI Agents
1. Initiate Withdrawal Flow
- Capture:
- Amount
- Phone number
- Generate unique externalUniqueId
- Initiate withdrawal
- Display:
- Token (OTP)
- Amount
- Expiry time
2. Display Active Withdrawal Token
- Fetch withdrawals
- Filter:
- status = PENDING
- Display:
- token
- amount
- expiry
3. Handle Expired Withdrawals
- Status = TIMEOUT indicates expired token
- Prompt user to initiate a new withdrawal
4. PnP / Boxer Cash In Flow
- POST to
/topupswithtype: ZA_PNP_CASH→ receivecompletionToken - Display token to customer with teller instructions
- Poll
GET .../topups/{topupId}until status isSUCCESSFULorEXPIRED - On
EXPIRED: inform customer the token expired; offer to generate a new one
5. PnP / Boxer Cash Out Flow
- Verify
availableBalanceviaGET .../wallets/{walletId} - POST to
/withdrawalswithtype: ZA_PNP_CASH→ receivetoken - Display token to customer with teller instructions (wallet is debited immediately)
- Poll
GET .../withdrawals/{withdrawalId}until status isSUCCESSFULorTIMEOUT - On
TIMEOUT: inform customer the token expired; the wallet is refunded automatically
Error Handling
Typical responses include:
- 400 – Invalid request
- 401 – Authentication failure
- 403 – Permissions error
Example Error Response
[
{
"type": "BUSINESS",
"severity": "LOW",
"description": "JWT has expired",
"code": "ARCH014",
"traceId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"spanId": "xxxxxxxxxxxxxxxx",
"environment": "sandbox-environment"
}
]Best Practices
- Always generate a unique externalUniqueId per request
- Never reuse tokens or identifiers
- Clearly display token expiry to users
- Avoid exposing sensitive data beyond masked formats
- Retry only with a new externalUniqueId to prevent duplicates
