Developer guide · API v1

Download .mdView raw

API reference

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

Base URL & authentication

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

RequestPermission
GET /accountaccount:read
GET /capabilitiesaccount: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}/fulfillmentorders:write
POST /orders/{id}/deliveryorders:deliver
POST /orders/{id}/cancelorders:cancel
POST /checkoutscheckouts:write
GET /checkouts/{id}checkouts:read
POST /refundsrefunds:write
GET /refunds/{id}refunds:read
GET /events?after=0events:read

Download the complete OpenAPI 3.1 contract for response schemas, required fields, limits, and errors.

For a managed catalog, CSV import and server-priced orders, read Products & orders. New catalog orders use capture on 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 operations use buyer sessions, not merchant keys.

Create a checkout

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. adaptive_pricing defaults to true for eligible sessions; set false for fixed-currency checkout. See currency eligibility and FX rates.

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

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.

{
  "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.