Base 3:16
Guides

Sync to QuickBooks, Xero and HubSpot

Send customers, invoices, payments and credit notes to QuickBooks Online or Xero, and plans, MRR and subscription status to HubSpot — one way, per environment, without code.

Base316 is the system of record for billing. Settings → Integrations copies what it produces into the tools the rest of the business already uses: the books (QuickBooks Online or Xero) and the CRM (HubSpot). Nothing is read back except the vendor's own ids, so a re-sync updates what is there instead of posting it twice.

What goes where

Base316QuickBooks OnlineXeroHubSpot
CustomerCustomer (DisplayName, email, address)Contact (Name, ContactNumber = Base316 id)Company (base316_customer_id)
Finalized invoiceInvoice — lines, DocNumber, due date, taxInvoice ACCREC, AUTHORISED, lines, tax per line—
Voided invoiceInvoice voidedInvoice VOIDED—
Payment (card or recorded)Payment linked to the invoicePayment against the invoice—
Credit noteCreditMemo, applied to the invoiceCredit note ACCRECCREDIT, allocated to the invoice—
Subscription——base316_plan, base316_mrr, base316_subscription_status on the company, and one deal
  • Money goes over exactly. Amounts are rounded once, to the currency's own minor unit — none for JPY and KRW, three places for KWD and BHD — and written into the request as the decimal Base316 rated, never through a floating-point number. Line amounts are split so they add up to the invoice total to the cent.
  • Tax is sent as the amount Base316 charged, against the vendor tax code you map each Base316 tax rate to. A document carrying two rates at once (state plus city) is refused with a message rather than guessed at — map a combined rate at the vendor and retry.
  • Discounts become a discount line in QuickBooks and a negative line in Xero.
  • A credit note on an invoice that is already paid is created but left unapplied: the customer is owed a refund, which is a decision for a person.
  • Prepaid wallet balance applied to an invoice is not sent as a payment. In the books, those invoices show the wallet amount as still open; record the wallet as you account for prepayments today.
  • MRR is the same figure as the revenue report: recurring plan fees and addons, per month, excluding usage; a cancelled subscription is 0.

When it syncs

Every customer, finalized invoice, payment, credit note and subscription change already raises a webhook event. The same events queue a sync job; the scheduler sends queued jobs every fifteen seconds. Nothing waits on the vendor while somebody is checking out.

  • A job sends whatever it depends on first — an invoice sends its customer if the customer has not been sent — so jobs never need to run in order.
  • Rate limits: a 429 pauses that connection until the vendor's Retry-After (a minute when it gives none), without using up an attempt.
  • Outages (timeouts, 5xx) are retried at 30 s, 2 min, 8 min, 32 min and 2 h, then marked failed.
  • A refused document — a missing mapping, a duplicate number — fails at once with the vendor's own message. Fix the cause and press Retry in the sync log.
  • Sync now sends everything that already exists: customers, issued invoices, settled payments and issued credit notes (live subscriptions for HubSpot), skipping anything already sent, with a progress bar.

Connecting

  1. Open Settings → Integrations in the environment you want to connect. Sandbox environments connect to the vendor's sandbox (a QuickBooks sandbox company, the Xero Demo Company, a HubSpot developer test account), and Production environments to live books — the same split as Stripe's test and live modes. Connect each environment separately.
  2. Press Connect, sign in at the vendor and choose the company. You come back to the Integrations screen.
  3. Press Mapping and choose where things post: the QuickBooks product or service (or Xero revenue account) for invoice lines, the account payments land in, and a vendor tax code for each Base316 tax rate. For HubSpot, choose the deal pipeline and stages.
  4. Press Sync now to send what already exists.

Disconnect stops syncing at once, cancels anything queued and revokes the token at the vendor. What is already in the books stays there. Reconnecting the same company resumes where it left off; connecting a different company starts clean, because the old company's ids mean nothing in the new one.

If the vendor stops renewing access (the person who connected it left, or revoked the app), the integration shows Needs reconnecting. Work keeps queuing and is sent once somebody presses Reconnect.

