Overview
Webhooks let Hyparrow push events to your server in real time instead of you polling the API. When something happens, a subscription renewal is paid, a customer transaction completes, Hyparrow sends an HTTPPOST with a JSON payload to the URL you configure.
Each webhook request carries these headers:
A delivery is acknowledged by
200, 201, 202 or 204. Anything else, or no answer within 30
seconds, is retried: the first attempt is immediate, then retries follow after 1m, 5m, 30m, 2h and
24h, for 6 attempts in total over roughly 26.5 hours. After the last one the delivery is
marked failed and stays visible in delivery history.
Base URL: Point your webhook URL at a sandbox listener and configure it from
https://api.hyparrow.cloud/api/v1Webhook settings endpoints are authenticated with your API key pair:https://sandbox.hyparrow.cloud/api/v1 with pk_test_ keys to safely exercise deliveries.Configure your webhook URL
POST /webhooks/settings sets the URL Hyparrow delivers events to. You may also pass an optional
allowedIps list to restrict which source IPs your endpoint accepts.
1
Set your URL
POST your HTTPS endpoint to
/webhooks/settings.2
Generate a signing secret
Call
/webhooks/settings/secret so deliveries are signed.3
Verify the signature on every request
Recompute the HMAC over the raw body and compare it to
X-Hyparrow-Signature.4
Send a test event
Use
/webhooks/settings/test to confirm end-to-end delivery.View current settings
GET /webhooks/settings returns your configured URL, allowed IPs, whether a secret is set (with a
masked preview), and your customer-email preference.
Generate a signing secret
POST /webhooks/settings/secret generates a new signing secret and stores it. The full secret is
returned only once, store it securely.
Verifying the signature
When a secret is set, every delivery includes anX-Hyparrow-Signature header. It is an
HMAC-SHA512 of the raw request body, keyed with your webhook secret and hex-encoded.
To verify, recompute the HMAC over the exact bytes you received and compare it to the header using
a constant-time comparison. Reject the request if they differ.
Timestamped signature (recommended)
X-Hyparrow-Signature covers only the body, so a captured delivery would still verify if someone
replayed it later. X-Hyparrow-Signature-256 closes that gap. It is the lowercase hex
HMAC-SHA256, keyed with the same webhook secret, of the string:
- Read
X-Hyparrow-Timestampand reject the request if it is more than 5 minutes from your clock. Every attempt, including a retry, is signed when it is sent, so a genuine delivery is always fresh. - Compute the HMAC-SHA256 over
timestamp + "." + rawBodyand compare it toX-Hyparrow-Signature-256in constant time. - Deduplicate on
X-Webhook-ID, which stays the same across retries of one event.
X-Hyparrow-Signature keeps working unchanged.
Send a test webhook
POST /webhooks/settings/test delivers a sample event to your configured URL so you can validate
your endpoint and signature handling.
Example event payload
All events share the same envelope: a top-levelevent name, a timestamp, and a data
object. On transfer, subscription and test events timestamp is Unix seconds (a number); on
payroll events it is an RFC 3339 string. For a single clock to check against, use the
X-Hyparrow-Timestamp header, which is Unix seconds on every event. Here is a subscription.payment.completed event, delivered when a subscription renewal is
paid:
checkout.payment.completed (a checkout-link payment landed)
and customer.transaction.completed (a customer transaction settled). All follow the same
event / timestamp / data envelope, so write your handler to switch on the event field.
Payroll
Bulk salary runs emit four events. Item events carry both the item’s owntransferCode and the batchReference, so you can reconcile per employee or per
run without keeping your own mapping between the two. See Payroll.
sent is not a confirmed credit —
that is the local debit record. payroll.item.settled is what confirms delivery.
Outbound transfer settlement
transfer.settled and transfer.failed tell you the final outcome of a payout you initiated with
POST /money-transfer/send. They fire only once the outcome is known for certain.
transfer.reversed tells you money for a transfer was credited back to your wallet.
Hyparrow asks the processor for the outcome of recent transfers every 5 minutes, starting 5 minutes
after the send, so a settled or failed event normally arrives within about 10 minutes. A transfer
the processor cannot see yet is asked about again after 15 minutes, 1 hour, 6 hours and then daily.
This matters because the
status returned by POST /money-transfer is written when your wallet is
debited, which happens before the transfer is submitted downstream. It confirms the debit, not the
delivery. These events are what confirm delivery.
provider object is the upstream processor’s own record, passed through unmodified so you can
verify the outcome independently rather than taking our word for it:
transactionResponseCode—90000means the beneficiary was credited. Any other value on atransfer.failedevent is the decline reason (for example90051, insufficient funds).transactionRef— the bank-traceable reference. Quote this when querying a beneficiary bank.transactionId— the processor’s record id.
amount is the principal and fee is the fee charged on top of it, both in kobo, so each can be
posted as its own ledger entry.
Set
clientReference on your POST /money-transfer/send request and it is echoed back on these
events, so you can match a settlement to your own record without storing the transferCode.Reversals
transfer.reversed fires whenever money for a transfer comes back to your wallet:
- At submission. If the processor refuses a transfer outright, the send returns an error, the
principal is credited straight back and no fee is charged.
transfer.reversedconfirms the credit withreason: "provider_rejected". - After a decline. A
transfer.faileddebit is reviewed and then reversed, principal and fee.transfer.reversedfires when that happens, withreason: "manual_reversal". Until then the debit stands; contact support if a failed transfer has not been reversed.
totalReturned as the reversing entry against the original clientReference. The reversal
also appears in GET /transactions and the statement export as a credit row
with reference REV_<transferCode>, carrying the same clientReference.
Customer email preference
PUT /webhooks/settings/customer-emails controls whether Hyparrow sends transactional emails
(invoice receipts, subscription notices) directly to your end customers. Set
handleCustomerEmails to false if you prefer to own all customer communication yourself.
The
handleCustomerEmails field is required and must be a boolean. Omitting it returns 400; an
explicit false is accepted and disables customer-facing emails.Delivery history
GET /webhooks/events returns recent delivery attempts so you can audit what was sent and whether
it succeeded. Useful both for debugging and as a polling fallback.
status is one of pending (awaiting a retry), delivered (a 2xx was returned),
or failed (exhausted all attempts). Inspect lastError and httpStatus to diagnose failures.
