Skip to content

Custom connections (not yet available)

If you're integrating your own organisation's LuciBook — no third-party end user, no consent screen — a custom connection using the OAuth 2.0 client-credentials grant is designed to be the lighter-weight option. It is not available yet.

Not yet available — contact support to register interest

Custom connections (client-credentials) are not live in this release: there is no self-service way to register a client-credentials application today, and no request against /oauth/token/ with grant_type=client_credentials can currently be turned into a working connection to an organisation. This page describes the intended design so you can plan for it. Contact support if you want to register interest or be notified when it ships — this page will be updated at that point.

How it's designed to work

An OWNER or ADMIN of the one organisation you're integrating consents once (no per-scope end-user picker — a custom connection either exists for the org or it doesn't). If a custom connection already exists for that (app, org) pair, granting a second one is designed to fail with custom_connection_exists; if none exists yet and something expects one, custom_connection_missing.

Getting a token (preview — not usable yet, see the callout above)

The intended request shape, once this ships:

POST /oauth/token/
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
scope=<space-separated scopes>
curl -s https://api.lucibook.co.uk/oauth/token/ \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials \
  -d scope="bills:read invoices:read"

A successful response is the same token-response shape every grant type returns:

{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 1800,
  "scope": "bills:read invoices:read"
}

There is no refresh token for client-credentials — request a new access token when the old one expires. As with the authorization-code flow, authenticate with HTTP Basic (-u client_id:client_secret); body-post credentials work as a fallback, same as described for authorization-code.

The grantor hostage warning

Whoever granted the custom connection is now load-bearing: if they leave the organisation or are demoted below a granted scope's minimum role tier, calls using that scope start failing insufficient_role — live, not at token issue (see Connections — effective scope). An organisation should not let its only OWNER be the sole grantor of a business-critical custom connection.

There is no "replace the grantor" operation planned. A connection records who granted it, and that cannot be repointed to a different user after the fact — only its granted scopes can be updated, by the same grantor consenting again. If the grantor's own access changes and you need a new one, the fix is a fresh consent by the new grantor, not an edit to the existing connection.

Two-secret rotation with grace

Rotating your client secret never invalidates a token already issued, and never causes a mid-rotation outage: the OLD secret keeps authenticating alongside the NEW one until a grace window elapses (developer.secret_rotation_grace_days, see the rate limits / policy defaults table). Rotate, deploy the new secret everywhere, then let the old one lapse — no downtime window to coordinate.