payment-gateway.app Docs
API Reference

Admin API

Management API — checkouts, transactions, invoices, clients, sites, providers, and organization settings.

Admin API

Base URL (canonical on the service): https://<your-admin-backend-host>/api/v1 — your deployment sets the host via ADMIN_BACKEND_DOMAIN. Self-hosted examples often use api.yourcompany.com or api.example.com; hosted production uses https://api.payment-gateway.app — Hostnames & DNS conventions.

OpenAPI / Swagger UI: https://<your-admin-backend-host>/api/v1/docs/index.html

Shorter public path (default reverse proxy): same host with /v1/.... The default reverse proxy configuration shipped with the deployment package rewrites /v1/* to /api/v1/* on {$ADMIN_BACKEND_DOMAIN} before proxying to the Admin Backend.

The Admin Backend is the primary integration surface for server-side automation. All endpoints require an API Key or JWT in the Authorization: Bearer header.

For merchant integrations, this is the API you should target. Use Organization API keys for organization-scoped automation. System API keys are intended for global administrative tasks such as platform provisioning or system-wide automation.

API Key format: sk_live_<keyId>.<secret> or sk_test_<keyId>.<secret>

The key ID and secret are dot-separated. Both parts are shown only once at creation time.

Organization API keys are already bound to a single organization. Do not send X-Organization-ID with organization API key requests; the backend resolves the organization directly from the key. JWT/session-based dashboard requests resolve organization context from the route :orgId parameter when present, otherwise from the selectedOrganization cookie maintained by the admin frontend.

[!IMPORTANT] Every endpoint enforces both role permission (Viewer, Editor, Admin) and scope (resource:action). A request fails with 403 if the API key lacks the required scope.


Authentication

POST /api/v1/auth                         # Initiate WebAuthn authentication
POST /api/v1/auth/authenticate-complete   # Complete WebAuthn authentication → JWT
POST /api/v1/auth/refresh-token           # Refresh JWT
POST /api/v1/auth/logout                  # Invalidate session
GET  /api/v1/auth/login/check             # Check if current JWT is valid

Session recovery

The dashboard uses access tokens valid for 30 minutes and renewable refresh sessions valid for 72 hours from sign-in or successful renewal. Continued use can renew the session; a closed or inactive browser may require a new passkey sign-in once its refresh session expires. The lifetime change applies to newly issued or renewed sessions and does not extend a previously issued token in place. Logout, revocation, account suspension and session-integrity checks still apply.

After the passkey is verified, the dashboard must also establish its authenticated session. If that step fails, the sign-in screen clears Completing your sign-in... and offers a retry instead of remaining stuck. A successful sign-in returns to the requested dashboard page. A session check that overlaps an explicit logout cannot restore the signed-out user.

For a reproducible failure, provide the request ID from the error details and the time to your operator. The server records a bounded validation reason against that request ID without logging token contents, device identifiers or IP addresses in the diagnostic event. Do not share cookies, passkey data or authentication tokens.

For v6.0.0 dashboard cookie sessions, serialize explicit refresh requests within each client. Only POST /api/v1/auth/refresh-token rotates refresh credentials. Protected routes and login checks return 401 when the access token is missing or expired, without changing cookies; the dashboard then refreshes explicitly and retries. Rotation consumes the previous refresh token; concurrent requests never receive the replacement credentials from that consumed token.

  • 409 with code REFRESH_IN_PROGRESS and Retry-After: 1 means another request has already rotated the session and its successor remains active. The rejected response leaves cookies unchanged. Wait for the successful request, then retry using the browser's current cookies.
  • 429 with code RATE_LIMIT_EXCEEDED is temporary. Honor Retry-After in seconds. Refresh has its own per-IP limit of 20 requests per 10-minute fixed window, separate from ordinary API traffic.
  • 503 with code RATE_LIMIT_UNAVAILABLE and Retry-After: 1 means the authentication rate-limit store is unavailable. Retry after that interval; the request does not proceed without its abuse-control check.
  • Network failures and server errors do not establish that the user has logged out. Preserve recoverable state and offer a bounded retry; avoid a tight refresh loop.
  • An invalid, expired or revoked refresh session remains terminal (401), as does a failed session-integrity check (403). A consumed token without a valid active successor cannot recover a session.

Login checks also have an independent per-IP request budget, using MPG_RATE_LIMIT_MAX_REQUESTS and MPG_RATE_LIMIT_WINDOW_SIZE (defaults: 500 requests per 5 minutes). Ordinary API traffic cannot exhaust either recovery budget.

Single-use rotation cannot recover replacement credentials from a consumed token. If a successful refresh response is lost before the browser stores its new cookies, a new interactive sign-in may be necessary. Temporary errors alone do not clear the dashboard session, but this protocol does not guarantee recovery from a lost successful response.

The dashboard keeps valid organization context during login checks, including an existing organization selected by a global administrator. This preserves direct links while backend role and organization checks remain enforced.


Checkouts

MethodPathRequired ScopeDescription
POST/api/v1/checkouts/{siteId}/createcheckout:createCreate a checkout session for a site
GET/api/v1/checkoutscheckout:readList all checkouts
GET/api/v1/checkouts/{checkoutId}checkout:readGet a checkout by checkout ID
PATCH/api/v1/checkouts/{checkoutId}checkout:updateUpdate checkout metadata

[!IMPORTANT] The {siteId} in the create path is your Site MongoDB ObjectId (from Sites → Edit). The {checkoutId} in get / patch is the checkout document id — not the site id.

Create Checkout

POST /api/v1/checkouts/{siteId}/create
Authorization: Bearer sk_live_keyid.secret
Content-Type: application/json

[!NOTE] Path examples below use /api/v1/... as registered in the Admin Backend. On {$ADMIN_BACKEND_DOMAIN} with the default Caddyfile, the same handlers are reachable at /v1/....

{
  "amount": 4900,
  "currency": "EUR",
  "email": "buyer@example.com",
  "returnUrl": "https://yourapp.com/success?txId={transactionId}",
  "cancelUrl": "https://yourapp.com/cancel",
  "ipnUrl": "https://yourapp.com/webhooks/payment-ipn",
  "externalReference": "order_12345",
  "invoiceNumber": "INV-2026-0001",
  "clientId": "507f1f77bcf86cd799439014",
  "items": [
    {
      "name": "Professional Plan — Monthly",
      "description": "Professional Plan — Monthly",
      "quantity": 1,
      "unitPrice": 4900,
      "vatRate": 19,
      "itemType": "digital_service"
    }
  }
}

[!IMPORTANT] Provide at least one of externalReference or invoiceNumber. The API accepts both, but a reference is used as matching/presentation evidence for reconciliation, never as canonical order uniqueness.

[!NOTE] {transactionId} in returnUrl/cancelUrl is a literal template placeholder — the gateway substitutes the actual transaction ID at redirect time.

Optional ipnUrl sets where the Main Backend sends outbound IPN for transactions created from this checkout; the Webhook Signing Secret on the Site is used to sign those requests.

Response 200:

{
  "id": "507f1f77bcf86cd799439011",
  "sessionPublicId": "abc123xyz789def456",
  "shortCode": "aB3xK9mw2P",
  "paymentUrl": "https://secure.payment-gateway.app/checkout/aB3xK9mw2P"
}

paymentUrl points to the checkout frontend host selected for the session: the default MAIN_FRONTEND_DOMAIN (hosted: secure.payment-gateway.app) unless provider/frontend-domain routing selects a provider-specific checkout host. See Hostnames & DNS conventions.

Response 409:

Checkout creation can return a business conflict instead of a payment URL. These responses include requestId; plugin and custom integrations should show that ID to the customer or operator for support.

CodeMeaning
CHECKOUT_BLOCKED_BY_DISPUTEAn open or under-review dispute exists for the same buyer.
CHECKOUT_BLOCKED_BY_CUSTOMER_HOLDA final merchant-loss Customer Risk Hold blocks checkout or requires merchant review.
CHECKOUT_RESTRICTED_BY_CUSTOMER_HOLDA Customer Risk Hold allows only configured safe provider types such as wire or wise.

Final dispute outcomes are not treated as unresolved disputes: lost and accepted are final merchant-loss outcomes, while won restores the non-disputed path. Customer Risk Holds are the separate policy mechanism for blocking future checkout or restricting it to bank-transfer methods.


Transactions

MethodPathRequired ScopeDescription
GET/api/v1/transactionstransaction:readList transactions (filterable)
GET/api/v1/transactions/:idtransaction:readGet transaction details
GET/api/v1/transactions/:id/taxtransaction:readGet full tax decision metadata
PUT/api/v1/transactions/:idtransaction:updateUpdate transaction metadata
DELETE/api/v1/transactions/:idtransaction:deleteDelete transaction
POST/api/v1/transactions/manualtransaction:createCreate a manual (off-platform) transaction
GET/api/v1/transactions/:id/adjustmentstransaction:readRead bounded adjustment history and eligibility
GET/api/v1/transactions/:id/adjustments/:operationIdtransaction:readRead one immutable adjustment operation
POST/api/v1/transactions/:id/refund/initiatetransaction:executeInitiate an eligible provider refund
POST/api/v1/transactions/:id/recalculate-taxtransaction:executeRe-run tax engine for one transaction

List Transactions

GET /api/v1/transactions?statuses[]=1&currency=EUR&startDate=2026-01-01&endDate=2026-03-31&limit=50&page=1

Query Parameters:

ParameterTypeDescription
statuses[]int[]Filter by status codes (-2 canceled, -1 initiated, 0 pending, 1 completed, 2 failed, 3 refunded, 4 disputed). Repeat the parameter or use comma-separated statuses
currencystringISO 4217 currency code (e.g., EUR, USD, GBP)
startDatedateStart date (YYYY-MM-DD or RFC3339)
endDatedateEnd date (YYYY-MM-DD or RFC3339)
siteIds[]string[]Filter by site IDs
providerIds[]string[]Filter by provider IDs
pageintPage number (default: 1)
limitintItems per page (default: 10, max: 100)

Refund initiation

GET /api/v1/transactions/:id/adjustments accepts a bounded limit and opaque cursor. Its capability and eligibility fields are authoritative; do not derive available refund capacity from transaction aggregates.

POST /api/v1/transactions/:id/refund/initiate requires one UUID in the canonical Idempotency-Key header. Its JSON body contains exactly amount, currency, reason, and expectedVersion. Stripe is the only provider currently supporting initiation. A successful acceptance returns the immutable operation and a direct statusPath.

GET /api/v1/transactions/:id/adjustments/:operationId?kind=refund reads that operation directly. Reuse the same header value and unchanged body after an uncertain response. Version and identity conflicts retain the original key. The explicit first-attempt REFUND_INITIATION_REJECTED or REFUND_INITIATION_UNSUPPORTED response permits correction only when it identifies the same operation and transaction and reports mutationApplied: false.

Legacy manual refund, chargeback, and financial-status mutation endpoints are not integration surfaces. Existing historical events remain readable. See Refunds and Returns for response states and recovery.


Invoices

MethodPathRequired ScopeDescription
GET/api/v1/invoicesinvoice:readList invoices
GET/api/v1/invoices/:idinvoice:readGet invoice
POST/api/v1/invoicesinvoice:createCreate draft invoice
POST/api/v1/invoices/preview-taxinvoice:createPreview canonical tax for a draft invoice payload
POST/api/v1/invoices/from-transaction/:transactionIdinvoice:createCreate invoice from transaction
PUT/api/v1/invoices/:idinvoice:updateUpdate draft invoice fields and/or line items
PATCH/api/v1/invoices/:id/notesinvoice:updateUpdate invoice notes
PUT/api/v1/invoices/:id/transactioninvoice:updateAssign a transaction to an existing invoice
POST/api/v1/invoices/:id/issueinvoice:executeIssue invoice (draft → issued)
POST/api/v1/invoices/:id/voidinvoice:executeVoid an issued invoice (Admin role required)
POST/api/v1/invoices/:id/correctinvoice:updateCorrect an invoice with audit trail / credit-note workflow
POST/api/v1/invoices/:id/send-emailinvoice:executeEmail invoice to client
POST/api/v1/invoices/:id/payment-checkoutinvoice:executeCreate an online payment checkout for the invoice
GET/api/v1/invoices/:id/pdfinvoice:executeDownload PDF
GET/api/v1/invoices/:id/xmlinvoice:executeDownload XRechnung XML
GET/api/v1/invoices/:id/xml/peppolinvoice:executeDownload Peppol BIS 3.0 XML
GET/api/v1/invoices/:id/pdf/zugferdinvoice:executeDownload ZUGFeRD PDF
POST/api/v1/invoices/:id/regenerate-pdfinvoice:executeForce PDF regeneration
DELETE/api/v1/invoices/:idinvoice:deleteDelete draft invoice

The e-invoice download endpoints enforce Settings > Invoicing > eInvoice Generation before proxying to the worker. Requests return 403 EINVOICE_DISABLED when eInvoice generation is disabled, 403 EINVOICE_FORMAT_NOT_ALLOWED when the requested format is not listed in settings.invoicing.eInvoice.allowedFormats, 409 INVOICE_NOT_ISSUED when a draft invoice requests XML or hybrid eInvoice output, and 422 EINVOICE_READINESS_FAILED when the issued invoice snapshot or organization settings are missing required readiness data such as IBAN or Peppol routing addresses. Draft invoices only support pro forma PDF download; the final invoice number is assigned during issue.

Issued invoices keep their party snapshot. To copy or correct a buyer Peppol routing address for an already issued invoice, use the normal correction endpoint. The correction updates the invoice snapshot only; it does not silently mutate the linked client record.

{
  "reason": "Copied buyer Peppol routing address from linked client for eInvoice generation",
  "changes": {
    "client": {
      "peppolRoutingAddress": {
        "schemeId": "9930",
        "value": "DE987654321"
      }
    }
  }
}

Create Invoice Payment Checkout

POST /api/v1/invoices/:id/payment-checkout creates a hosted payment link for an issued invoice.

The request body is optional:

{
  "amount": 10899,
  "expireInHours": 336,
  "returnUrl": "https://yourapp.com/success",
  "cancelUrl": "https://yourapp.com/cancel"
}
  • amount is in minor units. If omitted, the backend uses the invoice's payable outstanding amount.
  • Partial payment links are allowed from 50 minor units up to the outstanding amount.
  • Empty invoices, fully settled invoices, zero-outstanding invoices, and amounts below the minimum return 400.
  • If expireInHours is omitted, the backend uses the effective invoicing setting autoPaymentLinkExpiresInHours, falling back to 336 hours. Explicit values must be between 1 and 3120.
  • Reverse-charge, export, exempt, zero-rated, and manual synthetic invoice tax codes are not sent as checkout tax-rate codes; settlement tax allocation still follows the invoice tax snapshot.

Draft Invoice Tax Semantics

Draft invoice creation, draft invoice updates, recurring invoice generation, and transaction tax calculation all rely on the same backend tax engine and tax-rate timing rules.

  • Omit items[].taxRate to let the backend auto-calculate tax from the seller context, client billing country, client tax country fallback, item type, and the effective tax-rate schedule.
  • Provide items[].taxRate to apply an explicit manual override for that line item.
  • Use POST /api/v1/invoices/preview-tax before creating or updating a draft invoice when you need the exact canonical rate, totals, and taxRateCode the backend will use.

Example manual draft invoice payload with one automatic line and one explicit override:

{
  "siteId": "507f1f77bcf86cd799439011",
  "clientId": "507f1f77bcf86cd799439012",
  "currency": "EUR",
  "items": [
    {
      "description": "Subscription",
      "quantity": 1,
      "unitPrice": 12100,
      "amountType": "gross",
      "itemType": "digital_service"
    },
    {
      "description": "Manual reduced VAT line",
      "quantity": 1,
      "unitPrice": 10700,
      "amountType": "gross",
      "taxRate": 7,
      "itemType": "goods"
    }
  ]
}

In the example above, the first line is auto-calculated and the second line uses an explicit 7% override.


Recurring Schedules

MethodPathRequired ScopeDescription
GET/api/v1/invoices/recurringinvoice:readList schedules
POST/api/v1/invoices/recurringinvoice:createCreate schedule
GET/api/v1/invoices/recurring/:idinvoice:readGet schedule detail
PUT/api/v1/invoices/recurring/:idinvoice:updateUpdate schedule fields and template line items
POST/api/v1/invoices/recurring/:id/pauseinvoice:executePause schedule
POST/api/v1/invoices/recurring/:id/resumeinvoice:executeResume schedule
POST/api/v1/invoices/recurring/:id/cancelinvoice:executeCancel schedule or schedule future-cycle cancellation
POST/api/v1/invoices/recurring/:id/generateinvoice:executeManually generate next invoice
GET/api/v1/invoices/recurring/:id/upcominginvoice:executePreview upcoming invoices
POST/api/v1/system/recurring/processsystem:executeProcess all currently due schedules and due scheduled cancellations
GET/api/v1/system/recurring/statussystem:readRead recurring processor status and metrics

Recurring schedule payloads and responses use nextRunDate for the next scheduled processing date. Use startDate when creating a schedule; the backend materializes nextRunDate from the start date and interval. Do not use the older nextRunAt name in integrations.

Updating startDate also moves nextRunDate before any invoices have been generated. Once a schedule has generated invoices, startDate can only move future billing after the current nextRunDate; moving it onto the current cycle or an earlier date returns a validation error.

The cancel endpoint accepts an optional JSON body: { "reason": "..." }. The backend trims the reason, caps it at 500 characters, stores cancellation audit metadata (cancellationRequestedAt, cancelledBy, and cancellationReason), and keeps empty-body cancellation requests backward compatible.

The due-schedule processor generates invoices for active schedules whose nextRunDate is due. It also finalizes cancel_requested schedules whose effectiveFromCycle is due, with nextRunDate fallback for older records. Generated invoices remain drafts until an operator issues them unless the schedule has autoFinalizeInvoices enabled. autoSendInvoices is valid only when autoFinalizeInvoices is also enabled; recurring auto-send is the schedule-level email trigger after automatic issue. Recurring processing does not automatically capture payment-provider funds.

The Admin Panel labels the manual processor action Run Due Schedules because it does not force future scheduled cancellations early. The response keeps processed, completed, failed, and skipped, and also includes cancellationsFinalized so cancellation-only runs are visible in UI summaries and processor metrics.


Credit Notes

MethodPathRequired ScopeDescription
GET/api/v1/credit-notescredit_note:readList credit notes
GET/api/v1/credit-notes/:idcredit_note:readGet credit note
GET/api/v1/credit-notes/:id/pdfcredit_note:executeDownload PDF
POST/api/v1/credit-notes/:id/send-emailcredit_note:executeEmail credit note

Clients

MethodPathRequired ScopeDescription
GET/api/v1/clientsclient:readList clients
GET/api/v1/clients/searchclient:executeFull-text search
GET/api/v1/clients/:idclient:readGet client
POST/api/v1/clientsclient:createCreate client
PUT/api/v1/clients/:idclient:updateUpdate client
DELETE/api/v1/clients/:idclient:deleteDelete client
GET/api/v1/clients/:id/historyclient:executeGet client audit history

Client tax data may include a buyer Peppol routing address only for customers that receive Peppol BIS 3.0 XML. The value is encrypted like other client compliance fields. Use peppolRoutingAddress, not older generic endpoint names.

{
  "taxInfo": {
    "taxId": { "plaintext": "DE987654321" },
    "taxIdType": "vat",
    "taxCountry": "DE",
    "peppolRoutingAddress": {
      "schemeId": "9930",
      "value": { "plaintext": "DE987654321" }
    }
  }
}

Do not assume the VAT ID is the Peppol address. Use a VAT-number scheme such as 9930 only when the buyer confirms that exact Peppol address. Use 0088 only for a supplied GS1 GLN. Use 0204 for a German Leitweg-ID only when the German public-sector recipient confirms it as their Peppol receiver address.


Sites

MethodPathRequired ScopeDescription
GET/api/v1/sitessite:readList sites
GET/api/v1/sites/:idsite:readGet site
POST/api/v1/sitessite:createCreate site
PUT/api/v1/sites/:idsite:updateUpdate site
DELETE/api/v1/sites/:idsite:deleteDelete site
POST/api/v1/sites/:id/webhook-secret/regeneratesite:updateRotate outbound webhook secret

Organization Settings

Invoicing Settings

MethodPathRequired ScopeDescription
GET/api/v1/organizations/settings/invoicingorganization:executeGet invoicing settings for the active organization
PUT/api/v1/organizations/settings/invoicingorganization:executeUpdate invoicing settings for the active organization

The numbering contract is layout-based. Organizations store one layout for invoices and one for credit notes.

{
  "enabled": true,
  "autoGenerateInvoice": false,
  "companyName": "Example GmbH",
  "companyAddress": {
    "street": "Example Street 1",
    "city": "Berlin",
    "postalCode": "10115",
    "country": "DE"
  },
  "companyEmail": "billing@example.com",
  "bankIBAN": { "plaintext": "DE89370400440532013000" },
  "eInvoice": {
    "enabled": true,
    "allowedFormats": ["xrechnung", "peppol", "zugferd"],
    "strictValidation": true,
    "sellerPeppolRoutingAddress": {
      "schemeId": "9930",
      "value": { "plaintext": "DE123456789" }
    }
  },
  "invoiceNumberLayout": {
    "prefix": "INV-",
    "periodType": "calendar",
    "periodGranularity": "year",
    "fiscalYearStartMonth": 1,
    "fiscalYearLabelMode": "end_year",
    "periodSeparator": "dash",
    "infix": "-",
    "sequenceDigits": 6,
    "suffix": "",
    "resetPolicy": "calendar_year",
    "startNumber": 1
  },
  "creditNoteNumberLayout": {
    "prefix": "CN-",
    "periodType": "calendar",
    "periodGranularity": "year",
    "fiscalYearStartMonth": 1,
    "fiscalYearLabelMode": "end_year",
    "periodSeparator": "dash",
    "infix": "-",
    "sequenceDigits": 6,
    "suffix": "",
    "resetPolicy": "calendar_year",
    "startNumber": 1
  }
}

PDF storage contract

The organization setting pdfStorage configures invoice and credit-note PDFs. Set enabled: true and type: "s3" for Amazon S3 or a compatible service. The saved alias "aws" is also accepted. Region, bucket, access key, and secret key are required for a new S3 configuration; an empty endpoint selects Amazon S3. Azure, GCS, and unknown enabled providers are rejected. Disabled storage and saved local / filesystem settings use the temporary worker cache.

Storage credentials returned by GET are strings masked as "[MASKED]". PUT accepts encrypted-field inputs as strings or objects with a plaintext property. For an existing credential, blank or "[MASKED]" preserves the saved encrypted value; a new plaintext value replaces it. A blank endpoint explicitly selects the AWS default, while a masked endpoint preserves its saved value. Never send ciphertext or encryption metadata from a client.

baseUrl remains optional legacy configuration and is not used for new S3 document downloads. New pdfUrl values can be managed relative references, not public URLs. Consumers should use the authorized document download endpoints rather than treating that field as a public hyperlink.

A successful settings update validates and saves configuration only. It does not verify connectivity or permissions, migrate existing objects, or establish a retention policy. See PDF Storage for verification and the impact of changing or disabling the current storage configuration.

Tax settings (organization)

MethodPathRequired ScopeDescription
GET/api/v1/organizations/settings/taxorganization:executeRead org tax configuration (OSS/IOSS flags, VIES, rounding, etc.)
PUT/api/v1/organizations/settings/taxorganization:executeUpdate org tax configuration (Admin permission; API keys need scope above)
GET/api/v1/organizations/settings/tax/oss-threshold-statusorganization:readEU B2C cross-border threshold vs OSS registration status

Notes:

  • periodType is one of none, calendar, or fiscal.
  • periodGranularity is one of year, year_month, or year_month_day.
  • periodSeparator is dash or none and only matters when month or day is visible.
  • resetPolicy is one of never, calendar_year, or fiscal_year.
  • Sites inherit these layouts and may override only invoicePrefix and creditNotePrefix in their site-level invoicing settings.

Providers

MethodPathRequired ScopeRequired RoleDescription
GET/api/v1/providersprovider:readViewerList providers
GET/api/v1/providers/:idprovider:readViewerGet provider
POST/api/v1/providersprovider:createAdminCreate provider
DELETE/api/v1/providers/:idprovider:deleteAdminDelete provider
PUT/api/v1/providers/:id/toggleprovider:executeAdminEnable/disable provider

Domains

MethodPathRequired ScopeRequired RoleDescription
GET/api/v1/domainsdomain:readViewerList domains
POST/api/v1/domainsdomain:createAdminRegister custom domain
DELETE/api/v1/domains/:iddomain:deleteAdminRemove domain
GET/api/v1/domains/verify-cnamedomain:executeEditorVerify CNAME record

API Keys

MethodPathRequired ScopeDescription
GET/api/v1/organizations/{orgId}/api-keysapi_key:readList organization API keys
POST/api/v1/organizations/{orgId}/api-keysapi_key:createCreate organization API key
GET/api/v1/organizations/{orgId}/api-keys/{id}api_key:readGet organization API key details
PUT/api/v1/organizations/{orgId}/api-keys/{id}api_key:updateUpdate API key metadata / scopes
DELETE/api/v1/organizations/{orgId}/api-keys/{id}api_key:deleteDelete API key
POST/api/v1/organizations/{orgId}/api-keys/{id}/revokeapi_key:executeRevoke API key
POST/api/v1/organizations/{orgId}/api-keys/{id}/rotateapi_key:executeRotate API key (issues new secret)

Organization API keys are already bound to one organization, but the management endpoints remain nested under /organizations/{orgId}/api-keys for admin and automation workflows. System API keys use the /api/v1/system/api-keys family and are intended for global administrative automation rather than normal merchant checkout integrations.


Portal

MethodPathRequired ScopeDescription
POST/api/v1/organizations/settings/portal/magic-linksportal:createGenerate a one-time direct-login customer portal link
POST /api/v1/organizations/settings/portal/magic-links
Authorization: Bearer sk_live_keyid.secret
Content-Type: application/json
{
  "email": "buyer@example.com",
  "ttlMinutes": 15
}

Returns a one-time magic-link URL that can be used to direct the customer straight into the portal login flow for that email.

ttlMinutes is optional. If omitted, the system portal magic-link TTL is used. If provided, it can only shorten the lifetime; values above the system policy are capped to the configured system maximum.

Example server-side usage:

const response = await fetch(
  `${adminBaseUrl}/api/v1/organizations/settings/portal/magic-links`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${organizationApiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      email: "buyer@example.com",
      ttlMinutes: 15,
    }),
  },
);

if (!response.ok) {
  throw new Error(`Failed to create portal magic link: ${response.status}`);
}

const { url, expiresAt, ttlMinutes } = await response.json();

return {
  loginUrl: url,
  expiresAt,
  effectiveTtlMinutes: ttlMinutes,
};

The endpoint only succeeds when:

  • system-level portal access is enabled,
  • system-level portal magic links are enabled,
  • the authenticated organization has portal access enabled, and
  • the email has at least one portal-visible invoice, credit note, or recurring subscription in that organization.

Use this for trusted backend automations only. Do not expose it to browser or mobile clients.


Tax Management

MethodPathRequired ScopeDescription
GET/api/v1/tax-rulestax_rule:readList tax rules
POST/api/v1/tax-rulestax_rule:createCreate tax rule
DELETE/api/v1/tax-rulestax_rule:deleteDelete all tax rules (destructive)
PUT/api/v1/tax-rules/:idtax_rule:updateUpdate tax rule
DELETE/api/v1/tax-rules/:idtax_rule:deleteDelete one tax rule
POST/api/v1/tax-rules/populate-defaultstax_rule:createCreate missing default tax rules; returns an empty created array when already up to date
GET/api/v1/tax-ratestax_rate:readList tax rates
POST/api/v1/tax-ratestax_rate:createCreate tax rate
DELETE/api/v1/tax-ratestax_rate:deleteDelete all tax rates (destructive)
POST/api/v1/tax-rates/replace-defaultstax_rate:executeSync managed official default rates without deleting custom rates
GET/api/v1/tax-rates/:idtax_rate:readGet one tax rate
PUT/api/v1/tax-rates/:idtax_rate:updateUpdate tax rate
DELETE/api/v1/tax-rates/:idtax_rate:deleteDelete one tax rate

Admin JWT calls use the Admin role for these routes; API keys need the matching tax_rule:* / tax_rate:* scopes.

Tax Reports

Reports use query parameters for filters (e.g. startDate, endDate, timezone, source=transactions|invoices|both, currency, siteId). VAT OSS endpoints require year and quarter (1–4). Export endpoints return a file download and are GET, not POST.

MethodPathRequired ScopeDescription
GET/api/v1/tax/reports/by-countrytax_report:readTax summary grouped by buyer country
GET/api/v1/tax/reports/by-country/detailstax_report:readRow-level basis for one country in that report
GET/api/v1/tax/reports/by-country/exporttax_report:executeDownload country report as CSV
GET/api/v1/tax/reports/revenue-by-countrytax_report:readRevenue-style summary by country
GET/api/v1/tax/reports/revenue-by-country/exporttax_report:executeDownload revenue-by-country report as CSV
GET/api/v1/tax/reports/currenciestax_report:readCurrencies available for report filters
GET/api/v1/tax/reports/vat-osstax_report:readEU VAT OSS quarterly aggregation
GET/api/v1/tax/reports/vat-oss/detailstax_report:readOSS line basis (country, rate code, supply type)
GET/api/v1/tax/reports/vat-oss/exporttax_report:executeDownload OSS report as XML
GET/api/v1/tax/reports/vat-oss/export-elstertax_report:executeDownload OSS data as Elster CSV (Germany)
POST/api/v1/tax/reports/vat-oss/filed-baselinestax_report:executeRecord immutable evidence of a filed Union OSS country total (Admin role required; returns 201)
GET/api/v1/tax/reports/vat-oss/correction-workflowtax_report:readPage through tenant-scoped baseline choices, correction candidates, previews, and authorization history for one selected later return
POST/api/v1/tax/reports/vat-oss/correction-workflow/filed-baselinestax_report:executeRecord a schema-2, qualification-usable baseline with required registration evidence (Admin role required; returns 201)
POST/api/v1/tax/reports/vat-oss/correction-workflow/authorizationstax_report:executeAuthorize the exact previewed correction/version for the selected later return (Admin role required; returns 200)
POST/api/v1/tax/reports/vat-oss/correctionstax_report:executeAuthorize a frozen receipt correction for an evidenced later return (Admin role required; initial and idempotent replay return 200)

The correction workflow is staff-operated and does not file a return. Record the filed return first, including its external evidence reference and SHA-256. Include registrationIdentifier to create qualification-usable evidence. If the same immutable filing scope already contains schema-1 evidence, the strict workflow accepts only the exact same filing facts, preserves the existing baseline ID and correction head, and adds one immutable registration companion. It rejects divergent facts and never infers registration from current organization settings. Omitting the identifier on the legacy endpoint preserves its existing schema-1 contract. Then authorize the frozen correction with the baseline ID, an explicit later quarter, the selected return's member state, and a reason. The selected quarter must not predate frozen discovery or recognition and must fit the supported normal correction window calculated from the original quarter's submission deadline. Unsupported member-state transitions, missing historical evidence, expired or unprovable windows, and correction projections beyond the bounded preview fail closed.

VAT OSS JSON remains a truthful draft when filing is blocked. XML and Elster CSV exports require a snapshot-consistent observed_clear completeness assessment, complete correction projection, and supported selected-return routing. GET /api/v1/tax/reporting-state can return historyDiscoveryIncomplete:true when bounded historical discovery cannot prove absence; do not interpret that state as no reportable history.

The correction workflow requires laterYear, laterQuarter, and memberStateOfId; each result collection has its own opaque cursor. A response never implies that omitted pages are empty: continue with the corresponding nextBaselineCursor, nextCandidateCursor, or nextHistoryCursor. When another collection still has pages, send baselineExhausted, candidateExhausted, or historyExhausted for each stream that has no next cursor so it is not restarted. Candidate previews bind the exact receipt decision evidence as well as a history version. Send the preview token and history version back when authorizing; stale or concurrently changed evidence is rejected and must be previewed again. The server derives idempotency for guided authorization, so an ordinary operator does not supply an internal idempotency key.

The original /filed-baselines endpoint remains compatible with immutable schema-1 history. Use the strict correction-workflow baseline route for all new qualification evidence; it requires the registration identifier. API amounts remain integer minor units even though the Admin Panel accepts and displays human currency units.

Tax records (canonical ledger)

Immutable tax records back admin reporting drill-downs and traceability. List filters include date range, sourceKind, recognitionMode (accrual / cash), pagination, and links to transactions or invoices.

MethodPathRequired ScopeDescription
GET/api/v1/tax/reporting-statetax_report:readReconciliation and calculation readiness
GET/api/v1/tax/recordstax_record:readList tax records
GET/api/v1/tax/records/:idtax_record:readGet one tax record
GET/api/v1/tax/records/rebuildtax_record:readGet latest tax-record rebuild job
POST/api/v1/tax/records/rebuildtax_record:executeStart tax-record rebuild
GET/api/v1/tax/records/rebuild/:jobIdtax_record:readPoll tax-record rebuild job status
GET/api/v1/transactions/:id/tax-recordstax_record:readTax records linked to a transaction
GET/api/v1/invoices/:id/tax-recordstax_record:readTax records linked to an invoice

Both operations use POST /api/v1/tax/records/rebuild. For stored-snapshot repair, send {"mode":"repair","dryRun":true} to preview; explicitly execute with dryRun:false. Omitted mode retains repair behavior for existing clients.

For missing historical tax, send mode:"historical_calculation", dryRun:true, RFC3339 historicalFrom and historicalTo, and priceTreatment:"gross" or "net". The start is inclusive and end exclusive. Poll the returned jobId. Execution requires dryRun:false, confirm:true, the exact completed previewJobId, and the same range and price treatment. Do not silently refresh and execute a changed preview.

Status reports distinguish planned, committed, unchanged, excluded, unresolved and unprocessed. Use GET /api/v1/tax/records/rebuild to recover the latest organization job after a refresh. GET /api/v1/tax/reporting-state requires tax_report:read and exposes maintenance and calculation readiness. Accounting endpoints can return 503 during reconciliation while status/recovery remain accessible. A completed job with unresolved coverage is not evidence of fully consistent reporting.


Data Import / Export

MethodPathRequired ScopeDescription
POST/api/v1/data-import-export/importdata_io:executeImport CSV data
POST/api/v1/data-import-export/import/validatedata_io:executeValidate import file before committing
POST/api/v1/data-import-export/exportdata_io:executeExport entity data to CSV
GET/api/v1/data-import-export/template/:entityTypedata_io:readDownload import CSV template

Dashboard

GET /api/v1/dashboard

Returns aggregated metrics: gross volume, net revenue, transaction counts, failure ratios, top providers, and recent transactions. Used by the Admin Panel home page.


API Key Scopes Reference

ScopeGrants
checkout:readRead checkouts
checkout:createCreate checkout URLs
checkout:updateUpdate checkout metadata
transaction:readRead transactions and tax details
transaction:createCreate manual transactions
transaction:updateUpdate transaction metadata
transaction:deleteDelete transactions
transaction:executeChargeback management
invoice:readRead invoices and recurring schedules
invoice:createCreate invoices and schedules
invoice:updateUpdate draft invoices, notes, invoice links, and correction workflows
invoice:executePreview tax, issue, void, email, payment checkout, PDF download, and recurring schedule actions
invoice:deleteDelete draft invoices
credit_note:readRead credit notes
credit_note:executeDownload credit note PDFs and send credit note email
client:readRead clients
client:createCreate clients
client:updateUpdate clients
client:deleteDelete clients
client:executeSearch clients, view history
site:readRead sites
site:createCreate sites
site:deleteDelete sites
site:updateUpdate sites, rotate webhook secret
provider:readRead providers
provider:createCreate providers (Admin role also required)
provider:updateUpdate provider configuration (Admin role)
provider:executeEnable/disable providers (Admin role)
provider:deleteDelete providers (Admin role)
domain:readRead domains
domain:createCreate domains (Admin role)
domain:deleteDelete domains (Admin role)
domain:executeVerify CNAME, Apple Pay files
api_key:readRead API keys
api_key:createCreate API keys
api_key:updateUpdate API key metadata, scopes, or whitelist
api_key:deleteDelete API keys
api_key:executeRotate or revoke API keys
organization:readRead organization details
organization:updateUpdate organization settings
organization:executeAccess settings, encryption operations
tax_rule:readRead tax rules
tax_rule:createCreate tax rules and populate default rule sets
tax_rule:updateUpdate tax rules
tax_rule:deleteDelete tax rules
tax_rate:readRead tax rates
tax_rate:createCreate tax rates
tax_rate:updateUpdate tax rates
tax_rate:deleteDelete tax rates
tax_rate:executeSync managed official default tax rates (replace-defaults)
tax_report:readRead tax reports
tax_report:executeExport tax reports (CSV/XML/Elster)
tax_record:readRead canonical tax ledger records and rebuild job status
tax_record:executeStart tax-record rebuild jobs
data_io:readRead import/export templates
data_io:executeImport and export data
export:executeGenerate CSV exports
system:readRead system-level operational status endpoints
system:executeExecute system-level operational actions such as recurring due-schedule processing
portal:createGenerate direct-login customer portal magic links
vies:readCheck EU countries
vies:executeValidate VAT IDs
userprofile:readRead own user profile
userprofile:updateUpdate passkey labels, notification prefs
userprofile:executeManage passkeys and related user profile actions

Common least-privilege bundles

  • Checkout creation: checkout:create
  • Recurring schedule management: invoice:read, invoice:create, invoice:update, invoice:execute
  • Recurring processor operations: system:read, system:execute (system API keys or global administrator sessions)

Recurring schedules do not use a separate recurring:* scope family. They are governed by the invoice:* scope family.

Receipt recovery and financial commands

POST /transactions/manual accepts a completed bank receipt with originalTransactionId, bankEvidenceId, receiptAccountId, receivedAt (RFC3339), amount in the currency’s minor unit, currency, notes and acknowledgeDuplicatePayment. Existing site/provider/organization authorization still applies. No new invoice is required. POST /transactions/{id}/confirm accepts the same source/date evidence for an existing bank attempt.

POST /transactions/{id}/reconciliation/resolve is an evidenced financial command, not a free-form resolved label. Supply a UUID operationId, expectedVersion, action and evidence. correct_received_at additionally requires receivedAt. false_positive additionally requires destinationTransactionId, destinationVersion, amount and currency. Reuse the exact operation payload after an uncertain response; conflicting reuse fails.

POST /transactions/receipts/backfill requires administrative transaction-execution permission. Provide limit (1–100), optional afterId cursor and apply (false by default). The response reports scanned, captured, replayed and review counts plus the next cursor. The organization is taken from the authenticated context.

Receipt responses expose receipt, orderPaymentId and financialVersion. Unsupported disposition or tax plans return a conflict; no partial financial update is committed. See Receipts and order allocation and Refunds and Returns for migration safeguards and pending obligations.

Receipt endpoint examples above are relative to the Admin API base prefix /api/v1.

On this page