Relay
Relay is Payonify's API for marketplaces and platforms - businesses that collect payments from customers and then pay out sellers or service providers.
The flow is simple: you collect with ordinary charges, then pay out with transfers, keeping your own commission on each one. Transfers are paid from the funds you've collected.
Relay runs on EcoCash only for now - both the payments you collect and the payouts you send go over EcoCash. Support for other mobile-money wallets will follow.
Setting up Relay
- Create a project. In your Payonify dashboard, create a new project. Web is the default project type and the one Relay needs - leave it selected and the Enable Relay checkbox appears. Tick it to turn Relay on. The checkbox only shows for approved businesses; sole traders aren't eligible for Relay. Relay can only be enabled at creation, so use a new project rather than an existing one.
- Choose who covers the payout fees. With Relay on, you pick who bears the fee on each transfer - you (the default), the recipient, or split evenly. See Fees and Commission below for what each option means. You can change this later in the project's settings.
- Go live when you're ready. Toggle the project to live mode and follow the prompts - if your business isn't fully verified yet, we'll guide you through that first. When your platform is ready, add your production URL in the project's settings so we can review it.
How Relay Works
Step-by-Step Guide
-
Collect funds with a charge
Customer will pay using charges on your Relay-enabled project. When it goes through, the money lands in your account - first as
pending, thenavailableonce a short hold has passed. See the Charges API for the full details. -
Check what you can pay out
Your funds are kept per currency and per provider - money collected on EcoCash pays out on EcoCash. Verify the funds you have before you disburse to make sure you're holding enough:
CodeResponseOnly
availablefunds can be used for creating transfers.pendingis still in the short hold after collection, andreservedis set aside for transfers currently in flight - it comes back toavailableif a transfer fails. See the Transfers API for the full response. -
Validate the recipient (optional)
Before sending, you can confirm the recipient's number and see the name it's registered to, so you know the details are correct before you transfer. Use the same
mobile_moneydetails you'll include in the transfer:CodeResponseIf the number is not registered with the provider, you'll get a
422with arecipient_not_founderror instead. See the Transfers API for the full details. -
Create a transfer
Send money to the recipient and set your commission with
application_fee_amount:CodeResponse (201 Created):
ResponseThe transfer comes back as
pendingand is processed in the background. See the Transfers API for every field you can send. -
Track status
You don't have to keep checking on it. The moment the transfer succeeds, we send you a
transfer.succeededwebhook (ortransfer.failedif it didn't go through) - see the Webhooks guide for setting up an endpoint and verifying signatures. You can also retrieve the transfer whenever you like:CodeResponse -
List your transfers
You can list your transfers and filter them:
CodeSee the Filtering guide for all the options, and the Transfers API for the response structure.
Fees and Commission
Every transfer response carries three amounts, and we keep them separate:
| Amount | What it is |
|---|---|
amount | The amount you asked to send |
net_amount | What the recipient actually receives, after the transfer fee |
application_fee_amount | Your commission on the transfer (can be 0) |
You set amount and application_fee_amount on the request; we work out net_amount and return it.
Who covers the transfer fee. Every transfer has a small fee, and you pick who covers it when you set up your project:
- You cover it (the default): the recipient gets the full
amount, sonet_amountmatchesamount. - The recipient covers it: the fee comes out of what they get, so
net_amountis a little less thanamount. - Split it: you each cover half.
This is a project setting in your dashboard, not something you send on the request. Whichever you choose, net_amount always tells you exactly what the recipient will get.
When a transfer succeeds, the recipient gets the net_amount, and your full application_fee_amount becomes available in your Payonify account and settles like any other revenue.
Transfer Statuses
| Status | Meaning |
|---|---|
pending | We've accepted the transfer and it's on its way |
succeeded | The money reached the recipient |
failed | It didn't go through |
Reversing a transfer
Sometimes a payout needs to be undone - an order fell through, or you sent it in error. You can reverse a settled transfer, and the amount comes back to your relay balance.
Reverse a transfer by its ID with the Reversals API:
Code
Like a transfer, a reversal is processed in the background - you get a processing response straight away, then a reversal.succeeded (or reversal.failed) webhook once it's done.
A few things to know:
- It's the full amount. A transfer is reversed in full - you can't reverse part of it.
- Once per transfer. A second attempt returns
already_reversed. If a reversal fails, nothing changes and you can try again. - Only settled transfers. The transfer must have
succeededfirst; reversing one that's stillpendingreturnsreversal_invalid_state. - Your commission comes back too. The commission you kept on the transfer is reversed along with it.
The same Reversals API also reverses standard payouts - see the Reversals reference for details.
Test Numbers
In test mode, the recipient's mobile number decides what happens:
| Mobile Number | Behavior |
|---|---|
772111111 | Succeeds (recipient: John Doe) |
772111112 | Succeeds (recipient: Jane Smith) |
772111113 | Fails - recipient can't receive the funds |
772111114 | Fails - system timeout |
772111116 | Fails - unregistered recipient |
Errors
Common Relay errors (all invalid_request_error unless noted):
| Code | When it happens |
|---|---|
insufficient_funds | The transfer is more than your available funds |
relay_not_enabled | Relay isn't enabled for this project |
invalid_destination | The destination is missing or isn't a supported mobile-money wallet |
parameter_invalid | A field is invalid, or the amount doesn't cover the transfer fee |
channel_partner_not_found | Your relay payout merchant isn't set up yet |
b2c_not_allowed | B2C disbursements aren't enabled on the relay payout merchant |
recipient_not_found | The recipient's number doesn't exist or isn't valid (from a validation check) |
already_reversed | The transfer has already been reversed |
reversal_invalid_state | The transfer hasn't settled yet, so it can't be reversed |
See the Errors reference for the full error format.