Reference

Build on the Lucra API.

Drag a sticker to move it, or use the arrow keys while focused.

OAuth

Let other Lucra accounts connect your app.

Use OAuth when your app acts for Lucra accounts you don't own. A member signs in to Lucra and approves your app, and you get an access token instead of asking them for an API key. For your own account, a key is simpler.

The flow is the standard authorization code flow with PKCE: register your app once, send people to authorize it, exchange the code for tokens, and refresh them.

Register your app

Owners and admins create an app in Settings → Developers → OAuth apps with Create OAuth app:

FieldRule
NameUp to 200 characters; it can't contain "Lucra"
Callback URLsOne per line, up to 20; HTTPS only, no localhost, credentials or fragment
AccessRead, or Read and write

Lucra then shows the client ID (lucra_…) and client secret (lucra_oauth_…). The secret is shown once; keep it on your server. Every app is confidential and authenticates to the token endpoint with HTTP Basic. Any Lucra account can authorize it.

Copy setup instructions on the app copies its client ID, endpoints and callbacks, never the secret.

Authorize

Send the person to the authorization URL. Generate a fresh state and PKCE code verifier each time:

Terminal
https://api.onlucra.com/api/auth/oauth2/authorize
  ?response_type=code
  &client_id=lucra_…
  &redirect_uri=https://app.example.com/oauth/callback
  &scope=read%20write%20offline_access
  &resource=https://api.onlucra.com/v1
  &state=…
  &code_challenge=…
  &code_challenge_method=S256

They sign in if they need to, then approve on the consent screen. If they belong to more than one account, they pick which one your app gets, and the grant is for that account. Creators can't authorize apps.

Lucra then redirects to your redirect_uri with code and state. The redirect_uri must match a registered callback exactly. Check that state matches the one you sent.

resource names the API the token is for. Leave it out and the grant is for /v1. The code, its tokens and every refresh keep that audience, so the token request can leave it out too.

Exchange the code

The code lasts 10 minutes and works once.

Terminal
curl https://api.onlucra.com/api/auth/oauth2/token \
  -u "$LUCRA_CLIENT_ID:$LUCRA_CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code=$CODE \
  -d code_verifier=$CODE_VERIFIER \
  -d redirect_uri=https://app.example.com/oauth/callback \
  -d resource=https://api.onlucra.com/v1
JSON
{
  "access_token": "eyJ…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "expires_at": 1791306245,
  "refresh_token": "…",
  "scope": "read write offline_access"
}

Call the API

Use the access token like a key:

Terminal
curl https://api.onlucra.com/v1/account \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Lucra-Version: 2026-10-01"

It acts for the account that authorized your app. If that account is a partner, send Lucra-Account: acct_… to reach one of its client brands, as with a partner key.

With the SDK, pass the access token where you'd pass a key: new Lucra(accessToken).

Refresh

Access tokens last an hour. Swap the refresh token for a new pair:

Terminal
curl https://api.onlucra.com/api/auth/oauth2/token \
  -u "$LUCRA_CLIENT_ID:$LUCRA_CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token=$REFRESH_TOKEN

Store the new refresh token and drop the old one: reusing a refresh token ends the grant. A refresh token lasts 30 days, so refresh at least that often.

A grant lasts until the person disconnects your app, leaves the account, or a year passes. Then they authorize again.

Scopes

ScopeGrants
readEvery read, like a read key
writeReads and writes, like a write key
offline_accessA refresh token, so your app stays connected

Ask only for what your app registered: a Read app can't ask for write. A token never does more than the person's own role; members who aren't owners or admins read everything but payments. See Access.

Revoke

Revoking a refresh token ends the grant, like the person disconnecting your app: your app leaves their Settings → Connected apps and every token from that grant stops working. Revoking an access token ends only that token.

Terminal
curl https://api.onlucra.com/api/auth/oauth2/revoke \
  -u "$LUCRA_CLIENT_ID:$LUCRA_CLIENT_SECRET" \
  -d token=$REFRESH_TOKEN \
  -d token_type_hint=refresh_token

People disconnect your app in Settings → Connected apps, and its tokens stop working at once. Editing your app, rotating its secret or revoking it disconnects everyone; they authorize again.

Errors

ResultMeans
error=access_deniedThe person declined, or can't grant access to that workspace
invalid_grantThe code expired or was used, or the refresh token was reused
invalid_clientUnknown client, or a missing or wrong client secret
unsupported_grant_typeOnly authorization_code and refresh_token; for machine-to-machine access use an API key
401 invalid_keyThe token expired or was revoked; refresh or authorize again
403 permission_deniedA read token called a write
403 account_not_reachableLucra-Account isn't one the token reaches
403 internal_routeThe endpoint is only for Lucra's apps

Discovery

Server metadata is at https://api.onlucra.com/.well-known/oauth-authorization-server/api/auth, and the API's protected-resource metadata at https://api.onlucra.com/.well-known/oauth-protected-resource/v1. A 401 from /v1 names it in WWW-Authenticate (resource_metadata). Clients that can't register ahead, like MCP clients, register themselves at POST https://api.onlucra.com/api/auth/oauth2/register; such a client works with the first account that authorizes it.