vsmsconnect — API — xero

← all endpoints

GET/api/v1/accounting/xero/start auth: jwt (administrator)

Begin a Xero OAuth2 authorisation flow for the caller's business. Generates a PKCE verifier + state, persists them in OAuthStates, and returns the Xero authorisation URL the app/browser must redirect to. Rate-limited (10/min open bucket).

handlers: XeroAuthController.start

Inputs

No parameters.

Outputs

200
APIResponseDataObject<XeroStartResponse>
`{ authorizationUrl }` — the Xero hosted-consent URL.
409
APIError
BUSINESS_CONTEXT_REQUIRED — caller has not completed business registration.
429
APIError
Rate limit exceeded.
GET/api/v1/accounting/xero/callback auth: none

OAuth callback target hit by the user's browser after Xero consent. Validates the state (constant-time), exchanges the authorisation code, upserts XeroConnections rows for every tenant returned, and either 302-redirects (desktop) or serves a 200 text/html bridge page (mobile UA) that opens the native `vsms://auth/xero/...` deep link.

handlers: XeroAuthController.callback

Inputs

namerequireddefaultdescription
code query
string
no—OAuth authorisation code returned by Xero on success.
state query
string
no—Opaque PKCE state value previously issued by `/start`. Constant-time compared against OAuthStates row.
error query
string
no—Set by Xero when the user denies consent or the flow fails. Triggers the failure bridge / failure deep link.
error_description query
string
no—Human-readable error message from Xero (logged, sanitised before being exposed).

Outputs

302
redirect
Desktop User-Agent — Location header points at `${XERO_FRONTEND_WEB_BASE_URL}/auth/xero/{success|failure}` carrying tenant name or sanitised error code.
200
text/html
Mobile User-Agent — HTML bridge page that tries `XERO_FRONTEND_SUCCESS_URL` / `XERO_FRONTEND_FAILURE_URL` deep link then falls through to the web URL after 900 ms.
GET/api/v1/accounting/xero/status auth: jwt

Connection summary for the caller's business — whether a Xero connection exists, the active tenant, the list of authorised tenants (no tokens), and the current `autoFiscaliseInvoices` flag.

handlers: XeroAuthController.status

Inputs

No parameters.

Outputs

200
APIResponseDataObject<XeroStatusResponse>
Connection summary including `connected`, `activeTenant`, `tenants[]`, and `autoFiscaliseInvoices`.
409
APIError
BUSINESS_CONTEXT_REQUIRED — caller has no businessId on the JWT.
GET/api/v1/accounting/xero/tenants auth: jwt

List every Xero tenant authorised for the caller's business. Each entry carries `isActive` to indicate which tenant is the current target of sync / webhooks.

handlers: XeroAuthController.tenants

Inputs

No parameters.

Outputs

200
APIResponseDataList<XeroTenantSummary>
Tenant list with `tenantName`, `tenantId`, `isActive`.
POST/api/v1/accounting/xero/tenants/:xeroTenantId/activate auth: jwt (administrator)

Switch the active Xero tenant for the caller's business. Flips `IsActive` on XeroConnections rows so subsequent sync / webhook traffic uses the chosen tenant.

handlers: XeroAuthController.activateTenant

Inputs

namerequireddefaultdescription
xeroTenantId path
uuid
yes—Xero tenant GUID to mark active.

Outputs

200
APIResponseDataObject<XeroTenantSummary>
Newly active tenant summary.
404
APIError
Tenant not found for this business.
DELETE/api/v1/accounting/xero/connection auth: jwt (administrator)

Hard-disconnect Xero for the caller's business — deletes every XeroConnections row, drops encrypted tokens, fires `XERO_DISCONNECTED` audit.

handlers: XeroAuthController.disconnect

Inputs

No parameters.

Outputs

200
APIResponseDataObject<{ deletedCount: number }>
Number of connection rows deleted.