Skip to main content
Webhooks are HTTP POSTs Anton sends to a URL you control when something happens in your account — a payout moves through its lifecycle, a beneficiary is created, an FX exchange settles. Subscribe by calling POST /v1/webhooks with the URL and list of event types you want to receive. Deliveries are at-least-once. You may occasionally receive the same event twice — deduplicate on the envelope id. Events for the same resource are dispatched in the order they occur, but network retries mean they can arrive out of order at your endpoint. Failed deliveries retry with exponential backoff (30 seconds, 2 minutes, 8 minutes, 32 minutes, then hourly up to 5 attempts total) before being abandoned. Every delivery is HMAC-signed — see Webhook Signing for verification. Respond with any 2xx status within 30 seconds to acknowledge receipt.

Delivery envelope

Every delivery has the same outer shape. data holds the resource-specific payload documented below.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8U9V",
  "type": "payout.completed",
  "created_at": "2026-04-15T14:30:00Z",
  "data": {
    "id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "status": "completed",
    "...": "resource-specific fields"
  }
}
Each delivery also carries these HTTP headers:
HeaderDescription
X-Webhook-IDThe envelope id — use for deduplication.
X-Webhook-EventThe event type, e.g. payout.completed.
X-Webhook-TimestampUnix seconds at signing time. Used for verification.
X-Webhook-Signaturev1={hex-hmac-sha256}. See Webhook Signing.
User-AgentAntonPayments-Webhook/1.0

Event catalog

Payout events

10 events covering the full payout lifecycle.

Beneficiary events

Beneficiary create, update, delete, and block.

Instrument events

Payment instrument create, update, delete.

Batch events

Batch upload, completion, and failure.

FX events

Quote locks and exchange execution.

Funding events

Incoming funds credited to your balance.

Compliance events

Screening hits that require manual review.

Test events

Fired by the test-delivery endpoint.

Payout events

Event typeFires when
payout.createdA payout is successfully created via POST /v1/payouts.
payout.approvedA pending payout is approved (maker-checker flow).
payout.processingThe payout has been submitted to the underlying rail provider.
payout.sentThe rail has confirmed funds are on the way to the beneficiary.
payout.completedThe rail confirms funds have been delivered.
payout.failedRail submission or execution failed terminally.
payout.cancelledThe payout was cancelled before execution.
payout.returnedThe rail reported the funds were returned after sending.
payout.screening_failedCompliance screening blocked the payout.
payout.velocity_blockedA risk/velocity rule blocked the payout at create time.
The payload for payout lifecycle events is the payout object. The exact shape depends on the event — payout.created includes the initial fields; payout.sent, payout.completed, and payout.failed include rail identifiers and timestamps as they become available. payout.screening_failed and payout.velocity_blocked use a compact shape with just id, status, and reason.
When a real-time risk score is available on the payout (available via the Anton risk engine), the lifecycle event payloads include a risk object summarising the score and contributing factors.

payout.created

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8U9V",
  "type": "payout.created",
  "created_at": "2026-04-15T14:30:00Z",
  "data": {
    "id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "pending_screening",
    "source_amount": "500.00",
    "source_currency": "USD",
    "dest_amount": "458.25",
    "dest_currency": "EUR",
    "beneficiary_id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1",
    "instrument_id": "ins_01HX8Z9K0M2N3P4Q5R6S7T8UA2",
    "reference": "Invoice #1042",
    "created_at": "2026-04-15T14:30:00Z"
  }
}

payout.processing, payout.sent, payout.completed, payout.failed, payout.returned

These share the same lifecycle payload shape. rail_provider, rail_reference, completed_at, and failed_at are populated when the rail reports them.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8U9W",
  "type": "payout.completed",
  "created_at": "2026-04-15T14:45:12Z",
  "data": {
    "id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "completed",
    "source_amount": "500.00",
    "source_currency": "USD",
    "dest_amount": "458.25",
    "dest_currency": "EUR",
    "beneficiary_id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1",
    "instrument_id": "ins_01HX8Z9K0M2N3P4Q5R6S7T8UA2",
    "reference": "Invoice #1042",
    "rail_provider": "openpayd",
    "rail_reference": "OP-2026-04-15-778812",
    "created_at": "2026-04-15T14:30:00Z",
    "updated_at": "2026-04-15T14:45:12Z",
    "completed_at": "2026-04-15T14:45:12Z"
  }
}

