API reference QuickBooks
QuickBooks Service API Endpoints
The production API base URL is https://api.tradesmentechnologies.com/.
Authentication
Most endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <your_token>
Mutating requests may carry an Idempotency-Key header so a retry never acts twice — see Idempotent Retries in the Accounts API documentation.
The QuickBooks Callback endpoint is public and does not require authentication (Intuit redirects the user's browser to it).
Base Path
/prod/quickbooks
Overview
The QuickBooks integration connects one QuickBooks Online (QBO) company per Tradesmen company, using Intuit's OAuth 2.0 flow (the only authentication method Intuit supports — users authorize on Intuit's sign-in page rather than entering credentials in the app). Managing the connection (connect/disconnect) requires an ADMIN employee; any employee can read the status.
Once connected, syncing is fully automatic — there are no manual sync endpoints. A record is created in QuickBooks every time money moves:
| Money movement | QuickBooks record |
|---|---|
| TalusPay payment on an invoice (in-app, doc web client, or Tap to Pay) | Customer + Invoice (created lazily if needed) + Payment applied to the invoice. A tip is added to the invoice as a "Tip" line so the payment total matches the amount charged. |
Stripe "Pay over time" payment on an invoice or estimate deposit (stripe-endpoints.md), and its refunds |
Same as the TalusPay rows above (Payment / unapplied Payment; Refund Receipt), keyed on the Stripe PaymentIntent id. |
| TalusPay payment on an estimate (down payment / deposit) | Unapplied customer Payment (QBO estimates are non-posting and cannot receive payments) with a note referencing the estimate. When the job's final invoice later syncs, this payment is automatically applied to it. |
| Document manually marked PAID (cash/check/Zelle) | Invoice: Invoice + Payment for the amount due. Estimate: unapplied Payment for the deposit amount. |
| Document un-marked PAID | The manual QBO Payment is deleted. |
| Refund (in-app or from the TalusPay dashboard) | Refund Receipt for the refunded amount. |
| Void (in-app or from the TalusPay dashboard) | The sale's QBO Payment is deleted (the invoice reopens). |
Sync failures never block payments; the most recent failure is surfaced in lastSyncError on the status endpoint.
Endpoints
Get QuickBooks Connect URL
Start the OAuth flow. Returns the Intuit authorization URL the front end should open in a browser; after the user approves access, Intuit redirects to the callback below and the connection is stored.
- Method: GET
- Path:
/prod/quickbooks/connect - Authentication: Required (Bearer Token, ADMIN employee only)
- Success Response:
200 OK{ "authorizeUrl": "https://appcenter.intuit.com/connect/oauth2?client_id=...&response_type=code&scope=com.intuit.quickbooks.accounting&redirect_uri=...&state=..." } - Error Responses:
401 Unauthorized: Missing or invalid token.403 Forbidden: Caller is not an ADMIN employee.503 Service Unavailable: QuickBooks is not configured on the platform (missing Intuit app credentials).500 Internal Server Error: Unexpected server error.
- Notes: The URL embeds a single-use CSRF
statevalue that expires after 10 minutes. Request a fresh URL each time the user starts the flow.
QuickBooks Callback
OAuth redirect target for Intuit. Not called by the front end directly — the user's browser lands here after approving (or denying) access on Intuit's page.
- Method: GET
- Path:
/prod/quickbooks/callback - Authentication: None (public)
- Query Parameters:
code,state,realmIdon success;errorwhen the user denied access. - Response:
text/html— a small page telling the user the connection succeeded ("You can close this window and return to the app.") or failed. Status200on success,400on a denied/expired/invalid flow,500on server error. - Side Effects: Exchanges the authorization code for tokens, stores the connection, and fetches the QBO company name for display in the status endpoint.
Get QuickBooks Status
Check the company's QuickBooks connection.
- Method: GET
- Path:
/prod/quickbooks/status - Authentication: Required (Bearer Token, any employee)
- Success Response (Connected):
200 OK{ "connected": true, "status": "CONNECTED", "realmId": "9341454759827139", "companyName": "Example Plumbing LLC", "connectedAt": "2026-07-06T18:00:00.000Z", "lastSyncAt": "2026-07-06T19:15:23.000Z", "lastSyncError": null } - Success Response (Never Connected):
200 OK{ "connected": false } - Success Response (Connection Needs Reattention):
200 OK{ "connected": false, "status": "EXPIRED", "realmId": "9341454759827139", "companyName": "Example Plumbing LLC", "connectedAt": "2026-07-06T18:00:00.000Z", "lastSyncAt": "2026-07-06T19:15:23.000Z", "lastSyncError": { "message": "QuickBooks connection expired — please reconnect", "entity": "connection", "at": "2026-07-07T02:11:09.000Z" } } - Error Responses:
401 Unauthorized: Missing or invalid token.500 Internal Server Error: Unexpected server error.
- Notes:
statusis one ofCONNECTED,EXPIRED,REVOKED. WhenstatusisEXPIREDorREVOKEDthe front end should prompt an ADMIN to reconnect via the connect endpoint.lastSyncError(when non-null) describes the most recent sync failure:message(human-readable),entity(the sync action that failed),at(ISO timestamp). A subsequent successful sync clears it. OAuth tokens are never returned.
Disconnect QuickBooks
Disconnect the company's QuickBooks integration.
- Method: POST
- Path:
/prod/quickbooks/disconnect - Authentication: Required (Bearer Token, ADMIN employee only)
- Request Body: None
- Success Response:
200 OK{ "message": "QuickBooks disconnected" } - Error Responses:
401 Unauthorized: Missing or invalid token.403 Forbidden: Caller is not an ADMIN employee.404 Not Found: QuickBooks is not connected.500 Internal Server Error: Unexpected server error.
- Side Effects: Revokes the tokens at Intuit (best-effort) and removes the stored connection. Records already created in QuickBooks are left untouched, and the internal entity mappings are retained so reconnecting to the same QBO company later does not create duplicates.
Sync Behavior Notes
- Traceability: every QuickBooks record carries a reference back to the Tradesmen document it came from, using the document's type and display number (e.g. "Invoice 1-2", "Estimate 3-1", "Custom Document 2-4"). Invoices get
DocNumber= the display number plus an internal memo ("Tradesmen Tech — Invoice 1-2"); payments, deposits, and refund receipts name the document in their memo; card payments and refunds additionally carry the TalusPay transaction id inPaymentRefNum. - Customers are matched in QBO by display name ("First Last") before being created, so books that already contain the customer are reused rather than duplicated.
- Invoices sync lazily — only when money moves against them, not when they are sent. Line items, partial billing (
percent), and additional charges are mirrored from the document; TAX-type charges post as sales tax (falling back to a plain line when the QBO company has sales tax disabled). - Deposits: when an invoice's deposit was itself synced (the estimate deposit exists as a QBO Payment), the invoice posts at its FULL amount and the deposit payment is applied to it — income is stated correctly and no customer credit is stranded. When no synced deposit payment covers it (e.g. the deposit predates the QuickBooks connection) or the invoice uses partial billing, the deposit appears as a negative "Deposit received" line instead so the invoice balance still matches what the customer owes.
- All invoice lines reference a single generic "Services" product/service item (created automatically on first sync against the company's first income account).
- Refunds post as standalone Refund Receipts (reducing income and cash) rather than credit memos applied to the original invoice.
- Every sync operation is idempotent — TalusPay webhook echoes and retries never create duplicate QBO records.
- One-way push only: edits made inside QuickBooks are never pulled back into Tradesmen.