Integrations API
REST endpoints for integrations in SKUFlow: 18 operations with parameters, request and response fields, permissions and curl examples.
Base URL: https://app.skuflow.ai/api. Every call needs Authorization: Bearer skf_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.
| Operation | Endpoint | Risk |
|---|---|---|
| List integrations | GET /organizations/{organizationId}/integrations | read |
| Start connecting QuickBooks | POST /organizations/{organizationId}/integrations/quickbooks/connect | sensitive |
| Disconnect QuickBooks | POST /organizations/{organizationId}/integrations/quickbooks/disconnect | sensitive |
| QuickBooks mappings | GET /organizations/{organizationId}/integrations/quickbooks/settings | read |
| Set the QuickBooks mappings | PATCH /organizations/{organizationId}/integrations/quickbooks/settings | write |
| QuickBooks sync log | GET /organizations/{organizationId}/integrations/quickbooks/sync-log | read |
| Push a record to QuickBooks now | POST /organizations/{organizationId}/integrations/quickbooks/resync | write |
| Run the QuickBooks sync now | POST /organizations/{organizationId}/integrations/quickbooks/sync | write |
| A record's QuickBooks status | GET /organizations/{organizationId}/integrations/quickbooks/records/{entityType}/{entityId} | read |
| Start connecting Xero | POST /organizations/{organizationId}/integrations/xero/connect | sensitive |
| Choose the Xero organisation | POST /organizations/{organizationId}/integrations/xero/tenant | sensitive |
| Disconnect Xero | POST /organizations/{organizationId}/integrations/xero/disconnect | sensitive |
| Xero mappings | GET /organizations/{organizationId}/integrations/xero/settings | read |
| Set the Xero mappings | PATCH /organizations/{organizationId}/integrations/xero/settings | write |
| Xero sync log | GET /organizations/{organizationId}/integrations/xero/sync-log | read |
| Push a record to Xero now | POST /organizations/{organizationId}/integrations/xero/resync | write |
| Run the Xero sync now | POST /organizations/{organizationId}/integrations/xero/sync | write |
| A record's Xero status | GET /organizations/{organizationId}/integrations/xero/records/{entityType}/{entityId} | read |
List integrations
GET /organizations/{organizationId}/integrations
The accounting integrations, QuickBooks Online and Xero: for each, whether the server is configured for it and in which mode (mock, sandbox / production or live), the variables still missing, the connection, mappings, sync counts and records synced; blockedBy names the other one when it is connected (one accounting system at a time). Xero also lists pendingTenants while a consent that connected several Xero organisations waits for the choice. Start with POST /integrations/quickbooks/connect or POST /integrations/xero/connect. Read-only.
| Permission | integrations.manage (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | integrations_list (toolset settings) |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
quickbooks | object | |
quickbooks.config | object | |
quickbooks.config.mode | enum | One of mock, live, unconfigured. |
quickbooks.config.configured | boolean | |
quickbooks.config.missing | array of string | Environment variables the server still needs. |
quickbooks.config.redirectUri | string | |
quickbooks.config.environment | string | |
quickbooks.connection | object or null | |
quickbooks.connection.id | string | |
quickbooks.connection.status | enum | One of CONNECTED, DISCONNECTED, ERROR. |
quickbooks.connection.environment | string or null | |
quickbooks.connection.companyId | string or null | The QuickBooks realm id. |
quickbooks.connection.companyName | string or null | |
quickbooks.connection.connectedAt | string or null | |
quickbooks.connection.disconnectedAt | string or null | |
quickbooks.connection.lastSyncAt | string or null | |
quickbooks.connection.lastError | string or null | |
quickbooks.connection.mapping | object | |
quickbooks.tasks | object | |
quickbooks.tasks.PENDING | integer | |
quickbooks.tasks.SUCCEEDED | integer | |
quickbooks.tasks.FAILED | integer | |
quickbooks.tasks.DEAD | integer | |
quickbooks.tasks.SKIPPED | integer | |
quickbooks.synced | object | |
quickbooks.synced.CUSTOMER | integer | |
quickbooks.synced.INVOICE | integer | |
quickbooks.synced.PAYMENT | integer | |
quickbooks.synced.REFUND | integer | |
quickbooks.blockedBy | enum or null | Xero is connected: disconnect it before connecting QuickBooks. One of XERO. |
xero | object | |
xero.config | object | |
xero.config.mode | enum | One of mock, live, unconfigured. |
xero.config.configured | boolean | |
xero.config.missing | array of string | Environment variables the server still needs. |
xero.config.redirectUri | string | |
xero.config.scopes | string | The OAuth scopes SKUFlow asks Xero for. |
xero.connection | object or null | |
xero.connection.id | string | |
xero.connection.status | enum | DISCONNECTED with pendingTenants = waiting for the organisation choice. One of CONNECTED, DISCONNECTED, ERROR. |
xero.connection.environment | string or null | |
xero.connection.tenantId | string or null | The Xero organisation (tenant) id. |
xero.connection.tenantName | string or null | |
xero.connection.connectedAt | string or null | |
xero.connection.disconnectedAt | string or null | |
xero.connection.lastSyncAt | string or null | |
xero.connection.lastError | string or null | |
xero.connection.mapping | object | |
xero.pendingTenants | array of object | Xero organisations waiting for the choice (POST /integrations/xero/tenant). |
xero.pendingTenants[].tenantId | string | |
xero.pendingTenants[].tenantName | string | |
xero.blockedBy | enum or null | QuickBooks is connected: disconnect it before connecting Xero. One of QUICKBOOKS. |
xero.tasks | object | |
xero.tasks.PENDING | integer | |
xero.tasks.SUCCEEDED | integer | |
xero.tasks.FAILED | integer | |
xero.tasks.DEAD | integer | |
xero.tasks.SKIPPED | integer | |
xero.synced | object | |
xero.synced.CUSTOMER | integer | |
xero.synced.INVOICE | integer | |
xero.synced.PAYMENT | integer | |
xero.synced.REFUND | integer |
Start connecting QuickBooks
POST /organizations/{organizationId}/integrations/quickbooks/connect
Grants access: returns the Intuit consent URL to open (in mock mode, the callback URL directly). After consent Intuit redirects to /integrations/quickbooks/callback with code, state and realmId; finish with POST /integrations/quickbooks/callback. 400 when the server is not configured (see GET /integrations); 409 while Xero is connected (one accounting system at a time).
| Permission | integrations.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | integrations_quickbooksConnect (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
| Approval | Waits for your approval in SKUFlow when called with an API key or a connected app: the first call answers 428 APPROVAL_REQUIRED; once you approve it, repeat the call with the X-SKUFlow-Approval header. See Approvals. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/connect" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
authorizeUrl | string | |
mode | enum | One of mock, live, unconfigured. |
Disconnect QuickBooks
POST /organizations/{organizationId}/integrations/quickbooks/disconnect
Revokes access: revokes and forgets the QuickBooks tokens; nothing more is pushed and waiting syncs are skipped. The mappings of synced records are kept, so reconnecting the same company updates them rather than creating duplicates.
| Permission | integrations.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | integrations_quickbooksDisconnect (toolset settings) |
| Approval | Over MCP, this tool always waits for your approval in SKUFlow. See Approvals. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/disconnect" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
status | enum | One of CONNECTED, DISCONNECTED, ERROR. |
environment | string or null | |
companyId | string or null | The QuickBooks realm id. |
companyName | string or null | |
connectedAt | string or null | |
disconnectedAt | string or null | |
lastSyncAt | string or null | |
lastError | string or null | |
mapping | object | |
mapping.itemId | string or null | |
mapping.itemName | string or null | |
mapping.incomeAccountId | string or null | |
mapping.incomeAccountName | string or null | |
mapping.taxCodeId | string or null | |
mapping.taxCodeName | string or null | |
mapping.depositAccountId | string or null | |
mapping.depositAccountName | string or null |
QuickBooks mappings
GET /organizations/{organizationId}/integrations/quickbooks/settings
The current mappings and the choices from the connected company: income accounts, items, tax codes and deposit accounts. Invoices need the default item before they can be pushed. 409 when not connected. Read-only.
| Permission | integrations.manage (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | integrations_quickbooksSettings (toolset settings) |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/settings" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
mapping | object | |
mapping.itemId | string or null | |
mapping.itemName | string or null | |
mapping.incomeAccountId | string or null | |
mapping.incomeAccountName | string or null | |
mapping.taxCodeId | string or null | |
mapping.taxCodeName | string or null | |
mapping.depositAccountId | string or null | |
mapping.depositAccountName | string or null | |
options | object | |
options.incomeAccounts | array of object | |
options.incomeAccounts[].id | string | |
options.incomeAccounts[].name | string | |
options.incomeAccounts[].kind | string or null | |
options.depositAccounts | array of object | |
options.depositAccounts[].id | string | |
options.depositAccounts[].name | string | |
options.depositAccounts[].kind | string or null | |
options.items | array of object | |
options.items[].id | string | |
options.items[].name | string | |
options.items[].kind | string or null | |
options.taxCodes | array of object | |
options.taxCodes[].id | string | |
options.taxCodes[].name | string | |
options.taxCodes[].kind | string or null |
Set the QuickBooks mappings
PATCH /organizations/{organizationId}/integrations/quickbooks/settings
Choose the QuickBooks item invoice lines are booked to (required), the income account (for reference), the tax code for taxed invoices (default TAX) and the account payments are deposited to (default Undeposited Funds). Ids from GET /integrations/quickbooks/settings; null clears.
| Permission | integrations.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | integrations_quickbooksUpdateSettings (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
itemId | string or null | No | Up to 100 characters. |
incomeAccountId | string or null | No | Up to 100 characters. |
taxCodeId | string or null | No | Up to 100 characters. |
depositAccountId | string or null | No | Up to 100 characters. |
Example
curl -X PATCH "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/settings" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"itemId":"item_id","incomeAccountId":"incomeAccount_id","taxCodeId":"taxCode_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
itemId | string or null | |
itemName | string or null | |
incomeAccountId | string or null | |
incomeAccountName | string or null | |
taxCodeId | string or null | |
taxCodeName | string or null | |
depositAccountId | string or null | |
depositAccountName | string or null |
QuickBooks sync log
GET /organizations/{organizationId}/integrations/quickbooks/sync-log
Every push to QuickBooks, newest first: the record (customer, invoice, payment, refund), what triggered it (an event or a manual resync), status (PENDING, SUCCEEDED, FAILED = will retry, DEAD, SKIPPED with the reason), attempts, the QuickBooks id and the error. Filter by status or record. Read-only.
| Permission | integrations.manage (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | integrations_quickbooksSyncLog (toolset settings) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
status | query | enum | No | One of PENDING, SUCCEEDED, FAILED, DEAD, SKIPPED. |
entityType | query | enum | No | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | query | string | No | |
limit | query | integer | No | Page size, 1–200. Default 50. |
offset | query | integer | No | Rows to skip; the answer's total counts every match. Default 0. |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/sync-log" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
items | array of object | |
items[].id | string | |
items[].trigger | enum | One of EVENT, MANUAL. |
items[].eventId | string or null | |
items[].eventType | string or null | |
items[].entityType | enum | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
items[].entityId | string | |
items[].status | enum | One of PENDING, SUCCEEDED, FAILED, DEAD, SKIPPED. |
items[].attemptCount | integer | |
items[].nextAttemptAt | string or null | |
items[].completedAt | string or null | |
items[].remoteId | string or null | The record's id in QuickBooks. |
items[].error | string or null | |
items[].createdAt | string | |
items[].updatedAt | string | |
items[].label | string or null | |
total | integer |
Push a record to QuickBooks now
POST /organizations/{organizationId}/integrations/quickbooks/resync
Pushes one customer, invoice, payment or refund to the connected QuickBooks now (creating it, or updating the copy already there: never a duplicate), with what it depends on (an invoice's customer first). Answers the sync task. Drafts and cancelled invoices are skipped.
| Permission | integrations.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | integrations_quickbooksResync (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
entityType | enum | Yes | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | string | Yes |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/resync" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"entityType":"CUSTOMER","entityId":"entity_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
trigger | enum | One of EVENT, MANUAL. |
eventId | string or null | |
eventType | string or null | |
entityType | enum | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | string | |
status | enum | One of PENDING, SUCCEEDED, FAILED, DEAD, SKIPPED. |
attemptCount | integer | |
nextAttemptAt | string or null | |
completedAt | string or null | |
remoteId | string or null | The record's id in QuickBooks. |
error | string or null | |
createdAt | string | |
updatedAt | string |
Run the QuickBooks sync now
POST /organizations/{organizationId}/integrations/quickbooks/sync
Processes this organization's waiting pushes now instead of on the next scheduler run (it also runs every few seconds in the API service). Answers how many were attempted, succeeded, failed, died or were skipped.
| Permission | integrations.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | integrations_quickbooksSyncNow (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/sync" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
attempted | integer | |
succeeded | integer | |
failed | integer | |
dead | integer | |
skipped | integer | |
deferred | integer | Waiting for a rate-limit pause to end (Xero); no attempt used. |
A record's QuickBooks status
GET /organizations/{organizationId}/integrations/quickbooks/records/{entityType}/{entityId}
Whether a customer, invoice, payment or refund is in QuickBooks: SYNCED (with its QuickBooks id and when), PENDING, ERROR (with the reason), SKIPPED (why), NOT_SYNCED, or NOT_CONNECTED when the organization has no QuickBooks. For the badge on invoices and customers. Read-only.
| Permission | invoices.read (Owner, Admin, Staff, Member) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | integrations_syncStatus (toolset settings) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
entityType | path | enum | Yes | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | path | string | Yes |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/quickbooks/records/{entityType}/{entityId}" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
state | enum | One of SYNCED, PENDING, ERROR, SKIPPED, NOT_SYNCED, NOT_CONNECTED. |
remoteId | string or null | |
syncedAt | string or null | |
error | string or null | |
taskId | string or null |
Start connecting Xero
POST /organizations/{organizationId}/integrations/xero/connect
Grants access: returns Xero's consent URL to open (in mock mode, the callback URL directly). After consent Xero redirects to /integrations/xero/callback with code and state; finish with POST /integrations/xero/callback. 400 when the server is not configured (see GET /integrations); 409 while QuickBooks is connected (one accounting system at a time).
| Permission | integrations.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | integrations_xeroConnect (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
| Approval | Waits for your approval in SKUFlow when called with an API key or a connected app: the first call answers 428 APPROVAL_REQUIRED; once you approve it, repeat the call with the X-SKUFlow-Approval header. See Approvals. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/connect" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
authorizeUrl | string | |
mode | enum | One of mock, live, unconfigured. |
Choose the Xero organisation
POST /organizations/{organizationId}/integrations/xero/tenant
Grants access: connects SKUFlow to one of the Xero organisations the last consent connected (pendingTenants of GET /integrations). Choosing a different organisation than before starts with empty mappings. 409 when nothing waits for a choice (connect again) or QuickBooks is connected.
| Permission | integrations.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | integrations_xeroSelectTenant (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
| Approval | Waits for your approval in SKUFlow when called with an API key or a connected app: the first call answers 428 APPROVAL_REQUIRED; once you approve it, repeat the call with the X-SKUFlow-Approval header. See Approvals. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
tenantId | string | Yes | A tenantId from pendingTenants (Xero's organisation id, external). Up to 100 characters. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/tenant" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tenantId":"tenant_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
status | enum | DISCONNECTED with pendingTenants = waiting for the organisation choice. One of CONNECTED, DISCONNECTED, ERROR. |
environment | string or null | |
tenantId | string or null | The Xero organisation (tenant) id. |
tenantName | string or null | |
connectedAt | string or null | |
disconnectedAt | string or null | |
lastSyncAt | string or null | |
lastError | string or null | |
mapping | object | |
mapping.salesAccountCode | string or null | |
mapping.salesAccountName | string or null | |
mapping.taxType | string or null | |
mapping.taxTypeName | string or null | |
mapping.noTaxType | string or null | |
mapping.noTaxTypeName | string or null | |
mapping.taxRates | array of object or null | |
mapping.taxRates[].rate | number | |
mapping.taxRates[].taxType | string | |
mapping.taxRates[].taxTypeName | string or null | |
mapping.paymentAccountId | string or null | |
mapping.paymentAccountName | string or null | |
mapping.itemCode | string or null | |
mapping.itemName | string or null | |
mapping.trackingCategoryId | string or null | |
mapping.trackingCategoryName | string or null | |
mapping.trackingOptionId | string or null | |
mapping.trackingOptionName | string or null |
Disconnect Xero
POST /organizations/{organizationId}/integrations/xero/disconnect
Revokes access: removes the connection at Xero and forgets the tokens; nothing more is pushed and waiting syncs are skipped. The ids of synced records are kept, so reconnecting the same Xero organisation updates them rather than creating duplicates. Also cancels a connection waiting for the organisation choice.
| Permission | integrations.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | integrations_xeroDisconnect (toolset settings) |
| Approval | Over MCP, this tool always waits for your approval in SKUFlow. See Approvals. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/disconnect" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
status | enum | DISCONNECTED with pendingTenants = waiting for the organisation choice. One of CONNECTED, DISCONNECTED, ERROR. |
environment | string or null | |
tenantId | string or null | The Xero organisation (tenant) id. |
tenantName | string or null | |
connectedAt | string or null | |
disconnectedAt | string or null | |
lastSyncAt | string or null | |
lastError | string or null | |
mapping | object | |
mapping.salesAccountCode | string or null | |
mapping.salesAccountName | string or null | |
mapping.taxType | string or null | |
mapping.taxTypeName | string or null | |
mapping.noTaxType | string or null | |
mapping.noTaxTypeName | string or null | |
mapping.taxRates | array of object or null | |
mapping.taxRates[].rate | number | |
mapping.taxRates[].taxType | string | |
mapping.taxRates[].taxTypeName | string or null | |
mapping.paymentAccountId | string or null | |
mapping.paymentAccountName | string or null | |
mapping.itemCode | string or null | |
mapping.itemName | string or null | |
mapping.trackingCategoryId | string or null | |
mapping.trackingCategoryName | string or null | |
mapping.trackingOptionId | string or null | |
mapping.trackingOptionName | string or null |
Xero mappings
GET /organizations/{organizationId}/integrations/xero/settings
The current mappings and the choices from the connected Xero organisation: revenue accounts (by code), bank accounts for payments, tax rates (TaxType with its rate), items and tracking categories with their options; ratesInUse lists the tax rates on this organization's invoices, for per-rate tax mapping. Invoices need the sales account (and a tax rate when taxed), payments and refunds the bank account. 409 when not connected. Read-only.
| Permission | integrations.manage (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | integrations_xeroSettings (toolset settings) |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/settings" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
mapping | object | |
mapping.salesAccountCode | string or null | |
mapping.salesAccountName | string or null | |
mapping.taxType | string or null | |
mapping.taxTypeName | string or null | |
mapping.noTaxType | string or null | |
mapping.noTaxTypeName | string or null | |
mapping.taxRates | array of object or null | |
mapping.taxRates[].rate | number | |
mapping.taxRates[].taxType | string | |
mapping.taxRates[].taxTypeName | string or null | |
mapping.paymentAccountId | string or null | |
mapping.paymentAccountName | string or null | |
mapping.itemCode | string or null | |
mapping.itemName | string or null | |
mapping.trackingCategoryId | string or null | |
mapping.trackingCategoryName | string or null | |
mapping.trackingOptionId | string or null | |
mapping.trackingOptionName | string or null | |
options | object | |
options.salesAccounts | array of object | |
options.salesAccounts[].id | string | |
options.salesAccounts[].name | string | |
options.paymentAccounts | array of object | |
options.paymentAccounts[].id | string | |
options.paymentAccounts[].name | string | |
options.taxRates | array of object | |
options.taxRates[].id | string | |
options.taxRates[].name | string | |
options.taxRates[].rate | number | |
options.items | array of object | |
options.items[].id | string | |
options.items[].name | string | |
options.trackingCategories | array of object | |
options.trackingCategories[].id | string | |
options.trackingCategories[].name | string | |
options.trackingCategories[].options | array of object | |
ratesInUse | array of number | Tax rates (percent) on this organization's invoices. |
Set the Xero mappings
PATCH /organizations/{organizationId}/integrations/xero/settings
Choose the revenue account invoice lines are booked to (salesAccountCode, required), the Xero tax rate for taxed invoices (taxType, required when invoices carry tax) and for untaxed ones (noTaxType, default NONE), optional per-rate overrides (taxRates: SKUFlow rate → TaxType; replaces the list), the bank account payments go into and refunds come out of (paymentAccountId, required for payments and refunds), and optionally an item code and one tracking option for every line (category and option together). Values from GET /integrations/xero/settings; null clears.
| Permission | integrations.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | integrations_xeroUpdateSettings (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
salesAccountCode | string or null | No | A Xero account Code, e.g. 200. Up to 50 characters. |
taxType | string or null | No | A Xero TaxType, e.g. OUTPUT. Up to 100 characters. |
noTaxType | string or null | No | A Xero TaxType, e.g. NONE. Up to 100 characters. |
taxRates | array of object or null | No | Up to 50 items. |
taxRates[].rate | number | Yes | A SKUFlow tax rate in percent. 0–100. |
taxRates[].taxType | string | Yes | A Xero TaxType. Up to 100 characters. |
paymentAccountId | string or null | No | A Xero bank AccountID (external). Up to 100 characters. |
itemCode | string or null | No | A Xero item Code (external). Up to 100 characters. |
trackingCategoryId | string or null | No | A Xero TrackingCategoryID (external). Up to 100 characters. |
trackingOptionId | string or null | No | A TrackingOptionID of that category (external). Up to 100 characters. |
Example
curl -X PATCH "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/settings" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"salesAccountCode":"string","taxType":"string","noTaxType":"string"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
salesAccountCode | string or null | |
salesAccountName | string or null | |
taxType | string or null | |
taxTypeName | string or null | |
noTaxType | string or null | |
noTaxTypeName | string or null | |
taxRates | array of object or null | |
taxRates[].rate | number | |
taxRates[].taxType | string | |
taxRates[].taxTypeName | string or null | |
paymentAccountId | string or null | |
paymentAccountName | string or null | |
itemCode | string or null | |
itemName | string or null | |
trackingCategoryId | string or null | |
trackingCategoryName | string or null | |
trackingOptionId | string or null | |
trackingOptionName | string or null |
Xero sync log
GET /organizations/{organizationId}/integrations/xero/sync-log
Every push to Xero, newest first: the record (customer → Contact, invoice → ACCREC Invoice, payment → Payment, refund → CreditNote and its refund payment), what triggered it (an event or a manual push), status (PENDING, SUCCEEDED, FAILED = will retry, including a rate-limit pause, DEAD, SKIPPED with the reason), attempts, the Xero id and the error. Filter by status or record. Read-only.
| Permission | integrations.manage (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | integrations_xeroSyncLog (toolset settings) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
status | query | enum | No | One of PENDING, SUCCEEDED, FAILED, DEAD, SKIPPED. |
entityType | query | enum | No | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | query | string | No | |
limit | query | integer | No | Page size, 1–200. Default 50. |
offset | query | integer | No | Rows to skip; the answer's total counts every match. Default 0. |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/sync-log" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
items | array of object | |
items[].id | string | |
items[].trigger | enum | One of EVENT, MANUAL. |
items[].eventId | string or null | |
items[].eventType | string or null | |
items[].entityType | enum | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
items[].entityId | string | |
items[].status | enum | One of PENDING, SUCCEEDED, FAILED, DEAD, SKIPPED. |
items[].attemptCount | integer | |
items[].nextAttemptAt | string or null | |
items[].completedAt | string or null | |
items[].remoteId | string or null | The record's id in QuickBooks. |
items[].error | string or null | |
items[].createdAt | string | |
items[].updatedAt | string | |
items[].label | string or null | |
total | integer |
Push a record to Xero now
POST /organizations/{organizationId}/integrations/xero/resync
Pushes one customer, invoice, payment or refund to the connected Xero organisation now (creating it, or updating the copy already there: never a duplicate; a first contact push links an existing Xero contact of the same name), with what it depends on (an invoice's contact first). Answers the sync task. Drafts and cancelled invoices are skipped; an invoice already paid in Xero is not changed (Xero locks it).
| Permission | integrations.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | integrations_xeroResync (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
entityType | enum | Yes | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | string | Yes |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/resync" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"entityType":"CUSTOMER","entityId":"entity_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
trigger | enum | One of EVENT, MANUAL. |
eventId | string or null | |
eventType | string or null | |
entityType | enum | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | string | |
status | enum | One of PENDING, SUCCEEDED, FAILED, DEAD, SKIPPED. |
attemptCount | integer | |
nextAttemptAt | string or null | |
completedAt | string or null | |
remoteId | string or null | The record's id in QuickBooks. |
error | string or null | |
createdAt | string | |
updatedAt | string |
Run the Xero sync now
POST /organizations/{organizationId}/integrations/xero/sync
Processes this organization's waiting pushes now instead of on the next scheduler run. Pushes pause when Xero's rate limit (60 calls a minute, 5,000 a day) is reached and resume after the pause Xero asks for. Answers how many were attempted, succeeded, failed, died, were skipped or deferred.
| Permission | integrations.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | integrations_xeroSyncNow (toolset settings) |
| Plan | QuickBooks Online or Xero sync: the Connect plan and up. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/sync" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
attempted | integer | |
succeeded | integer | |
failed | integer | |
dead | integer | |
skipped | integer | |
deferred | integer | Waiting for a rate-limit pause to end (Xero); no attempt used. |
A record's Xero status
GET /organizations/{organizationId}/integrations/xero/records/{entityType}/{entityId}
Whether a customer, invoice, payment or refund is in Xero: SYNCED (with its Xero id and when), PENDING, ERROR (with the reason), SKIPPED (why), NOT_SYNCED, or NOT_CONNECTED when the organization has no Xero. For the badge on invoices and customers. Read-only.
| Permission | invoices.read (Owner, Admin, Staff, Member) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | integrations_xeroSyncStatus (toolset settings) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
entityType | path | enum | Yes | One of CUSTOMER, INVOICE, PAYMENT, REFUND. |
entityId | path | string | Yes |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/integrations/xero/records/{entityType}/{entityId}" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
state | enum | One of SYNCED, PENDING, ERROR, SKIPPED, NOT_SYNCED, NOT_CONNECTED. |
remoteId | string or null | |
syncedAt | string or null | |
error | string or null | |
taskId | string or null |