ezForex
Toggle sidebar
Log in
Customer webhooks

ezForex sends signed JSON events to active HTTPS endpoints that subscribe to an event.

Request format

Every request has a common envelope. Event-specific fields are nested under data.

{
  "id": "f8e44bb0-0b6f-4d2d-838d-a63ce746bb65",
  "event": "rates.imported",
  "created_at": "2026-07-18T19:15:32+00:00",
  "data": {
    "run_id": 184,
    "import_id": 91
  }
}
Field Type Description
id UUID Stable event identifier stored with the delivery.
event string One of the event names documented below.
created_at ISO 8601 datetime UTC time at which the delivery was created.
data object The event-specific payload.

Requests include X-EzForex-Event, X-EzForex-Delivery, X-EzForex-Timestamp, and X-EzForex-Signature headers. The timestamp is a Unix timestamp generated for the delivery attempt.

Events
rates.imported
Emitted

Sent to every active subscribed endpoint after an exchange-rate import completes successfully.

Data field Type Description
run_idintegerImport run identifier.
import_idintegerCreated rate snapshot identifier.
providerstringWinning rate-provider slug.
fallback_usedbooleanWhether a provider before the winner failed.
provider_attempt_countintegerNumber of providers attempted.
provider_timestampintegerUnix timestamp supplied by the rate provider.
rates_importedintegerNumber of rates saved.
rates_skippedintegerNumber of provider rates skipped.
"data": {
  "run_id": 184,
  "import_id": 91,
  "provider": "frankfurter-ecb",
  "fallback_used": true,
  "provider_attempt_count": 2,
  "provider_timestamp": 1784401200,
  "rates_imported": 169,
  "rates_skipped": 2
}
rates.import_failed
Emitted

Sent to every active subscribed endpoint when an exchange-rate import fails.

Data field Type Description
run_idintegerFailed import run identifier.
import_idnullNo snapshot is created when all providers fail.
providerstring or nullLast attempted provider slug.
provider_attempt_countintegerNumber of providers attempted.
error_countintegerNumber of errors recorded for the run.
error_messagestring or nullSummary of the import failure, when available.
finished_atISO 8601 datetime or nullTime at which the failed run finished.
"data": {
  "run_id": 185,
  "import_id": null,
  "provider": "frankfurter-ecb",
  "provider_attempt_count": 2,
  "error_count": 2,
  "error_message": "Every configured exchange-rate provider failed.",
  "finished_at": "2026-07-18T19:20:08+00:00"
}
rates.stale
Emitted

Sent once per stale-data incident to every active subscribed endpoint. Internal provider errors and notification details are never included.

Data field Type Description
incident_idintegerStable stale-episode identifier.
statestringstale or unavailable.
import_id / run_idinteger or nullRelated snapshot and acquisition run identifiers.
providerstring or nullRate provider slug, when a snapshot exists.
rates_as_ofISO 8601 datetime or nullProvider market timestamp.
stale_since / detected_atISO 8601 datetime or nullThreshold crossing and incident detection times.
threshold_secondsintegerConfigured freshness threshold.
"data": {
  "incident_id": 12,
  "state": "stale",
  "import_id": 91,
  "run_id": 184,
  "provider": "open-exchange-rates",
  "rates_as_of": "2026-07-18T16:00:00+00:00",
  "stale_since": "2026-07-18T19:00:00+00:00",
  "detected_at": "2026-07-18T19:05:00+00:00",
  "threshold_seconds": 10800
}
alert.triggered
Emitted

Sent only to active subscribed endpoints owned by the user whose rate alert was triggered. The alert must use the webhook channel.

Data field Type Description
occurrence_idintegerRecorded trigger occurrence identifier.
alert_idintegerRate alert identifier.
fromstringThree-letter source currency code.
tostringThree-letter target currency code.
directionstringup, down, or either.
threshold_pctnumberConfigured percentage threshold.
previous_ratenumberCross-rate in the previous distinct snapshot.
current_ratenumberCross-rate in the current snapshot.
change_pctnumberSigned percentage movement from the previous rate.
previous_import_idintegerPrevious snapshot identifier.
current_import_idintegerCurrent snapshot identifier.
"data": {
  "occurrence_id": 42,
  "alert_id": 17,
  "from": "USD",
  "to": "ZAR",
  "direction": "up",
  "threshold_pct": 2,
  "previous_rate": 18,
  "current_rate": 18.36,
  "change_pct": 2,
  "previous_import_id": 90,
  "current_import_id": 91
}
rates.stale
Reserved

This event can be selected when configuring an endpoint, but stale-rate monitoring is not currently emitted and its data contract has not yet been defined.

Verify the signature

Compute HMAC-SHA256 over X-EzForex-Timestamp, a period, and the exact raw request body. Compare it in constant time with the hex value after sha256= in X-EzForex-Signature.

$expected = hash_hmac('sha256', $timestamp.'.'.$rawBody, $secret);
if (! hash_equals($expected, $providedSignature)) {
    abort(401);
}

Deliveries retry connection errors and HTTP 408, 429, and 5xx responses with exponential backoff. Other 4xx responses are terminal. Redeliveries receive a new delivery ID and retain an audit link to the original.