Overview

Real-time event notifications when resources change.

First Leeward sends HTTP POST requests to your endpoint whenever resources change. Use webhooks to stay in sync without polling.

Setup

Register an endpoint by calling Create a webhook endpoint with a name and an HTTPS url. The response includes a signingSecret returned exactly once. Store it securely. First Leeward does not retain a copy, and there is no way to read it back later.

{
  "name": "production-events",
  "url": "https://api.example.com/webhooks/first-leeward"
}

You can register up to 5 active endpoints per project. Every event type is delivered to every active endpoint. To stop deliveries to an endpoint, call Disable a webhook endpoint. Disabling is one-way, so create a new endpoint if you need to resume delivery to the same URL.

To rotate a signing secret (for example if the previous value leaked), call Rotate the signing secret. The old secret is invalidated immediately. Update your verifier with the new value before the next event arrives, or in-flight deliveries will fail HMAC verification on your endpoint and be retried automatically.

Your endpoint must return a 2xx status code to acknowledge each delivery. Non-2xx responses or timeouts trigger automatic retries with exponential backoff over approximately 48 hours.

Source IP addresses

Webhook deliveries originate from a fixed set of egress IP addresses per environment. If your endpoint sits behind a firewall or allowlist, permit inbound traffic from all of the addresses for the environment you integrate with.

Contact solomont@cuminpay.io for the current list of egress IP addresses to allowlist.

Event envelope

Every delivery wraps event data in a standard envelope:

{
  "id": "evt_01J5K7Q8R9T0V1W2X3Y4Z5A6B7",
  "type": "CRYPTO_DEPOSIT_CREATED",
  "data": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "accountId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "chainId": "BASE",
    "status": "PENDING",
    "amount": "100.00"
  }
}
FieldTypeDescription
idstringUnique event ID. Use for idempotency.
typestringEvent type, e.g. CRYPTO_DEPOSIT_CREATED.
dataobjectPayload matching the corresponding API resource.

HTTP headers

HeaderDescription
X-Leeward-Webhook-IdUnique event ID (same as id in the body).
X-Leeward-Webhook-TimestampUnix timestamp (seconds) of signature generation.
X-Leeward-Webhook-SignatureHMAC-SHA256 signature. See Signature Verification.
Content-TypeAlways application/json.

Event types

EventDescription
USER_APPLICATION_STATUS_UPDATEDUser application status changed (KYC approval, rejection, etc.)
COMPANY_STATUS_UPDATEDCompany status changed (KYB approval, rejection, etc.)
CRYPTO_DEPOSIT_CREATEDNew on-chain deposit detected
CRYPTO_DEPOSIT_STATUS_UPDATEDDeposit status changed (approved, rejected, etc.)
CARD_STATUS_UPDATEDCard status changed (activated, frozen, blocked, etc.)
ACCOUNT_STATUS_UPDATEDAccount status changed (restricted, reactivated, etc.)
CARD_TRANSACTION_CREATEDNew card transaction
CARD_TRANSACTION_UPDATEDCard transaction lifecycle event (clearing, reversal, refund, dispute refund, etc.)
CARD_3DS_CHALLENGE_CREATED3DS challenge created for a cardholder (see 3D Secure)
CARD_SHIPMENT_STATUS_UPDATEDPhysical card shipment status changed
CARD_TOKENIZATION_REQUESTEDOTP for adding a card to a digital wallet via SMS verification (valid for a short window)
CARD_FRAUD_ALERT_CREATEDFraud alert created (detected or reported). See Fraud Alerts
CARD_FRAUD_ALERT_STATUS_UPDATEDFraud alert status changed (confirmed, declined, or expired)
CARD_DISPUTE_STATUS_UPDATEDCard dispute status changed

Payloads match the shape of the corresponding API resource. For example, CRYPTO_DEPOSIT_CREATED data matches GET /crypto-deposits/:id.

Delivery guarantees

Every event is delivered at least once, and in no particular order.

A delivery that times out or returns a non-2xx status is retried with exponential backoff over approximately 48 hours. A response we never see looks the same to us as a failure, so an event you already processed can arrive again. Deduplicate on the envelope id, which is stable across every redelivery.

Ordering is not guaranteed for any pair of events, including two events about the same resource. Treat each delivery as a statement about one resource at a point in time rather than a step in a sequence: upsert on the resource id in data, and ignore a payload whose updatedAt is older than what you have already stored.

Best practices

  • Return quickly. Respond with 2xx within a few seconds. Process heavy work in the background.
  • Be idempotent. Events may be delivered more than once. Use the id field to skip duplicates.
  • Do not rely on ordering. Reconcile from the payload rather than the arrival sequence.
  • Verify signatures. Always verify the signature before processing.