# Atlas Pay: authorization and delivery

> Deployment status updated 27 September 2026. See [Testing & launch](https://dashboard.atlaspay.sh/docs/testing) for verified checks and remaining acceptance work. Email sending is disabled.

Prepared by Kyle Campbell

The API and matching checkout code are deployed, with database migrations through 0022 applied. Dedicated test/live card-only configurations are installed. Stripe test authorization/capture was checked during the September 9 acceptance; actual carrier integration and live payouts remain separate acceptance work.

## The marketplace flow

Merchants onboard and add or import products. Atlas customers review an order and authorize its full total on Stripe. A separate, trusted delivery service fulfills the order. Atlas Pay captures the authorized funds only after that service confirms actual delivery.

```text
1. Merchant       -> Atlas Pay     : Create a priced product order
2. Buyer          -> Stripe        : Approve the authorization
3. Stripe         -> Atlas Pay     : Signed authorization event
4. Atlas Pay      -> Merchant      : order.authorized
5. Merchant       -> Delivery bot  : Dispatch once after a fresh order check
6. Delivery bot   -> Atlas Pay     : Confirm actual delivery
7. Atlas Pay      -> Stripe        : Capture the existing authorization
8. Atlas Pay      -> Merchant      : order.paid after processor verification

Before delivery:
Merchant          -> Atlas Pay     : Cancel the authorized order
Atlas Pay         -> Stripe        : Cancel the PaymentIntent, releasing its hold
```

This is authorization and capture, not escrow, an indefinite hold, or an instant bank payout. A hold can reduce a buyer's available balance. Issuers control when a released hold disappears. The actual card authorization deadline comes from Stripe's `capture_before`; Atlas never assumes a fixed seven-day window. See [Stripe's authorization guidance](https://docs.stripe.com/payments/place-a-hold-on-a-payment-method).

## Scope and compatibility

New orders created through `POST /api/v1/orders`, including `atlas_wallet` orders, use `capture_policy: on_delivery`. This currently applies to every new catalog order, including digital/service SKUs; its fulfillment integration must attest actual delivery or service completion. There is no automatic-capture override in the request body.

Existing orders retain `capture_policy: automatic`. Their fulfillment/refund flow is unchanged. Standalone `/checkouts`, payment links, WordPress/Wix gateway checkouts, and subscriptions are not switched to manual capture. Subscriptions continue using Stripe Billing. This change does not provide public product discovery, inventory reservations, delivery quotes, tracking updates, carrier proof verification, or a deployed delivery bot.

## Dispatch safely

Subscribe the order-creating key's callback to signed Atlas events, or consume `GET /events`. `order.authorized` is the dispatch signal for this policy, **not proof of captured payment**. Retrieve `GET /orders/{id}` before dispatching. Check the immutable reference/items/total/currency, mode, permitted shipping address, `capture_policy = on_delivery`, `payment_status = authorized`, `amount_refunded = 0`, and `settlement_action = null`. Confirm delivery is realistically possible before `capture_before`, with an operational safety margin. Deduplicate shipments by order ID in your own durable storage.

An old event is not current authorization. Never dispatch from a list snapshot alone or from an old authorization event. Existing automatic-capture orders still require a verified paid, undisputed, unrefunded payment before fulfillment. Neither a hold nor a captured payment eliminates later disputes.

## Delivery confirmation API

Use a dedicated server-side Atlas Pay key with `orders:deliver` and, for reads, `orders:read`. Keys are scoped to one merchant workspace and test/live mode. A delivery key can attest any order in that scope, so never expose it in the Atlas client or an AI prompt. Grant `orders:cancel` separately if cancellation is needed. Existing `orders:write` keys gain no capture permission automatically.

```text
POST /api/v1/orders/{id}/delivery
Authorization: Bearer <dedicated-delivery-service-key>
Idempotency-Key: delivery-order-1042-v1
Content-Type: application/json
```

```json
{
  "status": "delivered",
  "delivery_reference": "delivery-1042",
  "delivered_at": "2026-09-07T18:30:00.000Z"
}
```

Use your real order ID, delivery reference and delivery timestamp. References contain 1–120 letters, digits, underscores or hyphens. The timestamp must follow order creation; at most one minute of future clock skew is tolerated. The endpoint authenticates the service and validates payment ownership, but **does not independently prove that a package arrived**. The delivery service must validate its carrier callbacks, buyer confirmation or other agreed evidence before calling it. A free-text “delivered” chat message is not sufficient.

Atlas atomically records the delivery decision and actor before requesting capture of the entire stored order total, on its original connected account. Caller-supplied amounts, partial capture, additional charges and raw card fields are rejected. `fulfillment_status: delivered` records the trusted service's attestation, independently of payment status. A 200 response may still show `processing`; only `payment_status: paid` plus a verified captured charge means capture was confirmed. A failed capture can leave a delivered but unpaid order requiring reconciliation.

Retry with the identical body and Idempotency-Key, including the original timestamp. A changed payload/key or competing cancellation returns 409. The stable Stripe operation and stored decision recover uncertain responses without creating another payment. Unresolved actions older than 23 hours require operator review. There is no timer-driven capture retry: the trusted caller retries the same action after inspecting the order.

## Cancellation and expired holds

```text
POST /api/v1/orders/{id}/cancel
Authorization: Bearer <server-key-with-orders:cancel>
Idempotency-Key: cancel-order-1042-v1
Content-Type: application/json
```

```json
{ "reason": "customer_requested" }
```

Other reasons: `merchant_unavailable`, `delivery_failed`. This endpoint releases an existing uncaptured authorization; it is not an unpaid-checkout abandonment endpoint or a refund endpoint. `payment_status: canceled` confirms the processor canceled it. A cancellation and delivery confirmation cannot both claim the same order. After a delivery decision is accepted, an uncertain/failed capture needs reconciliation, not an opposing cancellation request. Refund an already captured payment through the existing refund process after reviewing it.

If the deadline passes, Atlas blocks capture and emits `order.authorization_expired`. That status may initially mean the deadline has passed while Stripe has not yet reported cancellation; it does not promise the buyer's bank balance has already updated. Unknown deadlines or non-card authorizations enter `authorization_review`, never automatic dispatch. Such uncaptured holds can still be canceled. No expired authorization is automatically extended or replaced with a new charge. A replacement order requires a fresh buyer review and authorization after reconciliation.

## Events and recovery

| Event | Meaning |
| --- | --- |
| `order.authorized` | Verified card hold with a known, unexpired deadline; not paid |
| `order.delivered` | Trusted service attested delivery; capture may still be pending |
| `order.paid` | Full captured payment verified; not an instruction to ship twice |
| `order.canceled` | Processor cancellation observed |
| `order.authorization_expired` | Authorization deadline passed; reconcile before any further action |
| `order.authorization_review` | Unsupported authorization or unknown deadline; do not dispatch |
| `order.payment_updated` | Refund/dispute observed; reconcile |

Stable event IDs and the existing signed D1 outbox prevent duplicate event creation, but delivery remains at least once and may be out of order. Callbacks go to the **order-creating key's** configured URL, not automatically to a later delivery key. Route the event to your delivery service from that backend, or use the workspace-scoped event feed. Addresses and emails are private fulfillment data: minimize access and retention and redact logs. Card details never go to the delivery service.

Stripe payment-intent events reconcile current processor state instead of trusting old snapshots. A separate five-minute job checks at most three due orders per run and never moves money. Lists expose `payment_checked_at`; they are not live Stripe reads. This small-batch reconciliation and the existing callback outbox need capacity sizing and backlog monitoring before a production launch.

## Deployment and acceptance gates

1. For a new installation, back up the database, apply all migrations in order, and deploy the Worker with its matching frontend. The Atlas Pay production database is migrated through 0022. Builds do not apply database migrations.
2. Configure a dedicated **card-only payment-method configuration usable on the connected accounts** and set `STRIPE_TEST_DELIVERY_PAYMENT_METHOD_CONFIGURATION` and `STRIPE_LIVE_DELIVERY_PAYMENT_METHOD_CONFIGURATION`. Match test/live mode and verify the inherited connected-account settings. Without a configuration ID, new product orders fail closed. The selected ID is frozen on the order for safe checkout retries. Both Atlas Pay configurations are installed; sandbox inheritance and an Elements manual-capture session were verified on September 27.
3. Use a restricted Stripe server key with required Checkout, connected-account PaymentIntent read/capture/cancel and charge-read permissions. Keep platform credentials out of bot clients. Restrict direct Stripe dashboard capture access operationally; someone with independent Stripe authority can still capture outside Atlas Pay.
4. Register `payment_intent.amount_capturable_updated`, succeeded, canceled, failed and processing events alongside the existing checkout/refund/dispute events. Verify webhook signatures and enable the added `2-59/5 * * * *` reconciliation schedule. The production Connect destinations are registered for these events; a signed sandbox event was received and persisted on September 27.
5. Connect the actual delivery bot with merchant-scoped credentials, dispatch deduplication, authentic delivery evidence, retry storage and an operator escalation path. Do not treat this API contract as an installed delivery integration.
6. Run real Stripe test-mode acceptance: authorize without capture, 3DS/decline, delivery capture, release before capture, duplicate delivery, network loss, expiry, unsupported configuration, refunds/disputes and merchant-account reconnection. Confirm actual issuer deadlines and the delivery SLA fit. No live money movement, carrier verification, load test or soak test was performed in this change.

## Automatic risk holds

Before dispatching or confirming delivery, retrieve the current order and require `risk_decision=allow`. A high-risk payment is automatically blocked; elevated risk, failed checks or missing assessments enter review. `order.risk_blocked` and `order.risk_review` are signed order events. Delivery capture rechecks processor evidence and the persisted decision. An operator review note cannot bypass these controls. An existing hold can still be canceled through the explicit cancellation endpoint; blocking alone does not cancel or refund it.

---

Source: [Atlas Pay — Delivery & payment holds](https://dashboard.atlaspay.sh/docs/delivery)

Prepared by Kyle Campbell
