# API reference

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

## Base URL & authentication

```text
https://dashboard.atlaspay.sh/api/v1
Authorization: Bearer <your-secret-key>
Content-Type: application/json
```

Use your own backend. CORS is not enabled for arbitrary storefront browsers. Every key is bound to one workspace, test/live mode, website origin, permission set, and expiry date. Workspace owners manage keys in Developers. Choose test or live mode in Settings before creating a key. The key fixes the API environment; browser preferences cannot change it. Complete Stripe onboarding separately in live mode before creating real payments. API keys are stored as SHA-256 hashes; callback secrets are encrypted at rest.

Keys are workspace-scoped, not order- or customer-scoped. A key with read permission can read that workspace’s resources in the same mode, including resources created by another key. Never give a key to a shopper.

## Endpoints

| Request | Permission |
| --- | --- |
| `GET /account` | `account:read` |
| `GET /capabilities` | `account:read` |
| `GET /products`, `GET /products/{id}` | `products:read` |
| `POST /products`, `POST /products/import`, `PUT /products/{id}` | `products:write` |
| `GET /orders`, `GET /orders/{id}` | `orders:read` |
| `POST /orders`, `POST /orders/{id}/fulfillment` | `orders:write` |
| `POST /orders/{id}/delivery` | `orders:deliver` |
| `POST /orders/{id}/cancel` | `orders:cancel` |
| `POST /checkouts` | `checkouts:write` |
| `GET /checkouts/{id}` | `checkouts:read` |
| `POST /refunds` | `refunds:write` |
| `GET /refunds/{id}` | `refunds:read` |
| `GET /events?after=0` | `events:read` |

[Download the complete OpenAPI 3.1 contract](https://dashboard.atlaspay.sh/openapi.json) for response schemas, required fields, limits, and errors.

For a managed catalog, CSV import and server-priced orders, read [Products & orders](https://dashboard.atlaspay.sh/docs/products). New catalog orders use [capture on delivery](https://dashboard.atlaspay.sh/docs/delivery), with separate delivery/cancellation scopes and explicit idempotency. `/fulfillment` is only for legacy automatic-capture orders. The existing `/checkouts` flow remains available for stores that own their catalog and order totals; its immediate-capture behavior is unchanged. [Buyer wallet](https://dashboard.atlaspay.sh/docs/wallet) operations use buyer sessions, not merchant keys.

## Create a checkout

```bash
curl https://dashboard.atlaspay.sh/api/v1/checkouts \
  -H "Authorization: Bearer $ATLAS_PAY_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-1042-checkout-v1' \
  -d '{
    "amount": 2500,
    "currency": "USD",
    "reference": "order-1042",
    "description": "Order 1042",
    "success_url": "https://shop.example.com/thank-you",
    "cancel_url": "https://shop.example.com/cart"
  }'
```

Replace the example domain with the website registered to your key. Both return URLs must have that exact HTTPS origin, including any subdomain. An optional `customer_email` prefills checkout. `ui_mode` defaults to `hosted`; use `elements` for [embedded payment fields](https://dashboard.atlaspay.sh/docs/payment-elements). `adaptive_pricing` defaults to true for eligible sessions; set false for fixed-currency checkout. See [currency eligibility and FX rates](https://dashboard.atlaspay.sh/docs/currencies).

`amount` is an integer from 1 to 99,999,999 in Stripe charge units, not a decimal price. USD 25.00 is 2500; JPY 2500 is 2500. ISK and UGX use two-decimal API representation but require a multiple of 100. Supported three-decimal currencies require a multiple of 10. Consult `/capabilities`; processor minimums and limits still apply.

Hosted creation returns a redirect URL. Elements creation returns `client_secret` and `account_id` only while the session is open. Never log or cache those creation responses; pass them only to the buyer authorized to pay the order. Reads and webhooks exclude secrets.

The 201 response contains `data.id`, `data.url`, `data.reference`, `data.amount`, `data.currency`, `data.livemode`, and `data.status`. It also includes `ui_mode` and nullable `presentment` (buyer amount/currency and receipt-only effective conversion rate). `amount`/`currency` remain the original integration price for verification and refunds. Persist the checkout ID against your order before showing checkout. Sessions expire after approximately 24 hours. A completed session may no longer have a URL.

## Verify before fulfillment

Read `GET /checkouts/{id}` on your server. Check the order reference, amount, currency, and mode. Fulfill only when `status = paid`, `risk_decision = allow`, and `fulfillment_allowed = true`. Other status values are `pending`, `expired`, `failed`, `review`, and `blocked`. A risk hold can coexist with `payment_status = paid`; the money has not automatically been refunded. An asynchronous payment may remain pending after the customer returns.

> A success URL, a checkout ID supplied by a browser, or a checkout-created response is not proof of payment.

## Issue a refund

```bash
curl https://dashboard.atlaspay.sh/api/v1/refunds \
  -H "Authorization: Bearer $ATLAS_PAY_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-1042-refund-1' \
  -d '{"checkout_id":"cs_test_REPLACE_ME","amount":500,"reference":"refund-1042-1"}'
```

A refund applies only to a checkout with `payment_status = paid` belonging to the same workspace and mode, including one under a risk hold. A risk decision does not issue a refund by itself. Each distinct partial refund needs a new reference and idempotency key. Retry that refund with the same values. Stripe enforces the remaining refundable balance.

Read `GET /refunds/{id}` for current status. Only `succeeded` is completed; `pending` and `requires_action` are unresolved. `failed` or `canceled` require reconciliation. Refunds created directly in Stripe are outside this merchant API’s refund registry.

## Retries, limits & errors

Creating a checkout, order, or refund requires an `Idempotency-Key` with 8–200 visible ASCII characters. Reuse the exact same key and normalized body after a timeout, 429, or 5xx. Keys are scoped by workspace, mode, and operation type. Reusing one with a different body returns 409. Completed operations replay their existing resource; unresolved operations older than 23 hours require manual processor reconciliation before a new attempt. Product creation/import use unique SKUs, edits require `expected_version`, and fulfillment uses an idempotent order transition instead of this header.

The service allows up to 120 requests per minute per key per Cloudflare location. It is not a globally precise counter. On 429 honor `Retry-After`; use exponential backoff with jitter for transient failures. Bodies are limited to 16 KiB, except product imports at 256 KiB.

```json
{
  "error": { "code": "IDEMPOTENCY_CONFLICT", "message": "…" },
  "request_id": "req_…"
}
```

400 means invalid input or a processor rejection; 401 invalid/expired credentials; 403 insufficient permission; 404 a missing or inaccessible resource; 409 a state conflict; 429 a rate limit; 502/503 temporary service/configuration problems. Include the response’s request ID in support reports, never the key or card details.

---

Source: [Atlas Pay — API reference](https://dashboard.atlaspay.sh/docs/api)

Prepared by Kyle Campbell