payout.screening_failed

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8U9X",
  "type": "payout.screening_failed",
  "created_at": "2026-04-15T14:30:03Z",
  "data": {
    "id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "screening_failed",
    "reason": "compliance screening flagged this payout"
  }
}

payout.velocity_blocked

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8U9Y",
  "type": "payout.velocity_blocked",
  "created_at": "2026-04-15T14:30:01Z",
  "data": {
    "id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "status": "velocity_blocked",
    "reason": "payout blocked by risk policy"
  }
}

payout.approved

Fires when a payout transitions to approved — either because compliance screening cleared it or an ops reviewer approved the manual review case it was queued in. Arrives before payout.processing for the same payout.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8U9W",
  "type": "payout.approved",
  "created_at": "2026-04-15T14:30:05Z",
  "data": {
    "id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "approved",
    "source_amount": "500.00",
    "source_currency": "USD",
    "dest_amount": "458.25",
    "dest_currency": "EUR",
    "beneficiary_id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1",
    "instrument_id": "ins_01HX8Z9K0M2N3P4Q5R6S7T8UA2",
    "reference": "Invoice #1042",
    "created_at": "2026-04-15T14:30:00Z",
    "updated_at": "2026-04-15T14:30:05Z"
  }
}

payout.cancelled

Fires when a payout is cancelled before being submitted to the rail. The payload includes the previous_status at the time of cancellation and the released_amount that was returned to the merchant’s available balance.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8U9V",
  "type": "payout.cancelled",
  "created_at": "2026-04-15T14:31:00Z",
  "data": {
    "id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "cancelled",
    "previous_status": "created",
    "released_amount": "500.00",
    "source_amount": "500.00",
    "source_currency": "USD",
    "dest_amount": "458.25",
    "dest_currency": "EUR",
    "beneficiary_id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1",
    "instrument_id": "ins_01HX8Z9K0M2N3P4Q5R6S7T8UA2",
    "reference": "Invoice #1042",
    "created_at": "2026-04-15T14:30:00Z",
    "updated_at": "2026-04-15T14:31:00Z"
  }
}

Beneficiary events

Event typeFires when
beneficiary.createdA beneficiary is created via POST /v1/beneficiaries.
beneficiary.updatedA beneficiary is edited, archived, restored, or has its PII updated.
beneficiary.deletedA beneficiary is deleted. Payload contains only the id.
beneficiary.blockedA beneficiary is blocked (e.g. by compliance).
The payload’s data field contains the beneficiary resource. Sensitive fields (BT token references, fingerprints, deletion timestamps) are never included.

beneficiary.created

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UB1",
  "type": "beneficiary.created",
  "created_at": "2026-04-15T12:00:00Z",
  "data": {
    "id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "type": "individual",
    "status": "active",
    "display_name": "Jordan Rivera",
    "country": "DE",
    "external_ref": "crm-id-8814",
    "metadata": {
      "cost_center": "engineering"
    },
    "created_at": "2026-04-15T12:00:00Z",
    "updated_at": "2026-04-15T12:00:00Z"
  }
}

beneficiary.updated, beneficiary.blocked

Same shape as beneficiary.created with the event type and resource state reflecting the new values.

beneficiary.deleted

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UB4",
  "type": "beneficiary.deleted",
  "created_at": "2026-04-15T12:30:00Z",
  "data": {
    "id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1"
  }
}

Instrument events

Event typeFires when
instrument.createdAn instrument is attached to a beneficiary via POST /v1/beneficiaries/{id}/instruments.
instrument.updatedAn instrument is edited.
instrument.deletedAn instrument is deleted. Payload contains only the id.

instrument.created

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UC1",
  "type": "instrument.created",
  "created_at": "2026-04-15T12:05:00Z",
  "data": {
    "id": "ins_01HX8Z9K0M2N3P4Q5R6S7T8UA2",
    "beneficiary_id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "method": "iban",
    "currency": "EUR",
    "country": "DE",
    "label": "Commerzbank EUR",
    "display_last4": "0194",
    "display_bank": "Commerzbank",
    "masked_account": "DE89 **** **** **** **** 0194",
    "status": "active",
    "is_default": true,
    "created_at": "2026-04-15T12:05:00Z",
    "updated_at": "2026-04-15T12:05:00Z"
  }
}
Credential tokens and fingerprints are never included — raw instrument credentials live only in the Basis Theory vault.

instrument.updated

Same shape as instrument.created with the new values reflected.

instrument.deleted

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UC4",
  "type": "instrument.deleted",
  "created_at": "2026-04-15T12:45:00Z",
  "data": {
    "id": "ins_01HX8Z9K0M2N3P4Q5R6S7T8UA2"
  }
}

