Skip to content

Connections

A connection is one (org, app) consent grant. A user can hold several — one per organisation they've connected your app to. Nothing in the token tells you which org a request is scoped to at any given moment except the URL you call.

Connecting more organisations

Send the user through the same authorize URL again. There is no multi-select — consent is one organisation per pass, the same Xero-style flow used to connect the first one.

If the user is already logged in to LuciBook (the normal case: they just finished connecting their first org, or they still have a session), they are not prompted to log in again — they land directly on the consent screen and pick another organisation from the picker. If they pick an organisation they've already connected your app to, re-consenting there just updates that connection's granted scopes; it does not create a duplicate or disturb any other organisation's connection.

A new connection is created alongside their existing ones each time they pick a fresh organisation — this is how a user accumulates connections to several organisations with the same app registration, without you registering a second client, and without you ever needing a second token. The access token you already hold identifies (your application, this user) only, not any one organisation — so every token you've been issued for this user already reaches every organisation they've connected, past and future, the moment each connection exists. You don't need to re-issue a token after the user connects another org; you only need to learn the new org_id (see List your connections below) and start calling it.

A token reaches every org it's connected to, but what it can actually do there is limited per request to the intersection of the token's own scopes and that org's connection granted_scopes (the granted_scopes array shown per item in List your connections) — see Effective scope, in plain words below for the full four-way check. Re-consenting to an org updates that connection's granted_scopes for every token, but a token's own scope is fixed at issuance — so an older token, issued before a re-consent that widened the requested scopes, still can't exceed the narrower scope set it was minted with. If a later consent to the same org requested extra scopes, use the token issued at that consent to exercise them there.

Per-org access is still enforced on every request, independent of which token you use — see Effective scope, in plain words below for the role/policy/revocation checks that run per call.

List your connections

GET /api/v1/connections
Authorization: Bearer <access token>

Returns every active connection the token's own user holds for your application, across every organisation they belong to:

{
  "items": [
    {
      "id": "…",
      "org_id": "…",
      "application_id": 4821,
      "granted_scopes": ["bills:read", "invoices:read"],
      "granted_at": "2026-09-01T10:00:00Z",
      "revoked_at": null,
      "revoked_reason": null,
      "last_used_at": "2026-09-20T08:12:00Z"
    }
  ],
  "count": 1
}

There is no separate "list my orgs" call — this response is where you learn which org_ids you can address in other endpoints' URLs.

application_id is your application's internal LuciBook id (a sequential numeric id, not a UUID) — it identifies which of your registered apps the connection belongs to. It is not a secret and carries no authority by itself; every request is still authenticated by the bearer token.

Revoke a connection

DELETE /api/v1/connections/{connection_id}
Authorization: Bearer <access token>

Only revokes a connection belonging to the same token's own user and your own application — a connection id alone is never enough authority. 204 on success, 404 if it doesn't match. The organisation's OWNER/ADMIN can also revoke from My Apps → Connected Apps, independent of your app.

Who may grant or revoke what

  • Granting a scope at consent requires the granting OrgMember to hold at least the scope's minimum role tier — see Scopes and role tiers.
  • Revoking is available to the app itself (the connection's own user, via the API above), or to any OWNER/ADMIN of the connected organisation from the org's Connected Apps page — independent of the app.
  • Revoking also happens automatically if the member who granted the connection is removed from the organisation, demoted below a scope they granted, or deletes their LuciBook account. revoked_at is stamped and connection.revoked fires exactly as it would for a manual revoke — you don't need to poll for this, only subscribe to the event.

Effective scope, in plain words

What a request can actually do is the intersection of four things, recomputed on every request (never cached from token issuance):

  1. the scopes the token was issued with,
  2. that organisation's connection granted_scopes — a different org the same token reaches can have a different (narrower or wider) set,
  3. the scopes the app is still allowed to request today (an app can lose a restricted capability after a token was issued), and
  4. the granting member's current role tier — checked against the whole effective scope set from steps 1–3, not per-endpoint, using the same per-scope role table consent uses (see Scopes and role tiers). If the grantor is no longer an active member, or any scope in the effective set now exceeds what their current role may hold — including payroll:* and migration:*, which need OWNER or ADMIN live, not only at consent — the whole request fails with insufficient_role, including a plain read. See Errors — insufficient_role for the full rule and how to recover.