Concepts
Webhooks
Every change is POSTed to the webhook URL on the key that caused it, signed with that key’s secret.
Events
| deposit:confirmed | A customer’s crypto deposit was credited to your balance. |
| collection:completed | A naira collection was paid and credited as USDT. |
| collection:expired / failed | It was not paid in time, or it failed. |
| swap:completed | A swap went through. |
| payout:processing / completed / failed | A crypto payout moved. Failed ones are refunded. |
| bank_payout:processing / completed / failed | A bank payout moved. Failed ones are refunded. |
Verify the signature
X-Rimoswap-Signature is an HMAC-SHA256 of the raw request body with the key's webhook signing secret. Compute it over the exact bytes you received; parsing and re-serialising the JSON first will not match.
Delivery
Answer with any 2xx. Anything else is retried after 1m, 5m, 25m, 2h and 10h. Events can arrive twice or out of order: de-duplicate on data.id with data.status, and read the object back if in doubt. Every event is listed, with your server's answer and a Resend button, on Business → Infrastructure API.
Verifying a webhookNode.js
import crypto from 'node:crypto'; app.post('/rimoswap', express.raw({ type: 'application/json' }), (req, res) => { const expected = crypto .createHmac('sha256', process.env.RIMOSWAP_WEBHOOK_SECRET) .update(req.body) // the raw bytes .digest('hex'); const given = req.get('X-Rimoswap-Signature') ?? ''; if (expected.length !== given.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(given))) { return res.status(400).send('bad signature'); } const event = JSON.parse(req.body.toString()); // de-duplicate on event.data.id + event.data.status res.sendStatus(200); });
