Skip to main content
This page explains the concepts. To turn it on step by step, follow the accounting-sync setup guide.

Supported Accounting Integrations

Recurso integrates with popular accounting platforms so your billing data flows directly into your general ledger. Connect your accounting software via OAuth, and Recurso will automatically sync invoices, payments, and credit notes.

QuickBooks

Pushes invoices, payments, and credit notes into QuickBooks (one-way)

Xero

Automated journal entries and invoice sync

Tally

Coming soon — sync billing data to Tally ERP

How It Works

Supported Providers

Setting Up OAuth Credentials

Before connecting an accounting provider, configure the OAuth credentials for your tenant.
1

Register an app with the provider

Create a developer app in QuickBooks Developer Portal or Xero Developer Portal. Set the redirect URI to https://api.recurso.dev/v1/accounting/callback/:provider.
2

Add credentials to Recurso

Provide the client ID and client secret via your Recurso dashboard or environment configuration.QuickBooks:
  • QBO_CLIENT_ID — your QuickBooks app client ID
  • QBO_CLIENT_SECRET — your QuickBooks app client secret
Xero:
  • XERO_CLIENT_ID — your Xero app client ID
  • XERO_CLIENT_SECRET — your Xero app client secret
3

Initiate the connection

Call the connect endpoint to begin the OAuth flow.
Store OAuth client secrets securely. Never expose them in client-side code or version control. Recurso encrypts stored tokens at rest.

Connect a Provider

Initiate the OAuth flow to link your accounting software.

Connect Parameters

OAuth State Security

Recurso generates an HMAC-signed state parameter for each OAuth flow to prevent CSRF attacks. The callback handler validates the state signature before exchanging the authorization code for tokens.

OAuth Callback

The callback at GET /v1/accounting/callback/:provider is handled automatically. Recurso validates the HMAC state, exchanges the code for tokens, saves the connection with sync_status: "idle", and redirects the user back.
You do not need to implement the callback handler yourself. Recurso manages the full token exchange and storage, including automatic token refresh.

List Connections

Retrieve all accounting connections for your tenant.

AccountingConnection Object

Disconnect a Provider

Remove an accounting connection. This revokes tokens and stops all syncing.
Disconnecting does not delete previously synced data from the accounting provider. It only stops future syncs and revokes Recurso’s access tokens.

Trigger a Sync

Manually trigger a sync to push billing data to your connected accounting platforms.
Recurso also runs automatic syncs on a daily schedule. The two differ in scope: the daily sync is incremental (unchanged entities are skipped — see below), while the manual endpoint forces a full re-push of every mapped entity. Use manual sync when you need data pushed immediately, or to repair provider-side records that were edited or deleted outside Recurso.

Incremental Sync (Dirty Tracking)

The scheduled daily sync does not re-push every mapped entity. An entity is skipped as unchanged when both of these hold:
  1. it already has an ID mapping on the connection (it has synced successfully before), and
  2. its source updated_at is not newer than the mapping’s last successful push.
Anything that fails those checks is (re-)pushed. In practice a re-push is triggered by whatever bumps the entity’s updated_at — editing a customer’s billing details, an invoice changing status, a plan rename, and so on. When it cannot be sure, the sync fails open and pushes anyway: entities with no mapping yet, rows predating change tracking (zero updated_at), or a failed mapping lookup are all treated as dirty. Skipping never risks a stale ledger; the worst case is a redundant push. Each connection’s sync log records how much work was actually done:
The manual POST /v1/accounting/sync endpoint sets force=true and bypasses the dirty check entirely — every mapped entity is re-pushed.

Xero Item Linkage

Plans sync to Xero as Items, and invoice lines reference them by item Code (Xero links lines to items by code, not by ID):
  • the Item is created with a Code derived from the plan’s code (e.g. PRO-USD), not an internal ID
  • invoice lines for that plan carry the same value as ItemCode, so revenue rolls up under the item in Xero reports
  • Xero caps item codes at 30 characters; Recurso applies the same truncation on both sides so the item’s Code and the line’s ItemCode always match
Invoice lines for plans that have not synced as items yet are sent bare, with just a description and the default sales account code.

Check Sync Status

View recent sync logs to monitor sync health and troubleshoot failures.

Sync Status Values

What Data Syncs

Recurso pushes the following billing objects to your accounting platform:
All finalized invoices are synced as invoices/bills in the accounting platform. Fields mapped include invoice number, customer name, line items, tax amounts, due date, and currency.
Successful payments are synced as payment receipts linked to their corresponding invoices. This keeps your accounts receivable accurate and up to date.
Credit notes and refunds are synced as credit memos. They are automatically linked to the original invoice in the accounting platform.

Monitoring Syncs

There are no accounting.* webhook event types — monitor sync state with the sync-status endpoint (connection, last sync time, and last error) and the sync log in the dashboard’s Integrations page.

Best Practices

Monitor Sync Logs

Check sync status regularly to catch token expirations and mapping errors early

Re-authorize Proactively

Refresh connections before tokens expire to avoid sync interruptions

Use Automatic Syncs

Let Recurso handle scheduled syncs rather than triggering manual syncs for every invoice

Reconcile Monthly

Cross-check synced totals against your accounting platform at the end of each month
If a sync fails due to an expired token, Recurso will attempt an automatic token refresh. If the refresh also fails, you will receive an accounting.sync_failed webhook and the connection’s is_active flag will be set to false. Re-authorize by calling the connect endpoint again.