Batch events

Event typeFires when
batch.uploadedA CSV or XLSX batch file is accepted by POST /v1/batches. Fires before validation begins.
batch.completedA batch finished processing with at least one successful row (includes partial success).
batch.failedA batch failed terminally — no rows could be processed, or every row failed.

batch.uploaded

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UE2",
  "type": "batch.uploaded",
  "created_at": "2026-04-15T15:10:00Z",
  "data": {
    "id": "batch_01HX8Z9K0M2N3P4Q5R6S7T8UF1",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "uploaded",
    "file_name": "payouts-2026-04-15.csv",
    "file_size": 48221,
    "format": "csv",
    "uploaded_at": "2026-04-15T15:10:00Z",
    "uploaded_by": "usr_01HX8Z9K0M2N3P4Q5R6S7T8UG1"
  }
}

batch.completed

Fires when processing finishes with status of completed (all rows succeeded) or partial (some succeeded, some failed). Branch on the payload status field to distinguish full from partial success.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UE3",
  "type": "batch.completed",
  "created_at": "2026-04-15T15:18:00Z",
  "data": {
    "id": "batch_01HX8Z9K0M2N3P4Q5R6S7T8UF1",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "completed",
    "file_name": "payouts-2026-04-15.csv",
    "format": "csv",
    "total_rows": 120,
    "processed_rows": 120,
    "valid_rows": 120,
    "invalid_rows": 0,
    "skipped_rows": 0,
    "created_at": "2026-04-15T15:10:00Z",
    "completed_at": "2026-04-15T15:18:00Z"
  }
}

batch.failed

Fires when a batch finishes with status = failed — either the file could not be processed at all (download/parse failure, empty file) or every row failed during processing. The payload includes an errors array describing the row-level failures when available.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UE4",
  "type": "batch.failed",
  "created_at": "2026-04-15T15:18:00Z",
  "data": {
    "id": "batch_01HX8Z9K0M2N3P4Q5R6S7T8UF1",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "status": "failed",
    "file_name": "payouts-2026-04-15.csv",
    "format": "csv",
    "total_rows": 120,
    "processed_rows": 0,
    "valid_rows": 0,
    "invalid_rows": 120,
    "skipped_rows": 0,
    "created_at": "2026-04-15T15:10:00Z",
    "completed_at": "2026-04-15T15:18:00Z",
    "errors": [
      { "row": 2, "field": "currency", "message": "unsupported currency: XYZ" }
    ]
  }
}

FX events

Event typeFires when
fx.quote.createdAn FX quote is locked (binding commitment). Indicative quotes do not fire an event.
fx.exchange.createdAn FX exchange is executed and is pending settlement.
fx.exchange.completedThe exchange has settled.
fx.exchange.failedThe exchange failed to settle, or was stuck in pending beyond the retry budget.

fx.quote.created

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UD1",
  "type": "fx.quote.created",
  "created_at": "2026-04-15T13:00:00Z",
  "data": {
    "id": "fxq_01HX8Z9K0M2N3P4Q5R6S7T8UD2",
    "sell_currency": "GBP",
    "buy_currency": "EUR",
    "sell_amount": "1000.00",
    "buy_amount": "1173.40",
    "customer_rate": "1.17340",
    "locked": true,
    "expires_at": "2026-04-15T13:05:00Z"
  }
}

fx.exchange.created, fx.exchange.completed, fx.exchange.failed

{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UD3",
  "type": "fx.exchange.completed",
  "created_at": "2026-04-15T13:01:12Z",
  "data": {
    "quote_id": "fxq_01HX8Z9K0M2N3P4Q5R6S7T8UD2",
    "sell_currency": "GBP",
    "buy_currency": "EUR",
    "sell_amount": "1000.00",
    "buy_amount": "1173.40",
    "mid_market_rate": "1.17450",
    "customer_rate": "1.17340",
    "provider": "openpayd",
    "provider_ref": "OP-FX-2026-04-15-41102",
    "status": "completed"
  }
}
A fx.exchange.failed event fired by the status-polling worker (for exchanges that get stuck in pending) uses a compact payload:
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UD4",
  "type": "fx.exchange.failed",
  "created_at": "2026-04-15T13:11:00Z",
  "data": {
    "id": "fxe_01HX8Z9K0M2N3P4Q5R6S7T8UD5",
    "sell_currency": "GBP",
    "buy_currency": "EUR",
    "sell_amount": "1000.00",
    "buy_amount": "1173.40",
    "status": "failed",
    "reason": "exchange stuck in pending state"
  }
}

