Connected Apps
A connected app is an OAuth 2.1 client LedgerBee registers for your product, so it can act on a company's data with that company's consent. Register once, then run the authorization-code flow for every company that connects.
Registration
Registration is handled by LedgerBee, not self-service. Email development@ledgerbee.com with:
- your app's display name
- one or more https redirect URIs
- the scopes your integration needs
- a logo URL (optional)
You get back a client_id (lb_app_client_…) and a client_secret
(lb_app_secret_…). The secret is shown once and cannot be retrieved, so store
it before you close the message.
Email the same address to change any of this later, or to rotate the secret. A rotation keeps the previous secret working for 24 hours, so you can roll credentials without downtime.
Authorize
Send the company's user to:
Code
Wrapped for readability; every parameter belongs on one query string, and the
space between scope names must be encoded as %20.
| Parameter | Required | Notes |
|---|---|---|
response_type | Yes | Must be code. |
client_id | Yes | Your lb_app_client_… id. |
redirect_uri | Yes | Must exactly match one of your registered https URIs. |
state | Recommended | Echoed back on the redirect. Generate an unguessable value per request and reject a callback whose state doesn't match. |
code_challenge | Yes | PKCE, RFC 7636. |
code_challenge_method | Yes | Must be S256; plain is refused. |
scope | No | Space-separated, and a subset of your registered scopes. Omit to request all of them. |
A signed-out user is sent to login and returned here afterward.
Consent
The user picks the company to connect if they belong to more than one, then
approves or denies the requested scopes as a fixed list. Approval grants
exactly what /authorize requested; there is no partial grant. A company holds
one live grant per app, so reconnecting refreshes it rather than adding a
second.
Approval redirects to your redirect_uri with code and state; denial
redirects with error=access_denied.
Token exchange
Code
Code
access_tokenis a JWT valid for 1 hour.refresh_tokenis valid for 30 days, single-use, and rotates on every exchange.scopereports what was actually granted. Read it back rather than assuming your request was granted verbatim.
Authenticate with client_secret_post as above, or client_secret_basic, an
Authorization: Basic header carrying client_id:client_secret. The examples
read the secret from an environment variable; curl still expands that into its
argument list, so on a shared machine use your own HTTP client instead.
This endpoint and /oauth/revoke take a form-urlencoded body
(RFC 6749 /
RFC 7009), so they are
documented here rather than in the API Reference, which covers the JSON
REST endpoints.
Refresh
Code
The 30 days are counted per token: each replacement starts its own window, so refreshing regularly keeps you connected indefinitely while a leaked token still dies 30 days after issue. A connection ends when the tenant revokes it or you go 30 days without refreshing.
Serialize refresh calls per grant across your workers: presenting an already-used refresh token is treated as a compromised token family and revokes the whole grant.
Using the access token
Code
See the API Reference for the available endpoints.
Scopes
The scope you request must be a subset of the scopes registered for your app.
Three are never grantable to a connected app: checkout, portal-sso-mint,
and portal-provision, which are API-key-only provisioning credentials. Legacy
vendors-read / vendors-write are accepted at /authorize and normalized to
suppliers-read / suppliers-write.
Revocation
Users disconnect your app from their Connected apps page at any time. Your app can also revoke a token directly:
Code
The response is 200 with an empty body whether the token was valid, already
revoked, or unknown. Only a client-authentication failure returns 401.
Errors
| Condition | What you observe | Fix |
|---|---|---|
Unknown or revoked client_id, or unregistered redirect_uri | Error page, no redirect | Check both against your registration |
response_type isn't code | error=unsupported_response_type | Send response_type=code |
Missing PKCE, or a method other than S256 | error=invalid_request | Send PKCE S256 |
| Scope outside your registered set, or an unknown name | error=invalid_scope | Request only registered scopes, or email us to add one |
| User declines | error=access_denied | Nothing to fix |
Bad, expired or replayed code, PKCE mismatch, redirect_uri mismatch | 400 invalid_grant | Restart the flow |
Wrong client_id / client_secret | 401 invalid_client | Check your credentials |
| Refresh token used, unknown, or grant revoked | 400 invalid_grant; a reused token revokes the grant | Reconnect |
| Token missing a required scope | 403, error="insufficient_scope" | Reconnect with the broader scope |
| Tenant disconnected your app | 401, error="invalid_token", details.reason: "grant_revoked" | Reconnect |
Discovery
Code
Returns the OAuth 2.1 metadata: authorization_endpoint, token_endpoint,
revocation_endpoint, jwks_uri, supported grant types and scopes. The
API Reference carries the full schema, including the JWKS and
protected-resource variants.