What it is
The Atlas Partner API is a single intake gate. Partners push three kinds of data to Atlas:
- Invoices — what a partner charges Atlas for the merchants Atlas manages.
- Fee statements — the transaction-level breakdown behind those invoice lines.
- Events — operational signals, such as a failed direct debit or a terminal swap.
Atlas reviews, maps every line to the correct merchant, and issues one monthly invoice per merchant under the Atlas brand. Nothing a partner submits is paid or booked automatically — every submission is stored immutably, acknowledged with an intake id, and reviewed by Atlas before anything is recorded in accounting.
Who it's for
- Payment providers
- POS vendors
- Hardware suppliers
In short: anyone Atlas manages a merchant relationship through, who needs to bill Atlas for that merchant's terminals, subscriptions, transaction fees or hardware.
How onboarding works
- Request partner access via info@atlascorporation.org.
- Receive a sandbox key (atp_test_…) and a webhook signing secret.
- Send a test invoice against the sandbox to confirm your integration maps merchants and totals correctly.
- Atlas verifies your sandbox submissions.
- Atlas issues a live key (atp_live_…).
Quickstart
Three calls to get oriented: who you are, submitting an invoice, and checking what happened to it.
1. Identify your key
bash
curl https://atlascorporation.org/api/partner/v1/me \
-H "Authorization: Bearer atp_test_xxxxxxxxxxxxxxxx"json response
{
"partner": "example-partner",
"mode": "test",
"scopes": ["invoices", "fee_statements", "events", "merchants.read"]
}2. Submit an invoice (JSON)
Itemize per merchant: each line carries atlas_merchant_ref (or your own partner_merchant_ref / terminal_id), a VAT rate, and a line total. Transaction-fee lines also carry transaction_count.
bash
curl -X POST https://atlascorporation.org/api/partner/v1/invoices \
-H "Authorization: Bearer atp_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: INV-2026-00042" \
-d '{
"document_type": "invoice",
"number": "INV-2026-00042",
"issue_date": "2026-09-01",
"due_date": "2026-09-30",
"currency": "EUR",
"supplier": {
"name": "Example Partner B.V.",
"vat_number": "NL000099998B01",
"kvk": "12345678"
},
"payment": { "method": "direct_debit", "reference": "INV-2026-00042" },
"lines": [
{
"atlas_merchant_ref": "am_7K2QH9",
"terminal_id": "50330000",
"category": "terminal_rental",
"description": "Terminal rental - Voorbeeld Restaurant",
"quantity": "1",
"unit_price": "29.00",
"vat_rate": "21",
"line_total": "29.00",
"period_start": "2026-09-01",
"period_end": "2026-09-30"
},
{
"atlas_merchant_ref": "am_7K2QH9",
"terminal_id": "50330000",
"category": "transaction_fee",
"description": "Debit consumer domestic - transaction fee",
"quantity": "412",
"unit_price": "0.02900",
"vat_rate": "21",
"line_total": "11.95",
"period_start": "2026-09-01",
"period_end": "2026-09-30",
"transaction_count": 412
}
],
"totals": { "net": "40.95", "vat": "8.60", "gross": "49.55" }
}'json response · 202
{
"intake_id": "itk_01J9Z8Q4K2V7",
"kind": "invoice",
"mode": "test",
"status": "received",
"received_at": "2026-09-01T10:03:00Z",
"sha256": "…"
}3. Check intake status
bash
curl https://atlascorporation.org/api/partner/v1/intake/itk_01J9Z8Q4K2V7 \
-H "Authorization: Bearer atp_test_xxxxxxxxxxxxxxxx"Authentication
Every request (except /health) carries a bearer key:
Keys are issued by Atlas per partner, shown once, and stored hashed on our side — Atlas cannot show you a key again after issuance. If a key is lost or compromised, request a rotation from Atlas; the old key is revoked once the new one is confirmed working.
Idempotency
Every write (POST /invoices, POST /fee-statements, POST /events) requires an Idempotency-Key header — up to 128 characters, retained for 30 days. Reuse the same key with the same body and you get back the original result. Reuse the same key with a different body and Atlas returns 409 Conflict. A natural choice is your own document or event id.
Merchant references
Three identifiers can appear on a line, and at least one is required so Atlas can route the cost to the right merchant (omit all three only for Atlas's own costs):
If you don't know your Atlas-side reference yet, call GET /merchants with your own identifiers and Atlas returns the mapping.
| Field | Meaning |
|---|---|
| atlas_merchant_ref | Atlas's own reference for the merchant, e.g. am_7K2QH9. Preferred — use it whene |
| partner_merchant_ref | Your own merchant identifier, e.g. m_12345. Atlas maps this to an atlas_merchant |
| terminal_id | The physical terminal's serial, e.g. 50330000. Useful when billing is per-device |
Invoices
POST /invoices accepts three transports:
- JSON (Atlas schema) — shown in the quickstart above.
- UBL 2.1 / Peppol BIS Billing 3.0 XML — the preferred format, sent as application/xml.
- multipart/form-data — a document part (UBL XML or Atlas JSON) plus an optional pdf part (max 10 MB) for a human-readable copy.
Every line should carry a merchant reference (see above). Totals (net, vat, gross) must reconcile with the sum of the lines, or the submission lands in needs_review with a totals_mismatch issue.
Line totals & VAT, precisely
line.line_total is the NET (excl. VAT) amount for that line: quantity × unit_price, rounded half-up to 2 decimals. It is the authoritative figure Atlas validates against — Atlas does not re-derive it from quantity and unit_price, because a displayed unit_price may itself be rounded (e.g. a per-transaction fee shown at 2 decimals but actually billed at a finer precision). Send the exact net amount in line_total.
VAT is calculated per rate, on the sum of line_total for every line that shares that vat_rate — never line-by-line. That per-rate VAT is compared to totals.vat within a 0.01 tolerance (vat_mismatch if it doesn't reconcile).
Optionally, submit totals.vat_breakdown — one {rate, base, vat} row per VAT rate present on the invoice:
Each row is checked against the lines: base must equal the sum of line_total for lines at that rate, and vat must equal base × rate / 100 (rounded half-up, 0 for exempt/reverse_charge/0). A mismatch never rejects the submission — it's stored with issue code vat_breakdown_mismatch and status needs_review.
Credit notes (document_type: "credit_note") must include credits_invoice_number — the number of the invoice being credited. Without it, the credit note is stored but flagged needs_review with issue code missing_credits_reference, since Atlas can't apply it automatically. Credit note lines and totals carry negative amounts; a mixed sign between the lines and totals.gross is flagged as sign_mismatch.
UBL / multipart example
Fee statements
POST /fee-statements submits the per-merchant, per-segment transaction breakdown behind an invoice's transaction-fee lines — informational, not a payment instruction. Segments look like:
Known segments: debit_consumer_domestic, debit_consumer_non_domestic, credit_consumer_eu, credit_consumer_non_eu, commercial, other.
Events & signature verification
POST /events is how Atlas receives your webhook calls once you've registered a target URL with us. Every event you send must include an Atlas-Signature header:
The signature is HMAC-SHA256 of the string "{t}.{body}" (timestamp, a literal dot, then the raw request body), computed with your webhook secret. Atlas rejects signatures where t is more than 300 seconds from current time, to guard against replay.
Verify in Python
Verify in Node
Known event types: merchant.onboarded, merchant.ended, terminal.activated, terminal.deactivated, terminal.swapped, payment.direct_debit_failed, invoice.reminder, chargeback.created, settlement.paid. Other types are accepted and stored.
Intake statuses
Every submission (invoice, fee statement or event) returns an intake_id and moves through one status:
Poll GET /intake/{intake_id} to follow a submission through these states.
Errors
Errors follow RFC 9457 (problem+json):
| Status | Meaning |
|---|---|
| 401 | Missing, invalid or revoked key, or a bad event signature. |
| 403 | Authenticated, but the key lacks the scope this endpoint requires (e.g. a mercha |
| 409 | Same Idempotency-Key with a different body (idempotency-conflict); an invoice nu |
| 413 | Payload too large (e.g. a PDF over 10 MB). |
| 422 | Invalid request shape (schema validation failed) — see errors[] for the offendin |
| 429 | Rate limited — default 60 requests/min per key. Respect Retry-After. |
Sandbox vs. live
Sandbox submissions are fully isolated — nothing you send with an atp_test_ key is ever booked, mixed with live data, or seen by a merchant.
| Sandbox | Live | |
|---|---|---|
| Key prefix | atp_test_ | atp_live_ |
| Reaches Atlas accounting | Never | Yes, after Atlas review |
| Issued | Step 2 of onboarding | After Atlas verifies your sandbox submissions |
Versioning & changelog
The API is versioned in the URL path (/api/partner/v1/...). Breaking changes get a new path version; additive changes (new optional fields, new event or error types) land in place and are listed here.
| Version | Date | Notes |
|---|---|---|
| v1.0.1 | 2026-09-17 | Additive: optional totals.vat_breakdown (per-rate reconciliation, issue code vat |
| v1.0.0 | 2026-09-17 | Initial public release: invoices (JSON / UBL 2.1 Peppol BIS 3 / multipart), fee |
Security & data
- EU-hosted. Atlas's infrastructure runs in the EU.
- TLS in transit. All API traffic is over HTTPS.
- Keys stored hashed. Atlas cannot retrieve or display a partner key after it is issued.
- Nothing is auto-paid. A submission being validated or booked_draft is not a payment instruction — Atlas reviews before anything is booked or paid.
We don't publish uptime figures or service-level commitments on this page, and we don't describe this integration as "certified" by any scheme — if you need contractual terms, raise it with your Atlas contact.