FinzBooksDevelopers

Authentication

Bearer-token auth. Two issuance paths, one verification layer.

Every request to /api/public/v1/* must carry an Authorization: Bearer … header. Pick the issuance path that matches your integration:

Token kindPrefixBest for
Personal Access Token (PAT)aibk_pat_…Server-side scripts, CI, internal tools
Sandbox PATaibk_pat_test_…Local development, integration tests
OAuth access tokenaibk_oat_…Partner apps that act on behalf of users

Personal Access Tokens

Mint from Console → API Tokens. The raw value is shown once at creation — copy it then. Subsequent listings only show the label prefix (e.g. aibk_pat_a1b2c3d4…).

Single-org vs multi-org

PATs are single-org by default — hard-bound to the org you minted them from. Cross-org calls are refused.

Tick “All my orgs” at mint time for a multi-org PAT. The token is bound to you, not a single org; every API call must pass ?organization_id=<id> to pick which org to target. The server confirms the user has an active membership in that org on every call.

OAuth 2.0 (Authorization Code + PKCE)

Use this when end-users grant your app access to their AI Books data — Zapier-style integrations, partner connectors, third-party dashboards.

Register an app

Go to Console → OAuth Apps, click Register, set your redirect URI(s). You'll get a client_id and a one-time client_secret. Treat the secret like a password — never commit it.

Authorize

GET /oauth/authorize
  ?response_type=code
  &client_id=<your_client_id>
  &redirect_uri=<your_redirect_uri>
  &code_challenge=<S256(verifier)>
  &code_challenge_method=S256
  &scope=AIBooks.contacts.READ AIBooks.invoices.READ
  &state=<csrf_token>

Omit organization_id for a multi-org token (pick the active org via the query param on each call), or pass it to bind the issued token to a single org.

Exchange the code

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<auth_code>
&redirect_uri=<your_redirect_uri>
&code_verifier=<verifier>
&client_id=<your_client_id>
&client_secret=<your_client_secret>

Response:

{
  "access_token": "aibk_oat_…",
  "refresh_token": "aibk_ort_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "AIBooks.contacts.READ AIBooks.invoices.READ"
}

Refresh

Access tokens live for 1 hour. Trade the refresh token for a new pair before then:

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=aibk_ort_…
&client_id=<your_client_id>
&client_secret=<your_client_secret>

Refresh tokens are single-use. Each rotation mints a new (access, refresh) pair. Replaying an already-rotated refresh revokes the entire token family and forces re-authorization — this detects stolen-token reuse.

Scopes

Scope strings follow the pattern:

AIBooks.<resource>.<OP>     # e.g. AIBooks.invoices.READ
AIBooks.<resource>.ALL      # all ops on a resource
AIBooks.fullaccess.all      # wildcard — everything

OAuth apps declare a maximum scope list at registration; users can narrow at consent time but cannot exceed it.