Checking from code

GET /v1/integrations (secret key) lists each vendor's status, mode, the connected account, when it last synced, the last error and how many jobs are pending or failed — base316.integrations.list() in the TypeScript SDK. Connecting is an OAuth consent a person gives in the dashboard, so there is no API to create a connection.

Operating Base316: going live

Base316 itself is registered once with each vendor as the developer app every customer authorizes. Until a deployment has the app's credentials, the integration says Not configured on this deployment and names the variables.

The redirect URI is the same for sandbox and live; register exactly one per deployment. They are {API URL}/integrations/oauth/{vendor}/callback:

DeploymentQuickBooksXeroHubSpot
QAhttps://qa-api.base316.com/integrations/oauth/quickbooks/callbackhttps://qa-api.base316.com/integrations/oauth/xero/callbackhttps://qa-api.base316.com/integrations/oauth/hubspot/callback
Productionhttps://api.base316.com/integrations/oauth/quickbooks/callbackhttps://api.base316.com/integrations/oauth/xero/callbackhttps://api.base316.com/integrations/oauth/hubspot/callback
Localhttp://localhost:3110/integrations/oauth/quickbooks/callbackhttp://localhost:3110/integrations/oauth/xero/callbackhttp://localhost:3110/integrations/oauth/hubspot/callback

(The billing deployment, if it should sync too, adds the same paths on https://billing-api.base316.com.) A deployment that is not BASE316_ENV=production — QA included — only ever uses sandbox credentials.

The variables go into the API and scheduler workloads' secrets (base316-secrets); the scheduler is what sends.

QuickBooks Online (Intuit Developer)

  1. At developer.intuit.com, create an app with the Accounting scope (com.intuit.quickbooks.accounting).
  2. Under Development → Keys & credentials, add the redirect URIs above and copy the development keys into BASE316_QUICKBOOKS_SANDBOX_CLIENT_ID and BASE316_QUICKBOOKS_SANDBOX_CLIENT_SECRET. These work only against sandbox companies (sandbox-quickbooks.api.intuit.com).
  3. For live books, complete Intuit's production questionnaire, add the production redirect URI under Production → Keys & credentials, and set BASE316_QUICKBOOKS_CLIENT_ID and BASE316_QUICKBOOKS_CLIENT_SECRET on the production deployment.
  4. Base316 pins minorversion=75 and sends requestid on every create.

Xero (Xero Developer)

  1. At developer.xero.com, create a Web app. New apps get granular scopes; Base316 requests openid profile email offline_access accounting.contacts accounting.invoices accounting.payments accounting.settings.read.
  2. Add the redirect URIs above, and set BASE316_XERO_CLIENT_ID and BASE316_XERO_CLIENT_SECRET. Xero has no separate sandbox keys — a sandbox environment authorizes the same app against the Demo Company — but BASE316_XERO_SANDBOX_CLIENT_ID / _SECRET are honoured if you register a second app for QA.
  3. Xero limits each organisation to 60 calls a minute and 5,000 a day; the sync waits out a 429 rather than failing.

HubSpot (public app)

  1. In a HubSpot developer account, create a public app using OAuth, with the scopes oauth crm.objects.companies.read crm.objects.companies.write crm.objects.deals.read crm.objects.deals.write crm.schemas.companies.read crm.schemas.companies.write crm.schemas.deals.read crm.schemas.deals.write. The schema scopes let the first sync create the base316 property group.
  2. Add the redirect URIs above, and set BASE316_HUBSPOT_CLIENT_ID and BASE316_HUBSPOT_CLIENT_SECRET. Sandbox environments connect to a developer test account through the same app.
  3. Tokens are exchanged and refreshed at HubSpot's v3 endpoint (/oauth/v3/token); v1 stops working on 16 February 2027.

Tokens are encrypted at rest under BASE316_ENCRYPTION_KEY and are moved by a key rotation like every other stored credential.

On this page