Changelog
Recent updates, new features, and improvements to the RevKeen platform.
Stay up to date with everything we ship. Major releases are announced on our blog — this page covers the full history.
October 2026
Usage events: a reused idempotency key with a different payload is refused (behaviour change)
POST /v2/usage-eventsnow compares the payload when anidempotency_keyis reused. The same payload still returnsduplicate. A different payload returns409idempotency_key_reusedwithexisting_event_idfor a single event, or aconflictresult inside a207batch, and nothing is recorded.- Before, the changed event was returned as
duplicateand silently dropped. - This is switched on account by account; you will be told before it reaches yours. See Usage-based billing.
- The batch response
summaryis now documented as it has always been sent:total,ingested,duplicates,skipped,failed, plus the newconflicts. The API reference previously listed aduplicatefield that was never returned.
Send a copy of a Direct Debit advance notice from the dashboard
- A subscription's Direct Debit panel has a Send notice copy button. It emails the customer a copy of the advance notice already sent, for the customer who did not get or lost it. A copy is not a new notice: it changes no amount or date. It is refused when the original bounced or was never sent, or when the collection has changed since. Up to 3 copies per notice, at least 15 minutes apart. See Emails and notices.
prorationBehavior: invoice_immediately is deprecated on the plan-change operations; new opt-in API version 2026-11-02
- On
POST /v2/subscriptions/{id}/change-planandPOST /v2/subscriptions/{id}/preview-change-plan,invoice_immediatelyis applied asnext_invoiceand never charges, as before, on every version up to2026-05-01(the default). Responses now carry aDeprecationheader and aLinkto the versioning guide. Sendnext_invoice. - New opt-in version
2026-11-02: there,invoice_immediatelymeans "charge the card now", which is a dashboard action. A card subscription gets403charge_now_requires_interactive_sessionand any other subscription gets400charge_now_card_only; nothing is changed or charged. Nothing changes for a request that does not sendRevKeen-Version: 2026-11-02: the default stays2026-05-01. - The dashboard's plan change dialog gains "Charge difference now" for active card subscriptions, when it is switched on for your account. The plan changes only if the charge succeeds.
POST /v2/subscriptions starts the trial its price asks for (behaviour change)
- A price with
trial_period_daysnow starts the subscription in a trial. Before,POST /v2/subscriptionsignored the price's trial and only an explicittrialEndstarted one, so the subscription was billed at once. If you create subscriptions on a price that has a default trial and want no trial, sendtrial_period_days: 0. - New optional request field
trial_period_days(0 to 730): the days of trial, counted from the start of the first period. It overrides the price's value. It cannot be combined withtrialEnd: sending both is a400with the codetrial_conflict.trialEndon its own works as before. - A trial does not need a saved
paymentMethodId, but a trial that starts without one cannot collect payment when it ends yet: the subscription moves toincomplete. Save a payment method for the customer before the trial ends.
GET /v2/customers/{id}/state needs three scopes and says when a list is cut short (breaking)
- The snapshot returns the customer's subscriptions and invoices, so the key now needs
customers:read,subscriptions:readandinvoices:read. A key with onlycustomers:readgets403instead of billing data it was not granted. subscriptions.activeandinvoices.overdueare read on their own, so an overdue invoice older than the newest 10, or an active subscription older than the newest 20, is no longer missing. Each list has a*_has_moreflag (active_has_more,all_has_more,overdue_has_more,recent_has_more) that is true when the customer has more rows than the list holds.amount_due_minorisnullwhen an invoice records no amount due. It no longer falls back to the invoice total, or to 0.- The response is typed in the API reference and the SDKs (
CustomerStateCustomer,CustomerStateSubscription,CustomerStateInvoice) instead of free-form objects.
Webhook events mandate.created and mandate.auddis_rejected are removed (deprecated)
- Neither event was ever sent. Both are removed from the event catalogue and the API reference. An AUDDIS rejection is reported as
mandate.suspended. POST /v2/webhook-endpointsandPATCH /v2/webhook-endpoints/{id}still accept both names inenabled_events, as deprecated no-ops. They are stored and returned as before, and they match no event. Remove them from your endpoint configuration; a later release may refuse them with400invalid_event_type.
GET /v2/usage/balance no longer returns cost and margin figures (breaking)
- The response no longer includes
total_cost_minor,total_payment_processing_cost_minor,total_platform_fee_minor,true_net_margin_minorortrue_net_margin_percent, normargin_minorandtotal_cost_minoron each meter. These are your own costs and fees, not figures for a customer-facing balance; they remain in the dashboard. - Balances, usage and the rest of the response are unchanged. If you read any of the removed fields, stop reading them before you update.
Customer create and update take snake_case fields (behaviour change)
POST /v2/customersandPATCH /v2/customers/{id}takeexternal_id,company_name,address_line1,address_line2,postal_codeandcustom_fields, matching the rest of the API. An unknown field is now refused with400naming it inparam, instead of being ignored.- The camelCase spellings
externalId,companyName,addressLine1,addressLine2,postalCodeandcustomFieldsare still accepted for now. A request that uses one gets aDeprecationheader and aLinkto the Customers guide. Sending the same field under both spellings with different values is refused with400on the snake_case name.
Checkout session create takes snake_case fields; three path parameters are renamed (behaviour change)
POST /v2/checkout-sessionstakesinvoice_id,success_url,cancel_url,allowed_methodsandcompanion_device_id, matching the rest of the API. An unknown field is refused with400naming it.- The camelCase spellings
invoiceId,successUrl,cancelUrl,allowedMethodsandcompanionDeviceIdare still accepted for now. A request that uses one gets aDeprecationheader and aLinkto the Checkout sessions page. Move to the snake_case names. - Three path parameters are renamed in the API reference and in the SDKs:
lineIdtoline_id(PATCHandDELETE /v2/cart-sessions/{id}/line-items/{line_id}),productIdtoproduct_id(GET /v2/storefront/products/{product_id}) andoriginIdtoorigin_id(DELETE /v2/storefront/origins/{origin_id}). A path parameter is matched by position, so no URL you send changes. If you call these operations through a generated SDK, the argument name changes when you update it.
checkout.session.completed and checkout.session.expired webhooks are now sent
- Both events were listed in the event catalogue but never delivered.
checkout.session.completedis now sent when a checkout session is paid, andcheckout.session.expiredwhen an open or pending session is expired withPOST /v2/checkout-sessions/{id}/expire. A session that only reaches its expiry time is not sent as an event. - A paid checkout also sends
commerce.checkout.completed, which is not part of the catalogue. An endpoint subscribed to*receives both events, each with its ownid;checkout.*receives only thecheckout.session.*events. To act once per payment, subscribe tocheckout.session.completedby name. - The
checkout.session.completedexample showedstatus: "complete". The value iscompleted.
Payment links on /v2 return one consistent shape, with a hosted url (behaviour change)
POST /v2/payment-links,GET /v2/payment-links,GET /v2/payment-links/{id}andPOST /v2/payment-links/{id}/expirenow return the documented snake_case fields. Before, they returned the stored record with camelCase keys such asmerchantId,successUrlandisActive. If you read those keys, switch tomerchant_id,success_urlandstatus.- Every link now carries
url, the hosted checkout page to share with your customer. You no longer need to build it from theid. public_idis gone from the response schema. It was documented as required but was never returned.statusis one ofactive,inactive,archivedorexpired, in responses and in thestatusfilter ofGET /v2/payment-links. A newly created link isactiveand is listed under?status=active. The old valuesdraftandcompletedwere never returned and are now refused as a filter with400.modeisinvoice,subscriptionorcustom. A link created in the dashboard returnscustom.Authorization: Bearer <key>now works on/v2/payment-links, as it does on the rest of the API. Before, onlyX-API-Keywas accepted here. A request with no credential gets401with aWWW-Authenticateheader.POST /v2/payment-links/{id}/expire,/activate,/deactivateand/archiveare no longer marked deprecated. They remain supported.idempotency_keyin the create body is deprecated and ignored. Send theIdempotency-Keyheader: a repeat request with the same header returns the original response.
Change a subscription's plan through the API, with a proration preview
POST /v2/subscriptions/{id}/change-planmoves an active, trialing or past due subscription to another recurring price. Nothing is charged when the plan changes: the difference for the rest of the period is added to (or credited on) the next renewal invoice, as in the dashboard. It needs thesubscriptions:writescope and thesubscription:updatepermission.POST /v2/subscriptions/{id}/preview-change-planshows the same figures without changing anything. It needs thesubscriptions:readscope and thesubscription:readpermission.- A new price with no amount above zero is refused with
400price_amount_missing, in the dashboard too. A Direct Debit subscription is refused with409direct_debit_plan_change_unsupportedand can still be changed from the dashboard. - New webhook event
subscription.plan_changed. See Subscriptions.
Checkout sessions through the API are created for an invoice, and the URL can be read back (behaviour change)
POST /v2/checkout-sessionsnow takes aninvoiceId, and only aninvoiceId. The session charges the invoice's amount due.productIdandamountMinorare removed: a session created from a product and an amount returned a link the hosted checkout page could not open, so no working integration is affected. Sending either, or any key the request does not define, is refused with400that names the key. To collect for something that is not yet an invoice, create the invoice first.- An invoice in
draftis refused with400("Invoice status draft cannot start checkout"), asPOST /v2/invoices/{id}/payalready refuses it. Finalize the invoice first. Issued, sent, approved and other payable statuses still start a session. GET /v2/checkout-sessions/{id}now returns the hosted checkouturlwhile the session ispendingoropenand has not expired, when the request is made with an API key. Before,urlwas alwaysnull. It isnullonce the session can no longer be paid, and for requests made through an OAuth app, an agent or MCP.POST /v2/checkout-sessions/{id}/expireonly expires a session that is stillopenorpending. A session whose payment has started is left alone and the call returns409. An unknown session returns404.
Pause and resume a subscription through the API
POST /v2/subscriptions/{id}/pausepauses an active or trialing subscription immediately, andPOST /v2/subscriptions/{id}/resumeresumes a paused one. Neither takes any parameters. They need thesubscriptions:writescope and thesubscription:pause/subscription:resumepermission. A Direct Debit pause lists the invoices left open indd_collection_stopped.- On resume, the periods that elapsed while the subscription was paused are not billed: it keeps its billing day and the next charge is the next billing date after the resume. A Direct Debit subscription resumes only if its mandate can collect now; otherwise the call is refused with
409mandate_not_usableand the subscription stays paused. - New webhook events
subscription.pausedandsubscription.resumed. See Subscriptions.
Collect an invoice by Direct Debit through the API
POST /v2/invoices/{id}/paywithmode: "collect"andpayment_method: "direct_debit"asks RevKeen to collect the invoice's outstanding amount by Direct Debit, against the customer's active mandate. It returns202. The invoice stays unpaid until the collection settles.- The customer is sent the Direct Debit advance notice first. While that notice is being delivered, the response shows
collection.statusawaiting_advance_notice; the collection is submitted once delivery is confirmed. - The amount collected is always the invoice's outstanding amount. A collect request that sends
amount_minor, or any other field, is refused with400. Card collection is not available through this operation, andpayment_method: "card"is refused with400. - It is refused with
409, and nothing is scheduled, when the invoice is already paid, void, a draft or not collectable, when a Direct Debit collection for it is already pending, when the customer has no usable Direct Debit mandate (mandate_not_usable), or when Direct Debit is not enabled for your account (direct_debit_not_configured). - It needs the
invoices:writescope, and for an API key alsomandates:write, plus thedirect_debit:submit_collectionpermission. Your account owners and admins are emailed whenever an API key starts a collection. Send anIdempotency-Keyheader: a replay schedules nothing a second time.
Record a payment received outside RevKeen through the API (behaviour change)
POST /v2/invoices/{id}/payrecords a payment the customer made outside RevKeen, such as a bank transfer, cash or a cheque, and applies it to the invoice. RevKeen collects no money. Sendmode: "record", apayment_method, areferenceand anamount_minor. All four are required and nothing is defaulted.notesandpaid_atare optional, andpaid_atcannot be in the future.- A payment that covers what is outstanding moves the invoice to
paid; a smaller one moves it topartially_paid. An amount above the invoice total less what is already paid is refused with400. Every recorded payment creates a payment record. - It is refused with
409, and nothing changes, when the invoice is already paid, void, a draft or uncollectible, or when a Direct Debit collection for the invoice is still pending. Cancel the collection first. - It needs the
invoices:writescope and theinvoice:mark_paidpermission. Send anIdempotency-Keyheader to make retries safe: a replay records nothing a second time. - Behaviour change:
PATCH /v2/invoices/{id}no longer acceptsstatusset topaidorpartially_paid. It returns400. UsePOST /v2/invoices/{id}/payinstead, so a paid invoice always has a payment record.
Detach a payment method and set the default customer payment method through the API
POST /v2/payment-methods/{id}/detachremoves a stored payment method from a customer. It is refused with409payment_method_in_use, and nothing changes, while the method is the customer's default or a subscription that is active, trialing or past due bills on it.POST /v2/payment-methods/{id}/set-defaultmakes a payment method the customer's default. It changes the customer's default only; existing subscriptions keep the payment method they bill on. A payment method that is no longer active is refused with409payment_method_not_active.- Both need the
payment_methods:writescope. Send anIdempotency-Keyheader to make retries safe.
POST /v2/subscriptions refuses fields it never used (breaking)
businessId,planId,billingAnchorDay,pauseAtPeriodEnd,cancelAtPeriodEnd,billingEndStrategy,billingEndsOn,billingMaxCyclesandprorationModewere accepted and ignored: the subscription was created without them, so a request for a fixed term produced a subscription that never ends. They are removed from the request schema, and a request that sends one gets400naming the field. Any other unknown field is refused the same way.- To end a subscription, call
POST /v2/subscriptions/{id}/cancel(immediately, or at the end of the period). Remove the nine fields from your requests; generated clients lose them from the create request type.
September 2026
Product collections
- Group products into collections from the dashboard or the API (
/v2/product-collections). A product can belong to more than one collection. See Core concepts.
RevKeen Commerce add-on
- Fee schedule
fees-2026-09-28-v1adds the optional RevKeen Commerce add-on, a managed RevKeen Storefront for merchants without a website: £29/month, included on Pro and Unlimited. No existing fee changed. See Fees.
Sanity Cart integration removed
- The merchant Sanity Cart integration has been removed, including the CLI's Sanity project scaffolding. Use RevKeen Cart with your own storefront instead; see Build a storefront.
RevKeen CLI v0.2.1 and v0.3.0
- CLI v0.2.1 was released on 27 September and v0.3.0 on 30 September. See CLI.
Subscriptions created through the API now bill their first period (behaviour change)
POST /v2/subscriptionsnow bills the first period when the subscription is created, at the price's amount for the quantity and at the price's interval. Before, an API-created subscription had no amount and never collected.- On card, the saved card is charged straight away.
paymentMethodIdis required unless the subscription starts in a trial; without it you get400payment_method_required. - By Direct Debit, the first collection is scheduled with advance notice, typically 5 to 7 working days after creation.
- With a trial, nothing is billed until the trial ends.
- A price that cannot be billed (no amount, not recurring, or pay-what-you-want) is refused with
400.
Installing and uninstalling apps now needs an Owner, Admin or Developer role (behaviour change)
- Installing or uninstalling an app from Apps → Marketplace now needs the Owner, Admin or Developer role. Finance, Support and Viewer members get
403and nothing is installed or removed. - Apps you already have installed are unaffected.
Create a Direct Debit subscription through the API
POST /v2/subscriptionsacceptspaymentRail: "direct_debit"with themandateIdof an active mandate the customer already has. GBP only. See Start from the API.- It needs a signed-in team member or an OAuth app with access to mandates. API keys are refused with
403, the same answer they get from the mandate endpoints. - Subscription responses now include
payment_rail,mandate_idandpayment_method_id. - Subscriptions created through the API don't collect payment yet, on card or Direct Debit. They are created without an amount, so no renewal takes a payment. To collect today, send the customer a checkout link or an invoice.
POST /v2/subscriptions now keeps paymentMethodId, quantity and productId (behaviour change)
- These fields were accepted and then silently ignored: the subscription was stored with no payment method, a quantity of 1 and no product. They are now stored.
- They are also checked. A
paymentMethodIdthat is not this customer's active payment method returns404payment_method_not_found. AproductIdthat is not yours returns404product_not_found, and one that does not match the price's product returns400product_price_mismatch. - If you were sending any of these, check that the values are the ones you intend, especially
quantity.
Payment-link create now applies every documented field (behaviour change)
POST /v2/payment-linksnow reads the snake_case fields it documents. A create with onlyproduct_idsworks; it previously returned400.- This affected the TypeScript, Go and PHP SDKs as well as direct API calls, because all of them send snake_case.
success_url,cancel_url,expires_at,selection_mode,default_product_idandhighlighted_product_idare now applied. Before, they were accepted and silently ignored, and every link expired after 7 days whateverexpires_atsaid. The bodyidempotency_keyis not applied: send theIdempotency-Keyheader instead.- If you rely on the 7-day expiry, leave
expires_atout. A link created without it still expires after 7 days. - A
single_selectlink whosedefault_product_idorhighlighted_product_idis not one of the link's products is now rejected with400. - These documented fields still have no effect on create:
redirect_url,collect_shipping,allow_promotion_codes,amount_minor,invoice_id,subscription_idandtax_behavior. - To retry a create safely, send the
Idempotency-Keyheader.
metadata.created_via is reserved on payment links
created_viain payment-linkmetadatais now set only by RevKeen. It records where a link was created.- If you send it on
POST /v2/payment-linksor when updating a link, it is dropped without an error. The rest of yourmetadatais stored as before. See Checkout Links.
Changing a payment link's state now needs payment_links:write
POST /v2/payment-links/{id}/expire,/activateand/archive, andPATCH /v2/payment-links/{id}/status, now require an API key with thepayment_links:writescope, like create and update already did. A key without it gets403.- Keys with
payment_links:writeorpayment_links:*, and dashboard sessions, are unaffected.
No available payment method now returns 503 instead of 500
POST /v2/checkout-sessionsandPOST /v2/cart-sessions/{id}/convertnow return503withcode: "NO_PAYMENT_METHODS_AVAILABLE"when you have no online payment method enabled and live for the checkout, or none that can collect its currency. They previously returned an undocumented500.- No session is created and the cart stays
open. Enable a payment method in Settings → Payments, then retry. Other errors, and successful responses, are unchanged.
Retired: order create, order pay and tax-rate endpoints
POST /v2/orders,POST /v2/orders/{id}/payand the five/v2/tax-ratesendpoints are removed. They were not part of the published API reference, returned an error on every request since they were introduced, and never created, paid for or stored anything.- Orders are created when a checkout is paid. Listing, retrieving, updating, cancelling and fulfilling orders is unchanged.
- The
orders_createMCP tool is removed with them. It was never exposed to connected apps.
A new subscription takes its price's currency (behaviour change)
POST /v2/subscriptionswith nocurrencynow creates the subscription in its price's currency. It previously storedUSDwhatever the price was priced in. If the price has no currency, your default currency (Settings → Regional) is used.- A
currencythat differs from the price's currency is now rejected with400, because the subscription renews at the price's amount and RevKeen does not convert between currencies. Leavecurrencyout, or send the price's own. - The published OpenAPI no longer shows
USDas a default for this field.
Omitted currency uses your default currency (behaviour change)
- A
/v2request that leaves outcurrencynow uses your default currency, the one you choose in Settings → Regional. It previously usedUSD. - This covers creating checkout sessions, payment intents and products, ad-hoc
price_dataon payment links, and the external product sync endpoints. The published OpenAPI no longer showsUSDas a default for these fields. - Sending
currencyexplicitly is unchanged, and is still the clearest way to be certain which currency a record gets. On payment links, the link's currency comes from its products; the top-levelcurrencyfield is not used.
Canva (beta)
- Connect your own Canva account from Apps → Marketplace to edit a design in Canva and import it as an image. You pay Canva directly for any subscription. See Product images and shared media.
September 2026 documentation update
Reviewed on 5 September: the guides now distinguish hosted checkout V13 from hosted invoice V6, document approved-origin checkout embedding, correct Cart SDK calls and usage deduplication behavior, and explain customer-scoped portal sessions. Start with checkout integration, usage integration, and portal integration.
Client releases have their own availability. Consult the release matrix before adopting candidate CLI or regenerated SDK methods. This documentation update does not declare an unpublished package or a beta integration generally available.
August 2026
Canonical API error contract (breaking for error-code branching)
403responses now carrytype: authorization_error. They were previously typedauthentication_error, so401and403can finally be told apart from the body alone.- Error codes are lower-snake-case throughout.
IP_NOT_ALLOWEDis nowip_not_allowed; the full401and403vocabularies are listed on Errors. - Every
401now returns aWWW-Authenticate: Bearer realm="revkeen-api"challenge header. - Every
429now returns a deterministicRetry-Afterin whole seconds, andX-RateLimit-Resetis a UNIX timestamp in seconds (it previously emitted milliseconds, contradicting the documented contract). 403scope denials no longer echo the granted scopes of the presented key.- The
unkey_key_idfield is removed from Cart API key status responses.idis the stable public identifier for a Cart key.
Checkout V13 multi-rail hosted checkout
- Hosted checkout collects card, Direct Debit, and wallets (Apple Pay / Google Pay) when those rails are enabled for the merchant.
- Invoice payers land on the hosted invoice page at
/i/{token}instead of hopping to a generic checkout link. - The public embed bundle at
https://releases.revkeen.com/embed/v1/checkout.jsmounts Direct Debit capture and a cart drawer on merchant sites. Hosted checkout can also be mounted in an approved-origin iframe; see the embed guide for callback limitations.
Direct Debit day-2
- Mandate request, Active-status gate, advance notice, collections, and indemnity handling are documented in the Direct Debit section, including emails and the customer, invoice, and checkout starting points.
- Customer portal can list mandates, cancel, and run change-of-bank with OTP.
Apps Marketplace
- Integrations install from Apps → Marketplace, not Settings.
- Slack, Linear, HubSpot, PracticeHub, Quaderno, Terminal, Messaging, Cart, and Customer Intelligence are in the catalog. Xero and QuickBooks Online remain in development and are not installable.
Official SDK release channels
- Current availability and source locations are on Official SDKs.
- TypeScript:
npm install @revkeen/sdk(npm · GitHub). - Go: canonical module
github.com/revkeen/sdk-go; reviewed public tag pending (GitHub). - PHP: generated source is on GitHub; supported package channel coming soon.
- There is no Python SDK.
July 2026
Direct Debit mandate journey
- Hosted Bacs mandate requests with London & Zurich bureau submission.
- Statuses: Awaiting setup, Submitted, Active, Failed, Suspended, Cancelled.
- Do not collect until the mandate is Active.
Usage meters in the dashboard
- Merchants manage meters under Products → Meters.
- Customer-facing balances use
GET /v2/usage/balance. A dedicated usage-analytics dashboard is not live.
June 2026
API keys — full key lifecycle
- Scope enforcement — keys are scoped and scopes are enforced on every request; a read-only key can never move money.
- IP allowlists — restrict a key to specific IPs/CIDR ranges; off-allowlist requests return
403 IP_NOT_ALLOWED. - Zero-downtime rolling — rotate a key with an overlap window (default 24h, up to 168h) so you can cut dependents over with no downtime.
- Last-used + inventory — see when each key was last used and which keys have gone stale (90+ days).
- Audit trail — every key event (created / rolled / revoked) is recorded with actor and timestamp.
- Immediate, durable revocation — revoked keys stop authenticating right away and stay blocked through restarts.
- See Authentication → Key lifecycle & security.
Linear and HubSpot via Nango
- Merchants connect Linear (workflow issue create) and HubSpot (read-only CRM context) through Nango OAuth from the Apps Marketplace.
March 2026
AI Copilot — Proactive Intelligence & Processing-Cost Tools
- MCP customer-v1 tools available as a governed, read-only billing surface
- Proactive alerts for processing-cost changes, churn risk, and dunning opportunities
- Wording updated 27 Sep 2026. Processing-cost figures cover recorded Direct Debit collection costs only; card processing fees are not recorded.
- Natural language queries across invoices, subscriptions, and revenue data
- Full-page chat interface in dashboard with visual output
Marketing Site Rework
- 5 new product pages: Payments, Billing, Intelligence, Platform, Future
- Interactive savings calculator on homepage and pricing page
- PostHog page view and CTA tracking across all pages
Search & Indexing
- Full-text search indexing for customers, invoices, and products
- Background indexing via cron-worker with BullMQ queue
February 2026
Customer Health Scores
- AI-powered health scoring (0–100) for every customer
- Factors: payment history, engagement, revenue trend, dispute rate
- Health score column added to customer list and detail views
January 2026
Usage-Based Billing
- Meter creation and usage event ingestion API
- Support for sum, count, max, and unique aggregation types
- Usage-based line items on invoices with configurable billing periods
Terminal Payments — PAX A920
- Full POS connector architecture: Cloud ↔ Tauri Connector ↔ PAX Terminal
- Device registration, pairing, and heartbeat monitoring
- In-person sale, refund, and void operations via dashboard and API
- Real-time payment status via SSE
December 2025
Platform Launch
- Engine-API on AWS ECS Fargate (Frankfurt)
- PostgreSQL 18 on RDS with row-level security
- Dashboard and Checkout on Vercel (global edge)
- Cron-worker and background services on Hetzner
Core Billing Engine
- Products with one-time, recurring, and usage-based pricing
- Subscription lifecycle: trials, grace periods, proration, cancellation
- Invoice pipeline: draft → open → finalized → paid
- AI-powered dunning with configurable retry schedules
Payment Processing
- NMI gateway integration with gateway-agnostic adapter layer
- Card-on-file and merchant-initiated transaction compliance
- Unified transaction model: sale, refund, void, capture, dispute
- Pay by Bank / open banking collection is not a live merchant checkout rail. Do not treat the December 2025 launch note as a current product claim.
Developer Tools
- REST API under
/v2/* - Generated TypeScript, Go, and PHP SDK source; see current SDK availability
- Webhook event types with signature verification
- Interactive API playground powered by fumadocs-openapi
- MCP server for AI agent billing automation
Integrations
- PracticeHub (patients, packages, appointments sync)
- Slack notifications
- Quaderno tax automation
- Xero and QuickBooks Online accounting sync was announced at launch and remains in development. It is not installable from the Apps Marketplace.
Owner: david · Reviewed 30 Sep 2026 · Next review 30 Oct 2026