Webhooks overview
Webhooks push account activity to your systems as it happens, so you do not have to poll. When something happens on one of your accounts (an inbound payment is allocated, an outbound payment leaves, an internal transfer completes), we POST a small signed JSON message to a URL you register with us.
Every delivery is signed with our private key so you can prove it came from us and was not altered. There are no shared secrets, no passwords, and nothing for you to keep in a vault: you verify deliveries with a public key you fetch from us, the same way you already verify tokens.
Webhooks are a notification channel, not the system of recordPayloads are deliberately thin: they identify what happened and give you the id to fetch. Always read the full object from the API before acting on it. If your endpoint is down, nothing is lost: every event is also readable from
GET /v1/eventsfor 90 days.
Start here
- Build a receiver behind a public HTTPS URL that verifies the signature and answers
2xxin milliseconds. - Send us that URL and the event types you want. We register it and immediately ping it with
endpoint.verification.v1. - Answer the ping with any
2xx. The endpoint flips toActiveand real events start flowing. - Trigger a sandbox event and check the whole chain: digest, signature, de-duplication, then the fetch from the API.
- Schedule a
GET /v1/eventsreconciliation job before you go live.
How it works
- Something happens on your account. The platform records the event.
- We work out which of your registered endpoints subscribe to that event type.
- We POST the event to each of those URLs, signed, with a 15-second timeout.
- Your endpoint returns any
2xx. That is the whole contract. The response body is ignored. - If it does not, we retry on a backoff ladder for up to five days.
What a delivery looks like
POST /your/webhook/path HTTP/1.1
Host: webhooks.example.com
Content-Type: application/json
Webhook-Delivery-Id: 4a2f8c61-9b3d-4e77-a0c5-5d1e8f2b7c30
Webhook-Attempt: 1
Webhook-Group-Id: 7c9e5b12-4f68-4a3d-9e21-0b5c6d7e8f90
Content-Digest: sha-256=:rPoQRJ3l9Wiod0Y+++RfM+DU/KrKF19OAVD+7e8F4k4=:
Signature-Input: sig1=("@method" "@authority" "@request-target" "content-digest" "webhook-delivery-id" "webhook-attempt" "webhook-group-id");created=1758100462;keyid="70WOedgzv6hJ63CiLF9TDh6L5XBoL7c71svrGglv65o";alg="rsa-pss-sha256"
Signature: sig1=:<base64 signature>:
{"id":"9f1c2f0e-4c3b-4b8a-9d2a-5f7c1e6b0a11","type":"inbound-payment.received.v1","occurredAt":"2026-09-17T09:14:22.431+00:00","data":{"id":"3b8c5a41-9d27-4e6f-8a10-2c4b7e9f0d55","clientId":"c0a81f42-7b3e-4d19-9f88-1e2d3c4b5a60","accountId":"6d5e4f3a-2b1c-4098-8765-4321fedcba09","amount":{"amountInMinorUnits":125000,"value":1250.00,"currency":"GBP"},"code":"IP-000123"}}The body is the envelope: an id, a type, an occurredAt and a type-specific data block. Delivery metadata (which delivery this is and which attempt) lives in headers, never in the body, so the body stays byte-identical however many times it is sent. See Webhook events for the envelope and every event type.
Endpoints and subscriptions
A webhook endpoint is one HTTPS URL plus the list of event types it wants.
Endpoints belong to the auth group your API credentials belong to, not to a single client. If your group covers several member clients, one endpoint receives events for all of them and every payload carries a clientId telling you which member it is about. No other group information ever reaches you.
| Rule | Value |
|---|---|
| Scheme | https only |
| Host | Must be publicly resolvable. localhost, private, loopback, link-local and carrier-grade NAT addresses are rejected |
| Credentials in the URL | Not allowed (no user:password@) |
| URL length | 500 characters |
| Endpoints per auth group | 10 (disabled endpoints still occupy a slot) |
| Duplicate URLs in one group | Rejected, case-insensitively |
| Redirects | Never followed. A 3xx is a failed attempt |
Query strings and paths are preserved exactly as you register them; we never canonicalise your URL.
Getting set up
Webhook registration is operated by us today; there is no self-service API yet. To go live:
- Send your IFX Payments representative the HTTPS URL, the event types you want, a technical contact and your target go-live date, for each environment separately. Sending all of it at once saves a round trip.
- We register the endpoint. It starts in
Pendingand we immediately send it a verification ping: a normal signed delivery of typeendpoint.verification.v1. - Your endpoint answers
2xx. The endpoint flips toActiveand real events start flowing. A ping that never succeeds leaves the endpointPending, receiving nothing.
Changing the URL later puts the endpoint back to Pending and re-runs verification, so build the new URL before you ask for the change.
Coming laterThe
webhooks:manageOAuth scope is reserved for self-service endpoint management. It grants nothing today.
Events you can subscribe to
| Event type | Raised when |
|---|---|
inbound-payment.received.v1 | An inbound payment has been allocated to one of your accounts |
inbound-payment.returned.v1 | An inbound payment has gone back to the remitter |
outbound-payment.scheduled.v1 | An outbound payment is accepted and waiting for its value date |
outbound-payment.awaiting-funds.v1 | An outbound payment is waiting for the debiting account to be funded |
outbound-payment.sent.v1 | An outbound payment has left for the beneficiary bank |
outbound-payment.rejected.v1 | An outbound payment will not be sent |
outbound-payment.returned.v1 | The beneficiary bank sent an outbound payment back |
outbound-payment.cancelled.v1 | An outbound payment was cancelled before it was sent |
internal-payment.completed.v1 | A transfer between your own accounts has completed |
fx-trade.booked.v1 | A trade is agreed and the rate is fixed |
fx-trade.settled.v1 | Both legs of a trade have settled |
fx-trade.failed.v1 | A trade will not settle |
transaction.created.v1 | A ledger movement was booked on one of your currency accounts |
Subscribe only to the types you handle. transaction.created.v1 is the ledger feed and much higher volume than the rest: one payment or trade produces several movements.
Plus one system event you cannot subscribe to and will always receive: endpoint.verification.v1, the ping that proves a new URL works.
Payloads and examples for every type are in Webhook events.
If you would rather pull
GET /v1/events returns the same events over HTTP, newest first, for 90 days after they were raised. It needs the events:read scope. Use it as:
- the recovery path when your endpoint was down or disabled, and
- a complete integration route on its own if you would rather not run a receiver at all.
The id is the same value the webhook body carries, so the two de-duplicate against each other. See Building a receiver for the polling pattern.
Environments
Sandbox and production are separate in every respect: separate endpoint registrations, separate subscriptions, and separate signing keys. A delivery signed in sandbox will not verify against production's key set, which is deliberate. Point each environment's receiver at that environment's key set URL.
What we guarantee, and what we do not
| Delivery | At least once. Retries and replays mean the same id can arrive more than once, so de-duplicate on it |
| Ordering | Not guaranteed. Reconcile with occurredAt and a fetch from the API |
| Success | Any 2xx within 15 seconds |
| Retries | Backoff ladder of 30s, 5m, 30m, 2h, 5h, then every 8 hours, until the event is five days old |
| Throughput | One delivery at a time plus the 15-second timeout is a hard ceiling. A handler that answers in 200ms sustains hundreds of events a minute; one that takes 10 seconds sustains six |
| Concurrency | One delivery in flight per endpoint at a time |
| Payload | Thin and stable for a given type name; new optional fields may appear without notice |
| Body | Byte-identical across every attempt and replay of the same event |
Next
- Securing your webhook endpoint: verifying the signature, the key set, rotation.
- Webhook events: the envelope, versioning and every event type.
- Delivery, retries and endpoint health: what happens when your endpoint is slow, failing or offline.
- Building a receiver: a working checklist, recovery, testing and troubleshooting.
Updated about 2 months ago