Skip to main content
Webhooks let Melio notify your system when something changes, so you don’t have to poll. When a subscribed event occurs, Melio sends an HTTP POST to the endpoint you registered. Each partner has a single webhook endpoint per environment (Test and Live). You configure it in the developers portal, under Developers → Webhooks. Webhook endpoints are not managed through the API.

Setting up your endpoint

In the developers portal, open Developers → Webhooks and select the environment you’re configuring (Test or Live).
  1. Enter your Endpoint URL. It must be an https URL.
  2. Choose the events you want to receive (see Event types). You must select at least one.
  3. Select Save endpoint.
When you save the endpoint for the first time, the portal creates its signing secret and shows it once. Copy it into your secret store right away. You’ll use it to verify deliveries. If you lose it, rotate it to get a new one. From the same page you can:
  • Edit the URL or the subscribed events. Saving replaces the whole event list.
  • Pause deliveries without deleting the endpoint, and resume them later. See Retries and failure handling for what happens to events while paused.
  • Rotate the signing secret.
  • Delete the endpoint. Melio stops sending deliveries. Registering a new endpoint later creates a new signing secret.

Event types

Delivery payload

Each delivery is a lightweight notification. It identifies the affected resource, not its full state. Treat the payload as a signal to fetch the current resource (for example GET /payments/{id}) rather than as the source of truth, so you never act on a stale snapshot. Every delivery includes these fields: Some events carry a small data object; most do not, and you should fetch the resource for its current state. Account created/deleted events include:

Verifying deliveries

Every delivery is signed so you can confirm it genuinely came from Melio and was not tampered with. Deliveries follow the Standard Webhooks specification and are signed with HMAC-SHA256 using your webhook signing secret. The signing secret looks like whsec_<base64>. It is shown in the developers portal when you create your endpoint or rotate the secret. It is not an API key and can’t be used to call the API, so your webhook receiver never needs to hold an API credential.

Request headers

The signed content is the string {webhook-id}.{webhook-timestamp}.{raw request body}.

Verifying with the Standard Webhooks library

The simplest option is the official standardwebhooks library. Pass it the signing secret exactly as shown in the portal, along with the raw request body and the request headers. It throws if the signature is invalid or the timestamp is more than 5 minutes old.

Verifying without a library

If you’d rather not add a dependency, compute the HMAC yourself. The HMAC key is the base64-decoded part of the secret after whsec_.
Verify the signature against the raw request body exactly as received. Re-serializing the parsed JSON can change byte-for-byte formatting and produce a different HMAC, causing valid deliveries to fail verification. Reject any delivery whose signature does not match.

Rotating the signing secret

You can rotate the signing secret from Developers → Webhooks at any time, for example on a schedule or if you suspect it has leaked. The new secret is shown once, when you rotate. When you rotate, you choose how the old secret is retired:
  • Keep the old secret valid for 24h (default). For the next 24 hours every delivery is signed with both the old and the new secret, and webhook-signature carries both signatures. Your verifier keeps accepting deliveries with either secret, so you can deploy the new secret at any point in that window without dropping events. After 24 hours only the new secret signs.
  • Rotate now. The old secret stops signing immediately. Use this if the secret may have leaked. Deliveries are signed only with the new secret, so update your verifier first or expect failed verifications until you do.
Only one overlap can be in progress at a time. While the previous secret is still signing, only Rotate now is available.

Handling deliveries reliably

  • Verify every delivery. Check the signature and the timestamp before you trust the payload.
  • Be idempotent. The same event may be delivered more than once. Deduplicate on messageId (also sent as the webhook-id header) so you process each event only once.
  • Respond quickly with a 2xx. Acknowledge receipt fast and do heavy work asynchronously. Failed deliveries are retried.
  • Fetch current state. Because deliveries are notifications, always read the resource (GET) to get its latest state before acting, rather than trusting a possibly out-of-order payload.
  • Pause safely. Pause the endpoint in the portal to stop deliveries during maintenance instead of deleting and recreating it. Note that events that occur while paused are dropped (see below).

Retries and failure handling

A delivery succeeds when your endpoint returns a 2xx status. Any other outcome is a failure and Melio retries the delivery: a 4xx, a 5xx, a connection error, or no response at all. Redirects are not followed, and a 3xx response counts as a failed delivery. Point the endpoint URL directly at your handler.

Retry schedule

Melio makes up to 3 attempts per event, spaced roughly 45 minutes apart:
  1. Within seconds of the event occurring.
  2. About 45 minutes after attempt 1.
  3. About 45 minutes after attempt 2.
The interval is fixed and there is no exponential backoff, so a failing endpoint has about 90 minutes to recover before Melio gives up on an event. After the third failed attempt, the delivery is parked in an internal dead letter queue and is not retried again. Replaying events is in development and will be part of a future release.
Retries are per event, not per batch. One failing delivery does not delay or duplicate the deliveries for other events.

What gets retried

Because every failing status is retried, returning a 5xx is a legitimate way to ask for a retry, for example when your own datastore is briefly unavailable. Return a 2xx only once you have durably stored or queued the delivery.

Paused endpoints

While your endpoint is paused, or has no subscription for an event type, Melio does not attempt a delivery at all. Those events are dropped, not buffered. They will not arrive when you resume the endpoint, and they are never retried. Pausing is safe for suppressing traffic during maintenance, but it is not a queue. If you cannot afford to miss events, keep the endpoint active and return a failing status so the delivery is retried instead.

Staying correct across retries

  • Deduplicate on messageId. The same messageId is used for every attempt of the same event, so it is a stable idempotency key. It is also sent as the webhook-id header. Treat a delivery whose messageId you have already processed as a no-op and return 2xx.
  • Don’t assume ordering. A retried delivery can arrive after newer events for the same resource. A retried api.payment.updated may land after the payment has moved on again. Always GET the resource and act on its current state rather than on the payload, and never rely on occurredAt ordering to drive a state machine.
  • Respond quickly. Acknowledge with a 2xx as soon as the delivery is persisted, and do heavy work asynchronously. A slow handler burns the attempt and forces an unnecessary retry.
  • Expect a fresh timestamp on a retry. Each attempt is signed at the time it is sent, so the 5-minute timestamp check in your verifier applies to retries as well.