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 with403if 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 validSession 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.
409with codeREFRESH_IN_PROGRESSandRetry-After: 1means 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.429with codeRATE_LIMIT_EXCEEDEDis temporary. HonorRetry-Afterin seconds. Refresh has its own per-IP limit of 20 requests per 10-minute fixed window, separate from ordinary API traffic.503with codeRATE_LIMIT_UNAVAILABLEandRetry-After: 1means 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
POST | /api/v1/checkouts/{siteId}/create | checkout:create | Create a checkout session for a site |
GET | /api/v1/checkouts | checkout:read | List all checkouts |
GET | /api/v1/checkouts/{checkoutId} | checkout:read | Get a checkout by checkout ID |
PATCH | /api/v1/checkouts/{checkoutId} | checkout:update | Update 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
externalReferenceorinvoiceNumber. The API accepts both, but a reference is used as matching/presentation evidence for reconciliation, never as canonical order uniqueness.
[!NOTE]
{transactionId}inreturnUrl/cancelUrlis 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.
| Code | Meaning |
|---|---|
CHECKOUT_BLOCKED_BY_DISPUTE | An open or under-review dispute exists for the same buyer. |
CHECKOUT_BLOCKED_BY_CUSTOMER_HOLD | A final merchant-loss Customer Risk Hold blocks checkout or requires merchant review. |
CHECKOUT_RESTRICTED_BY_CUSTOMER_HOLD | A 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/transactions | transaction:read | List transactions (filterable) |
GET | /api/v1/transactions/:id | transaction:read | Get transaction details |
GET | /api/v1/transactions/:id/tax | transaction:read | Get full tax decision metadata |
PUT | /api/v1/transactions/:id | transaction:update | Update transaction metadata |
DELETE | /api/v1/transactions/:id | transaction:delete | Delete transaction |
POST | /api/v1/transactions/manual | transaction:create | Create a manual (off-platform) transaction |
GET | /api/v1/transactions/:id/adjustments | transaction:read | Read bounded adjustment history and eligibility |
GET | /api/v1/transactions/:id/adjustments/:operationId | transaction:read | Read one immutable adjustment operation |
POST | /api/v1/transactions/:id/refund/initiate | transaction:execute | Initiate an eligible provider refund |
POST | /api/v1/transactions/:id/recalculate-tax | transaction:execute | Re-run tax engine for one transaction |
List Transactions
GET /api/v1/transactions?statuses[]=1¤cy=EUR&startDate=2026-01-01&endDate=2026-03-31&limit=50&page=1Query Parameters:
| Parameter | Type | Description |
|---|---|---|
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 |
currency | string | ISO 4217 currency code (e.g., EUR, USD, GBP) |
startDate | date | Start date (YYYY-MM-DD or RFC3339) |
endDate | date | End date (YYYY-MM-DD or RFC3339) |
siteIds[] | string[] | Filter by site IDs |
providerIds[] | string[] | Filter by provider IDs |
page | int | Page number (default: 1) |
limit | int | Items 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/invoices | invoice:read | List invoices |
GET | /api/v1/invoices/:id | invoice:read | Get invoice |
POST | /api/v1/invoices | invoice:create | Create draft invoice |
POST | /api/v1/invoices/preview-tax | invoice:create | Preview canonical tax for a draft invoice payload |
POST | /api/v1/invoices/from-transaction/:transactionId | invoice:create | Create invoice from transaction |
PUT | /api/v1/invoices/:id | invoice:update | Update draft invoice fields and/or line items |
PATCH | /api/v1/invoices/:id/notes | invoice:update | Update invoice notes |
PUT | /api/v1/invoices/:id/transaction | invoice:update | Assign a transaction to an existing invoice |
POST | /api/v1/invoices/:id/issue | invoice:execute | Issue invoice (draft → issued) |
POST | /api/v1/invoices/:id/void | invoice:execute | Void an issued invoice (Admin role required) |
POST | /api/v1/invoices/:id/correct | invoice:update | Correct an invoice with audit trail / credit-note workflow |
POST | /api/v1/invoices/:id/send-email | invoice:execute | Email invoice to client |
POST | /api/v1/invoices/:id/payment-checkout | invoice:execute | Create an online payment checkout for the invoice |
GET | /api/v1/invoices/:id/pdf | invoice:execute | Download PDF |
GET | /api/v1/invoices/:id/xml | invoice:execute | Download XRechnung XML |
GET | /api/v1/invoices/:id/xml/peppol | invoice:execute | Download Peppol BIS 3.0 XML |
GET | /api/v1/invoices/:id/pdf/zugferd | invoice:execute | Download ZUGFeRD PDF |
POST | /api/v1/invoices/:id/regenerate-pdf | invoice:execute | Force PDF regeneration |
DELETE | /api/v1/invoices/:id | invoice:delete | Delete 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"
}amountis in minor units. If omitted, the backend uses the invoice's payable outstanding amount.- Partial payment links are allowed from
50minor units up to the outstanding amount. - Empty invoices, fully settled invoices, zero-outstanding invoices, and amounts below the minimum return
400. - If
expireInHoursis omitted, the backend uses the effective invoicing settingautoPaymentLinkExpiresInHours, falling back to336hours. Explicit values must be between1and3120. - 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[].taxRateto 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[].taxRateto apply an explicit manual override for that line item. - Use
POST /api/v1/invoices/preview-taxbefore creating or updating a draft invoice when you need the exact canonical rate, totals, andtaxRateCodethe 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/invoices/recurring | invoice:read | List schedules |
POST | /api/v1/invoices/recurring | invoice:create | Create schedule |
GET | /api/v1/invoices/recurring/:id | invoice:read | Get schedule detail |
PUT | /api/v1/invoices/recurring/:id | invoice:update | Update schedule fields and template line items |
POST | /api/v1/invoices/recurring/:id/pause | invoice:execute | Pause schedule |
POST | /api/v1/invoices/recurring/:id/resume | invoice:execute | Resume schedule |
POST | /api/v1/invoices/recurring/:id/cancel | invoice:execute | Cancel schedule or schedule future-cycle cancellation |
POST | /api/v1/invoices/recurring/:id/generate | invoice:execute | Manually generate next invoice |
GET | /api/v1/invoices/recurring/:id/upcoming | invoice:execute | Preview upcoming invoices |
POST | /api/v1/system/recurring/process | system:execute | Process all currently due schedules and due scheduled cancellations |
GET | /api/v1/system/recurring/status | system:read | Read 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/credit-notes | credit_note:read | List credit notes |
GET | /api/v1/credit-notes/:id | credit_note:read | Get credit note |
GET | /api/v1/credit-notes/:id/pdf | credit_note:execute | Download PDF |
POST | /api/v1/credit-notes/:id/send-email | credit_note:execute | Email credit note |
Clients
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/clients | client:read | List clients |
GET | /api/v1/clients/search | client:execute | Full-text search |
GET | /api/v1/clients/:id | client:read | Get client |
POST | /api/v1/clients | client:create | Create client |
PUT | /api/v1/clients/:id | client:update | Update client |
DELETE | /api/v1/clients/:id | client:delete | Delete client |
GET | /api/v1/clients/:id/history | client:execute | Get 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/sites | site:read | List sites |
GET | /api/v1/sites/:id | site:read | Get site |
POST | /api/v1/sites | site:create | Create site |
PUT | /api/v1/sites/:id | site:update | Update site |
DELETE | /api/v1/sites/:id | site:delete | Delete site |
POST | /api/v1/sites/:id/webhook-secret/regenerate | site:update | Rotate outbound webhook secret |
Organization Settings
Invoicing Settings
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/organizations/settings/invoicing | organization:execute | Get invoicing settings for the active organization |
PUT | /api/v1/organizations/settings/invoicing | organization:execute | Update 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)
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/organizations/settings/tax | organization:execute | Read org tax configuration (OSS/IOSS flags, VIES, rounding, etc.) |
PUT | /api/v1/organizations/settings/tax | organization:execute | Update org tax configuration (Admin permission; API keys need scope above) |
GET | /api/v1/organizations/settings/tax/oss-threshold-status | organization:read | EU B2C cross-border threshold vs OSS registration status |
Notes:
periodTypeis one ofnone,calendar, orfiscal.periodGranularityis one ofyear,year_month, oryear_month_day.periodSeparatorisdashornoneand only matters when month or day is visible.resetPolicyis one ofnever,calendar_year, orfiscal_year.- Sites inherit these layouts and may override only
invoicePrefixandcreditNotePrefixin their site-level invoicing settings.
Providers
| Method | Path | Required Scope | Required Role | Description |
|---|---|---|---|---|
GET | /api/v1/providers | provider:read | Viewer | List providers |
GET | /api/v1/providers/:id | provider:read | Viewer | Get provider |
POST | /api/v1/providers | provider:create | Admin | Create provider |
DELETE | /api/v1/providers/:id | provider:delete | Admin | Delete provider |
PUT | /api/v1/providers/:id/toggle | provider:execute | Admin | Enable/disable provider |
Domains
| Method | Path | Required Scope | Required Role | Description |
|---|---|---|---|---|
GET | /api/v1/domains | domain:read | Viewer | List domains |
POST | /api/v1/domains | domain:create | Admin | Register custom domain |
DELETE | /api/v1/domains/:id | domain:delete | Admin | Remove domain |
GET | /api/v1/domains/verify-cname | domain:execute | Editor | Verify CNAME record |
API Keys
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/organizations/{orgId}/api-keys | api_key:read | List organization API keys |
POST | /api/v1/organizations/{orgId}/api-keys | api_key:create | Create organization API key |
GET | /api/v1/organizations/{orgId}/api-keys/{id} | api_key:read | Get organization API key details |
PUT | /api/v1/organizations/{orgId}/api-keys/{id} | api_key:update | Update API key metadata / scopes |
DELETE | /api/v1/organizations/{orgId}/api-keys/{id} | api_key:delete | Delete API key |
POST | /api/v1/organizations/{orgId}/api-keys/{id}/revoke | api_key:execute | Revoke API key |
POST | /api/v1/organizations/{orgId}/api-keys/{id}/rotate | api_key:execute | Rotate 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
POST | /api/v1/organizations/settings/portal/magic-links | portal:create | Generate a one-time direct-login customer portal link |
Create Customer Portal Magic 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/tax-rules | tax_rule:read | List tax rules |
POST | /api/v1/tax-rules | tax_rule:create | Create tax rule |
DELETE | /api/v1/tax-rules | tax_rule:delete | Delete all tax rules (destructive) |
PUT | /api/v1/tax-rules/:id | tax_rule:update | Update tax rule |
DELETE | /api/v1/tax-rules/:id | tax_rule:delete | Delete one tax rule |
POST | /api/v1/tax-rules/populate-defaults | tax_rule:create | Create missing default tax rules; returns an empty created array when already up to date |
GET | /api/v1/tax-rates | tax_rate:read | List tax rates |
POST | /api/v1/tax-rates | tax_rate:create | Create tax rate |
DELETE | /api/v1/tax-rates | tax_rate:delete | Delete all tax rates (destructive) |
POST | /api/v1/tax-rates/replace-defaults | tax_rate:execute | Sync managed official default rates without deleting custom rates |
GET | /api/v1/tax-rates/:id | tax_rate:read | Get one tax rate |
PUT | /api/v1/tax-rates/:id | tax_rate:update | Update tax rate |
DELETE | /api/v1/tax-rates/:id | tax_rate:delete | Delete 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.
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/tax/reports/by-country | tax_report:read | Tax summary grouped by buyer country |
GET | /api/v1/tax/reports/by-country/details | tax_report:read | Row-level basis for one country in that report |
GET | /api/v1/tax/reports/by-country/export | tax_report:execute | Download country report as CSV |
GET | /api/v1/tax/reports/revenue-by-country | tax_report:read | Revenue-style summary by country |
GET | /api/v1/tax/reports/revenue-by-country/export | tax_report:execute | Download revenue-by-country report as CSV |
GET | /api/v1/tax/reports/currencies | tax_report:read | Currencies available for report filters |
GET | /api/v1/tax/reports/vat-oss | tax_report:read | EU VAT OSS quarterly aggregation |
GET | /api/v1/tax/reports/vat-oss/details | tax_report:read | OSS line basis (country, rate code, supply type) |
GET | /api/v1/tax/reports/vat-oss/export | tax_report:execute | Download OSS report as XML |
GET | /api/v1/tax/reports/vat-oss/export-elster | tax_report:execute | Download OSS data as Elster CSV (Germany) |
POST | /api/v1/tax/reports/vat-oss/filed-baselines | tax_report:execute | Record immutable evidence of a filed Union OSS country total (Admin role required; returns 201) |
GET | /api/v1/tax/reports/vat-oss/correction-workflow | tax_report:read | Page 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-baselines | tax_report:execute | Record a schema-2, qualification-usable baseline with required registration evidence (Admin role required; returns 201) |
POST | /api/v1/tax/reports/vat-oss/correction-workflow/authorizations | tax_report:execute | Authorize the exact previewed correction/version for the selected later return (Admin role required; returns 200) |
POST | /api/v1/tax/reports/vat-oss/corrections | tax_report:execute | Authorize 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.
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET | /api/v1/tax/reporting-state | tax_report:read | Reconciliation and calculation readiness |
GET | /api/v1/tax/records | tax_record:read | List tax records |
GET | /api/v1/tax/records/:id | tax_record:read | Get one tax record |
GET | /api/v1/tax/records/rebuild | tax_record:read | Get latest tax-record rebuild job |
POST | /api/v1/tax/records/rebuild | tax_record:execute | Start tax-record rebuild |
GET | /api/v1/tax/records/rebuild/:jobId | tax_record:read | Poll tax-record rebuild job status |
GET | /api/v1/transactions/:id/tax-records | tax_record:read | Tax records linked to a transaction |
GET | /api/v1/invoices/:id/tax-records | tax_record:read | Tax 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
| Method | Path | Required Scope | Description |
|---|---|---|---|
POST | /api/v1/data-import-export/import | data_io:execute | Import CSV data |
POST | /api/v1/data-import-export/import/validate | data_io:execute | Validate import file before committing |
POST | /api/v1/data-import-export/export | data_io:execute | Export entity data to CSV |
GET | /api/v1/data-import-export/template/:entityType | data_io:read | Download import CSV template |
Dashboard
GET /api/v1/dashboardReturns 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
| Scope | Grants |
|---|---|
checkout:read | Read checkouts |
checkout:create | Create checkout URLs |
checkout:update | Update checkout metadata |
transaction:read | Read transactions and tax details |
transaction:create | Create manual transactions |
transaction:update | Update transaction metadata |
transaction:delete | Delete transactions |
transaction:execute | Chargeback management |
invoice:read | Read invoices and recurring schedules |
invoice:create | Create invoices and schedules |
invoice:update | Update draft invoices, notes, invoice links, and correction workflows |
invoice:execute | Preview tax, issue, void, email, payment checkout, PDF download, and recurring schedule actions |
invoice:delete | Delete draft invoices |
credit_note:read | Read credit notes |
credit_note:execute | Download credit note PDFs and send credit note email |
client:read | Read clients |
client:create | Create clients |
client:update | Update clients |
client:delete | Delete clients |
client:execute | Search clients, view history |
site:read | Read sites |
site:create | Create sites |
site:delete | Delete sites |
site:update | Update sites, rotate webhook secret |
provider:read | Read providers |
provider:create | Create providers (Admin role also required) |
provider:update | Update provider configuration (Admin role) |
provider:execute | Enable/disable providers (Admin role) |
provider:delete | Delete providers (Admin role) |
domain:read | Read domains |
domain:create | Create domains (Admin role) |
domain:delete | Delete domains (Admin role) |
domain:execute | Verify CNAME, Apple Pay files |
api_key:read | Read API keys |
api_key:create | Create API keys |
api_key:update | Update API key metadata, scopes, or whitelist |
api_key:delete | Delete API keys |
api_key:execute | Rotate or revoke API keys |
organization:read | Read organization details |
organization:update | Update organization settings |
organization:execute | Access settings, encryption operations |
tax_rule:read | Read tax rules |
tax_rule:create | Create tax rules and populate default rule sets |
tax_rule:update | Update tax rules |
tax_rule:delete | Delete tax rules |
tax_rate:read | Read tax rates |
tax_rate:create | Create tax rates |
tax_rate:update | Update tax rates |
tax_rate:delete | Delete tax rates |
tax_rate:execute | Sync managed official default tax rates (replace-defaults) |
tax_report:read | Read tax reports |
tax_report:execute | Export tax reports (CSV/XML/Elster) |
tax_record:read | Read canonical tax ledger records and rebuild job status |
tax_record:execute | Start tax-record rebuild jobs |
data_io:read | Read import/export templates |
data_io:execute | Import and export data |
export:execute | Generate CSV exports |
system:read | Read system-level operational status endpoints |
system:execute | Execute system-level operational actions such as recurring due-schedule processing |
portal:create | Generate direct-login customer portal magic links |
vies:read | Check EU countries |
vies:execute | Validate VAT IDs |
userprofile:read | Read own user profile |
userprofile:update | Update passkey labels, notification prefs |
userprofile:execute | Manage 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.