API authentication, signing and scopes
Send JSON events to POST /api/integrations/commerce/v1/events/ with Authorization: Bearer <your scoped API key>. The endpoint accepts schemaVersion 1 (legacy decimal amounts) and schemaVersion 2 (integer minor units). Prefer version 2 for new integrations. Use separate keys for each connection and environment; never include credentials in URLs.
For signed delivery use POST /api/integrations/commerce/webhooks/{connectionId}/. Send x-meshdex-timestamp as Unix seconds and x-meshdex-signature as v1= followed by the hexadecimal HMAC-SHA256 of timestamp + a period + the exact raw JSON body, using your connection signing secret. Requests must be within five minutes of current time. Sign the exact bytes sent; do not reformat after signing.
API keys require an active connection and the matching scope. Events write: commerce.events:write. Event status: commerce.events:read. Orders: commerce.orders:read. Catalog: commerce.catalog:read. Availability: commerce.availability:read. Reconciliation: commerce.reconciliation:read. Use minimum scopes. Catalog mappings and historical application require authorized workspace administration.
A 202 response means queued, not posted. Save the returned event ID. GET /api/integrations/commerce/v1/events/{eventId}/ returns its status, error and result; the source external event ID can also be used. GET /api/integrations/commerce/v1/orders/?orderId=your-source-invoice-id returns accounting/inventory links. GET /api/integrations/commerce/v1/reconciliation/ returns a paginated reconciliation view; follow nextCursor using after.
Automatic SaaS service setup
A custom SaaS connector can discover its connected Ledger currency and approved tax rates with GET /api/integrations/commerce/v1/connector-setup/. Use commerce.catalog:read. The response also lists missing account routing and required credential scopes.
With commerce.catalog:write, POST the connector’s existing service definitions to the same endpoint. Each definition supplies externalProductId and name. Synchronization creates new Ledger service mappings using the approved income account; existing mappings and products are preserved and conflicts require review. Service prices and currencies remain source-authoritative on each invoice.
Catalog setup is permitted for a valid scoped key on Draft or Paused SaaS connections, allowing services to be configured before activation. Financial event submission still requires an Active connection. Test connections do not post invoice/payment accounting.
For foreign-currency book valuation, GET the same endpoint with currency and date, for example ?currency=USD&date=2026-01-01. It returns dated evidence for one transaction-currency unit into the actual Ledger base currency. A connector must explicitly approve this reference-rate policy, freeze the returned evidence before first delivery and reuse it unchanged on retries. Gateway settlement conversion remains a separate event/workflow.
{
"services": [
{
"externalProductId": "example-starter-monthly",
"name": "Example Starter monthly"
},
{
"externalProductId": "example-starter-yearly",
"name": "Example Starter yearly"
}
]
}Version 2 amounts, taxes and exchange rates
Every event supplies externalEventId, eventType, schemaVersion: 2, occurredAt and a monotonically increasing integer sourceRevision encoded as a string. Stable IDs identify customers, source invoices, captures, credits, refunds, settlements and returns within one connection. Keep event ID and payload unchanged when retrying. A different event ID must not be used to change the values of an existing capture/refund/settlement.
Use integer minor units: INR/USD/EUR/GBP/AUD/CAD/NZD/SGD/HKD/AED/SAR/CHF use 100 minor units per currency unit; JPY uses one. Other currencies and fractional quantities are rejected for automatic accounting. Quantities must be positive integers; at most 200 invoice lines. Each minor-unit amount and invoice total must be at most 2147483647. Shipping is an explicit mapped line; discounts belong to each line.
Each line specifies unitPriceMinor, quantity, discountMinor and tax with treatment EXCLUSIVE, INCLUSIVE, EXEMPT or ZERO_RATED, amountMinor and components. Components include name, rate and amountMinor; amounts round to the nearest minor unit, with halves rounded upward, and must reconcile exactly. totalMinor equals the sum of discounted line totals including tax. Inclusive prices already include tax. Positive taxes require an approved taxRateId. Fully discounted lines are allowed when the overall invoice remains positive.
GST requires tax.countryCode: IN, HSN/SAC, placeOfSupply and either IGST or both CGST/SGST. Never mix interstate and intrastate components. Tax rates/payable accounts must be active and match the workspace. Tax is not inferred from currency. Credits/refunds specify taxMinor and taxPayableAccountId when reversing tax.
Foreign-currency invoice, capture, refund and settlement events include baseCurrency, positive exchangeRate, exchangeRateSource and exchangeRateDate. The rate converts one transaction/settlement currency unit into Ledger base currency. Receipt/refund cash uses the actual rate; receivable and credit reversals retain the invoice rate. Partial amounts allocate the remaining carrying balance so a fully closed invoice has no rounding residue. Differences require configured FX gain/loss accounts. No currency or rate is silently substituted.
A source that calculates aggregate Indian GST may record a one-minor-unit CGST/SGST allocation difference explicitly: tax.roundingMethod: AGGREGATE, with roundingAdjustmentMinor: 1 or -1 on exactly one component. This exception requires equal CGST/SGST rates, original aggregate tax reconciliation and all normal GST details; larger or unrelated rounding changes are rejected. Invoice tax and total are unchanged.
Physical, digital and SaaS examples
These examples are fictional and exempt from tax for readability. Replace the source/customer IDs and prices with your real source values. Approve product-1, service-1 and plan-pro mappings in your connection first. Configure invoice authority as Source for these examples. Physical stock is used only when its approved mapping uses Supply stock; digital and SaaS mappings use service products with Ledger only.
{
"externalEventId": "example-physical",
"eventType": "invoice.created",
"schemaVersion": 2,
"sourceRevision": "1",
"occurredAt": "2026-10-14T10:00:00Z",
"data": {
"invoiceId": "invoice-physical",
"invoiceNumber": "SOURCE-PHYSICAL",
"invoiceAuthority": "SOURCE",
"documentReference": "https://billing.example.com/invoices/invoice-physical",
"currency": "INR",
"customer": {
"id": "customer-1",
"name": "Example Customer",
"email": "[email protected]",
"billingAddress": {
"countryCode": "IN",
"regionCode": "KA"
}
},
"issueDate": "2026-10-14",
"dueDate": "2026-10-14",
"lines": [
{
"productId": "product-1",
"description": "Example physical product",
"quantity": 2,
"unitPriceMinor": 5000,
"discountMinor": 0,
"tax": {
"treatment": "EXEMPT",
"amountMinor": 0,
"components": []
}
}
],
"totalMinor": 10000
}
}{
"externalEventId": "example-digital",
"eventType": "invoice.created",
"schemaVersion": 2,
"sourceRevision": "1",
"occurredAt": "2026-10-14T10:00:00Z",
"data": {
"invoiceId": "invoice-digital",
"invoiceNumber": "SOURCE-DIGITAL",
"invoiceAuthority": "SOURCE",
"documentReference": "https://billing.example.com/invoices/invoice-digital",
"currency": "INR",
"customer": {
"id": "customer-1",
"name": "Example Customer",
"email": "[email protected]",
"billingAddress": {
"countryCode": "IN",
"regionCode": "KA"
}
},
"issueDate": "2026-10-14",
"dueDate": "2026-10-14",
"lines": [
{
"productId": "service-1",
"description": "Digital service",
"quantity": 1,
"unitPriceMinor": 10000,
"discountMinor": 0,
"tax": {
"treatment": "EXEMPT",
"amountMinor": 0,
"components": []
}
}
],
"totalMinor": 10000
}
}{
"externalEventId": "example-saas",
"eventType": "subscription.renewed",
"schemaVersion": 2,
"sourceRevision": "1",
"occurredAt": "2026-10-14T10:00:00Z",
"data": {
"invoiceId": "invoice-saas",
"invoiceNumber": "SOURCE-SAAS",
"invoiceAuthority": "SOURCE",
"documentReference": "https://billing.example.com/invoices/invoice-saas",
"currency": "INR",
"customer": {
"id": "customer-1",
"name": "Example Customer",
"email": "[email protected]",
"billingAddress": {
"countryCode": "IN",
"regionCode": "KA"
}
},
"issueDate": "2026-10-14",
"dueDate": "2026-10-14",
"lines": [
{
"productId": "plan-pro",
"description": "Monthly SaaS renewal",
"quantity": 1,
"unitPriceMinor": 10000,
"discountMinor": 0,
"tax": {
"treatment": "EXEMPT",
"amountMinor": 0,
"components": []
}
}
],
"totalMinor": 10000,
"subscriptionId": "subscription-1",
"billingPeriod": {
"cycle": "monthly",
"start": "2026-10-14",
"end": "2026-11-14"
}
}
}{
"externalEventId": "example-yearly",
"eventType": "subscription.renewed",
"schemaVersion": 2,
"sourceRevision": "1",
"occurredAt": "2026-10-14T10:00:00Z",
"data": {
"invoiceId": "invoice-yearly",
"invoiceNumber": "SOURCE-YEARLY",
"invoiceAuthority": "SOURCE",
"documentReference": "https://billing.example.com/invoices/invoice-yearly",
"currency": "INR",
"customer": {
"id": "customer-1",
"name": "Example Customer",
"email": "[email protected]",
"billingAddress": {
"countryCode": "IN",
"regionCode": "KA"
}
},
"issueDate": "2026-10-14",
"dueDate": "2026-10-14",
"lines": [
{
"productId": "plan-pro",
"description": "Yearly SaaS renewal",
"quantity": 1,
"unitPriceMinor": 10000,
"discountMinor": 0,
"tax": {
"treatment": "EXEMPT",
"amountMinor": 0,
"components": []
}
}
],
"totalMinor": 10000,
"subscriptionId": "subscription-1",
"billingPeriod": {
"cycle": "yearly",
"start": "2026-10-14",
"end": "2027-10-14"
}
}
}Supported event lifecycle
- customer.upserted creates or synchronizes a connection-scoped customer using revisions. Permitted updates include billing name, contact details, address and tax identity. Unrelated/manual customers are never automatically merged.
- invoice.created, order.created and subscription.renewed create invoices. invoice.updated/order.updated apply supported draft revisions. Stale revisions are ignored; conflicting revisions require review. Posted invoices are corrected by explicit credit events or governed Ledger adjustments, never overwritten.
- Send a distinct source invoice ID for every monthly/yearly billing period. Send source-issued proration or plan-change invoices with actual charged amounts and the relevant period; MeshDex does not calculate subscription schedules or entitlements. Subscription created/changed/cancelled events update references with accountingEffect: NONE. Cancelling future renewal does not refund or void a paid period.
- invoice.cancelled/order.cancelled void unpaid drafts and release unfulfilled reservations. Posted/paid or fulfilled records require explicit credit/refund or return workflows.
- payment.captured includes invoiceId, paymentId, currency, amountMinor and optional paidAt. It records confirmed captured cash into the configured bank/clearing account. Authorized/failed payments are references only.
- credit.issued includes invoiceId, creditId, currency, amountMinor, taxMinor, reason and optional creditedAt. It reduces revenue/receivable; it does not return cash.
- refund.completed includes invoiceId, refundId, currency, amountMinor, taxMinor, reason and completedAt. It records a confirmed gateway refund and corresponding credit. Include creditId to reuse an already issued matching credit; one credit can be consumed by only one matching cash refund. Otherwise a separate credit is created. Repeated IDs cannot produce duplicate credits/refunds. Refund created/failed events are references only.
- Multiple partial refunds are supported within recorded cash and remaining credit/tax bounds. Physical returns use return.received with orderId, returnId, lines and qualityApproved; financial refunds do not automatically return stock. Returns default to pending inspection; source quality approval is honored only after an administrator selects the trusted source approval policy.
Status, errors and safe recovery
RECEIVED/PROCESSING indicate queue work; WAITING_DEPENDENCY indicates a missing earlier business record. COMPLETED indicates processing completed; inspect result.accountingEffect because references and tests can complete with NONE. REVIEW_REQUIRED requires operator correction; RETRYABLE_FAILED is temporary; DEAD_LETTER requires recovery.
400 rejects invalid dates, amounts, tax/rates or unsupported events. 401 rejects invalid/expired/revoked credentials; 403 rejects missing scope. 409 rejects reused event identity with different content. 402 indicates plan capacity; 429 indicates rate limits. Retry temporary failures with backoff and unchanged IDs/body. Do not retry validation errors blindly.
Connection or module access revoked after receipt prevents further processing. Result signing/notifications do not imply gateway cash movement; only confirmed source capture/refund events do. Reconciliation shows the current invoice page and latest 25 settlement/dispute outcomes; use Ledger journal filters for older entries.
Managed provider setup requires administrator verification before live use. Optional deferred service revenue uses accountant-approved recognition; it does not schedule renewals or execute gateway refunds. Source applications own billing schedules, entitlement changes and actual cash execution.
Related guides
Ask your workspace administrator for access or contact support with the page name and a sanitized error message.