Funding events

funding.credit

Fires when incoming funds are received on a merchant account and credited to the internal balance. The payload carries the masked sender name only — Anton never forwards raw counterparty PII to webhook subscribers.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UF2",
  "type": "funding.credit",
  "created_at": "2026-04-15T16:00:00Z",
  "data": {
    "id": "opd_01HX8Z9K0M2N3P4Q5R6S7T8UF3",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "amount": "10000.00",
    "currency": "USD",
    "sender_name": "J*** S****",
    "account_id": "opa_01HX8Z9K0M2N3P4Q5R6S7T8UF4",
    "reference": "Invoice top-up",
    "source_provider": "openpayd",
    "credited_at": "2026-04-15T16:00:00Z"
  }
}

Compliance events

screening.hit

Fires when compliance screening returns a possible match that requires manual review. The payload intentionally omits the matched sanctions-list entry content — Anton treats that information as compliance-sensitive and does not surface match details to merchants to avoid tipping-off risk. Subscribers should treat this event as a signal that a review case is open for the payout; the payout itself transitions to manual_review.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UF5",
  "type": "screening.hit",
  "created_at": "2026-04-15T14:30:04Z",
  "data": {
    "payout_id": "pay_01HX8Z9K0M2N3P4Q5R6S7T8UAB",
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "beneficiary_id": "ben_01HX8Z9K0M2N3P4Q5R6S7T8UA1",
    "screening_id": "scr_01HX8Z9K0M2N3P4Q5R6S7T8UF6",
    "highest_score": 0.72,
    "review_id": "rev_01HX8Z9K0M2N3P4Q5R6S7T8UF7",
    "status": "manual_review"
  }
}

Balance events

balance.low

Fires when a merchant’s available balance drops below a configured low-balance threshold. Thresholds are configured per (merchant, currency) pair — one threshold per pair — either in the merchant dashboard or via the Balance Alerts API. The event uses latching semantics to prevent spam while the balance oscillates around the threshold:
  • Fires once when the available balance transitions from at-or-above the threshold to below it.
  • Does not fire again while the balance remains below the threshold.
  • Re-arms automatically when the balance recovers to at-or-above the threshold. No recovery event is emitted.
  • The next dip below the threshold fires again.
Delivery is best-effort and runs asynchronously from the balance mutation. A webhook failure never blocks the underlying payout, funding credit, or FX exchange.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UG1",
  "type": "balance.low",
  "created_at": "2026-04-15T17:22:00Z",
  "data": {
    "merchant_id": "mer_01HX8Z9K0M2N3P4Q5R6S7T8UZZ",
    "currency": "USD",
    "balance_amount": "87.42",
    "threshold_amount": "100.00",
    "latched_at": "2026-04-15T17:22:00Z"
  }
}

Test events

test

Fired by POST /v1/webhooks/{id}/test to help you verify your endpoint is reachable and your signature verification works. The payload is a synthetic example with hard-coded identifiers — do not persist or act on it.
{
  "id": "evt_01HX8Z9K0M2N3P4Q5R6S7T8UE1",
  "type": "test",
  "created_at": "2026-04-15T15:00:00Z",
  "data": {
    "test": true,
    "event_type": "test",
    "object": "payout",
    "id": "pay_test000000000000001",
    "status": "completed",
    "source_amount": "100.00",
    "source_currency": "USD",
    "dest_amount": "85.00",
    "dest_currency": "EUR",
    "created_at": "2026-04-15T15:00:00Z"
  }
}

Subscribing to events

Create a subscription with the list of event types you want to receive. The signing secret is returned once, at creation time — store it securely.
The url must use https:// and must be publicly reachable from the internet. Anton rejects plaintext http:// endpoints, loopback addresses, RFC1918 private ranges, .local / .internal hostnames, and cloud metadata endpoints — these are refused with webhook_url_not_https or webhook_url_private_address. Plaintext http:// is only accepted against a local development API. See Errors → Webhook subscriptions.
curl https://api.antonpayments.dev/v1/webhooks \
  -X POST \
  -H "Authorization: Bearer ak_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/anton",
    "events": ["payout.created", "payout.completed", "payout.failed"]
  }'
Inspect past deliveries with GET /v1/webhooks/events, GET /v1/webhooks/events/{id}, and GET /v1/webhooks/events/{id}/deliveries. See Webhook Signing for verification.