# Webhooks & retries

Server-side integrations with explicit payment confirmation and safe retries.

## Register a receiver

Set your website and callback URL when creating an API key. The callback must use the same public HTTPS origin, port 443, and no redirect. Atlas Pay sends JSON by POST with an `Atlas-Signature` header. Copy the signing secret once and keep it server-side.

Events include `payment.risk_updated`, `checkout.updated`, `refund.updated`, `order.paid`, `order.payment_updated`, `order.fulfilled`, `order.risk_blocked` and `order.risk_review`. Risk events stop dispatch or fulfillment until the current order permits it. Each contains `id`, `type`, `created_at`, `livemode`, and `data`. The data object follows the corresponding resource schema in OpenAPI. Order events require the catalog/order release and migrations; see [Products & orders](https://dashboard.atlaspay.sh/docs/products).

## Verify the raw body

Compute HMAC-SHA256 over `timestamp + "." + rawBody` using the signing secret. Require a timestamp within five minutes and compare the signature in constant time. Do not parse and reserialize the JSON before checking it.

```javascript
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(rawBody, signature, secret) {
  const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(signature || '');
  if (!match || !secret) return false;
  if (Math.abs(Date.now() / 1000 - Number(match[1])) > 300) return false;
  const expected = createHmac('sha256', secret)
    .update(match[1] + '.' + rawBody).digest();
  return timingSafeEqual(expected, Buffer.from(match[2], 'hex'));
}
```

## Apply updates safely

1.  Verify the signature, payload size, and test/live mode. Reject invalid requests.
2.  Deduplicate by event ID using durable storage. Events can be delivered more than once or out of order.
3.  Retrieve the current checkout, order, or refund from Atlas Pay. Match reference, items, amount, currency and mode. For `on_delivery` catalog orders, require a fresh `authorized` status, known unexpired `capture_before`, no settlement action, and no refund/dispute before dispatching. Capture follows trusted delivery confirmation; `order.delivered` is not proof of captured payment. For legacy `automatic` orders and standalone checkouts, require verified paid status first. Do not allow an older event to regress payment state or trigger duplicate dispatch. Read [Delivery & payment holds](https://dashboard.atlaspay.sh/docs/delivery).
4.  Persist the update and deduplication record, then return a 2xx response. If processing fails, return a non-2xx response so the delivery can retry.

## Delivery & recovery

Verified Stripe events are stored in Cloudflare D1 before callback delivery. A scheduled worker checks the outbox once per minute, processing up to 20 deliveries per run. Requests time out after eight seconds. Failed deliveries retry up to 12 attempts with exponential backoff capped at six hours. Delivery is at least once, not exactly once, and callback latency increases with backlog.

For recovery, read `GET /events?after=0`. Each page contains up to 100 events, a monotonically increasing `sequence`, `next_cursor`, and `has_more`. Persist the cursor only after processing the page. Sequence gaps are normal. The feed is workspace/mode scoped, including events from older keys. Retrieve current resource status before applying a recovered event.

Key expiration blocks API access but does not stop already-configured callbacks. Revocation stops both API access and that key’s callback delivery. For rotation, install the replacement key, reconcile old pending checkouts/refunds with the event feed, then revoke the old key. A single-secret plugin cannot verify both callback secrets simultaneously, so use a maintenance window.

> This preview does not yet include a callback-delivery dashboard or a one-click replay control. Your integration must monitor failures and implement event-feed recovery before a live launch.

## Standalone payment risk updates

`payment.risk_updated` contains `payment_id`, `risk_decision` and `fulfillment_allowed`. It is sent to the API key that created the associated checkout, using the same signed outbox and durable event feed. It can arrive after checkout completion. Stop fulfillment for `review` or `block`; retrieve the current checkout and verify `status=paid`, `risk_decision=allow`, `fulfillment_allowed=true` and your expected order details before fulfilling. A callback is a snapshot and may arrive late or out of order. Subscriptions and payment links without an API checkout association appear in the merchant dashboard and private admin alerts; this event does not add a subscription entitlement webhook.

---

Source: [Atlas Pay — Webhooks & retries](https://dashboard.atlaspay.sh/docs/webhooks)

Prepared by Kyle Campbell
