Appearance
Callbacks Overview
Callbacks are how Flywire notifies your systems about events as they happen. Where the rest of this documentation describes inbound requests (your server calling the Agents API), a callback is the opposite direction: Flywire sends an asynchronous HTTP POST to a URL you configure whenever something noteworthy occurs — for example, when a payment's status changes.
You don't poll for these events. Once callbacks are enabled for your agency, Flywire pushes them to your endpoint as they occur.
Envelope
Every callback shares the same top-level envelope. The event-specific data lives under data; the surrounding fields identify and timestamp the delivery.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier for this callback delivery. Use it to de-duplicate — Flywire may deliver the same event more than once. |
occurredOn | string (ISO-8601) | When the event occurred. |
data | object | Event-specific payload. Its shape depends on the callback type — see the per-callback pages below. |
json
{
"id": "9f1c8d7e-2a3b-4c5d-8e6f-0a1b2c3d4e5f",
"occurredOn": "2026-03-15T08:55:00Z",
"data": {
"...": "event-specific fields"
}
}Headers
Each callback request carries the following headers:
| Header | Description |
|---|---|
Content-Type | application/vnd.flywire.<type>+json;version=<version> — a vendor media type identifying the event and its version. For example, application/vnd.flywire.payment.status.updated+json;version=1.0. |
Message-Signature | A Base64-encoded HMAC-SHA256 signature of the raw request body, keyed with your agency API key. Use it to verify the callback genuinely came from Flywire. See Verifying signatures. |
| Custom auth header | If configured for your agency, Flywire adds an additional authentication header (for example an API-key header) with a value you provide. Use it as a second layer of verification. |
Verifying signatures
The Message-Signature header lets you confirm a callback is authentic and untampered. Compute Base64(HMAC-SHA256(apiKey, rawRequestBody)) over the exact bytes of the request body and compare it against the header value using a constant-time comparison.
The signing key is your agency API key — the same key you send as X-Authentication-Key on inbound requests (see Authentication).
WARNING
Verify against the raw request body exactly as received. Re-serializing the parsed JSON can change whitespace or key order and produce a different signature.
python
import base64
import hashlib
import hmac
def is_signature_valid(api_key: str, raw_body: bytes, signature_header: str) -> bool:
expected = base64.b64encode(
hmac.new(api_key.encode(), raw_body, hashlib.sha256).digest()
).decode()
return hmac.compare_digest(expected, signature_header)javascript
import crypto from "node:crypto";
function isSignatureValid(apiKey, rawBody, signatureHeader) {
const expected = crypto
.createHmac("sha256", apiKey)
.update(rawBody)
.digest("base64");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Configuration
Callbacks are set up with Flywire on a per-agency basis. The relevant settings are:
| Setting | Description |
|---|---|
| Callbacks enabled | A flag that turns outbound callbacks on for your agency. When disabled, no callbacks are sent. |
| Default callback URL | The endpoint Flywire delivers callbacks to. |
| Allowed callback domain | The callback URL must match this domain — a safeguard against misconfigured or unexpected destinations. |
| Custom auth header (optional) | An extra authentication header (name and value) added to every callback request. |
Contact Flywire to enable callbacks and register your endpoint.
Delivery and retries
Flywire uses your HTTP response status to decide whether a callback was accepted:
| Response | Outcome |
|---|---|
2xx | Accepted. No further delivery attempts. |
4xx (except 429) | Rejected. The callback is not retried. |
429 or 5xx | Treated as a transient failure and retried. |
| Network error / timeout | Treated as a transient failure and retried. |
To integrate reliably:
- Respond quickly with a
2xx. Acknowledge receipt first, then do heavier processing asynchronously so you don't risk a timeout and an unnecessary retry. - Be idempotent. Because an event may be delivered more than once, use the envelope
idto recognize and ignore duplicates. - Verify every callback. Check the
Message-Signature(and any custom auth header) before acting on the payload.
Available callbacks
| Callback | Event type | Description |
|---|---|---|
| Payment Status Updated | payment.status.updated | Sent when a payment is recorded or its status changes. |
TIP
More callback types will be documented here over time. If you rely on an event that isn't listed yet, reach out to Flywire.