Zum Inhalt springen

Authentication

Every request carries a Bearer token in the Authorization header. An API key is all you need for your own scripts; apps that connect other DebtFlow accounts use OAuth 2.0 with PKCE.
MethodTokenUsed for
API keydf_live_…Your own scripts, servers and automations for your own account
OAuth 2.0dfo_…Third-party apps acting on behalf of other users

API keys

You create keys in the dashboard under Settings → Developers. There you choose the permissions and, optionally, an expiry date. The key has the format df_live_ plus 32 characters and is shown only once; DebtFlow only stores a hash. Revoked or expired keys are rejected with 401 invalid_api_key.

curl 'https://www.debt-flow.io/api/v1/me' \
  -H "Authorization: Bearer $DEBTFLOW_API_KEY"
Keys belong on the server
An API key has the rights of your account within the selected scope. Never embed it in browser or mobile apps and do not commit it to Git. Create a separate key with the smallest necessary permissions for each application – then each one can be revoked individually.

OAuth 2.0

With OAuth, other DebtFlow users connect your application to their account without giving you a password or a key. DebtFlow supports the authorisation code flow with PKCE (RFC 6749, RFC 7636) and refresh tokens.

EndpointURL
Authorisationhttps://www.debt-flow.io/oauth/authorize
TokenPOST https://www.debt-flow.io/api/v1/oauth/token
RevocationPOST https://www.debt-flow.io/api/v1/oauth/revoke
Metadata (RFC 8414)https://www.debt-flow.io/.well-known/oauth-authorization-server

1. Register your app

Under Settings → Apps → Register app you enter the name, description, website, redirect URIs and the maximum permissions the app may request. You receive a client ID (oac_…) and – for confidential apps – a client secret (dfs_…), shown only once.

  • Confidential: runs on your server and keeps the secret private. Authenticates at the token endpoint with HTTP Basic or client_id/client_secret in the body. PKCE is recommended.
  • Public: browser, mobile or desktop app without a secret. PKCE is mandatory; only client_id is sent to the token endpoint.
  • Redirect URIs must match exactly and use https://. For development, http://localhost and http://127.0.0.1 are allowed, with any port.

2. Generate PKCE values

For every sign-in, generate a random code_verifier (43–128 characters) and derive the code_challenge from it with SHA-256. Also generate a random state to protect against CSRF. Both values stay in the user's session until the redirect back.

# code_verifier: 64 random characters from A–Z a–z 0–9 - . _ ~
CODE_VERIFIER=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 64)
# code_challenge = BASE64URL(SHA256(code_verifier)) without "="
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')

3. Redirect the user for consent

Redirect
https://www.debt-flow.io/oauth/authorize
  ?response_type=code
  &client_id=oac_DeineClientId0000000
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
  &scope=contacts%3Aread%20invoices%3Awrite
  &state=7Jx0…
  &code_challenge=E9Mel…
  &code_challenge_method=S256
ParameterMeaning
response_typealways code
client_idclient ID of your app
redirect_uria registered redirect URI; may be omitted if exactly one is registered
scopepermissions separated by spaces; if omitted, all permissions registered for the app
staterandom value, returned unchanged
code_challengeBASE64URL(SHA256(code_verifier)), 43 characters
code_challenge_methodonly S256

If the user is not signed in, they sign in to DebtFlow first. They then see your app, the requested permissions and the redirect target, and either allow or deny access.

4. Handle the redirect

After consent, DebtFlow redirects to your redirect URI:

https://app.example.com/oauth/callback?code=dfc_…&state=7Jx0…&iss=https%3A%2F%2Fwww.debt-flow.io

Check that state matches your session and that iss equals https://www.debt-flow.io (RFC 9207). The code is valid for 5 minutes and can be redeemed only once. If a code is redeemed a second time, DebtFlow revokes all tokens issued with it.

5. Exchange the code for tokens

The token endpoint expects application/x-www-form-urlencoded. redirect_uri is required if it was passed during authorisation.

curl -X POST 'https://www.debt-flow.io/api/v1/oauth/token' \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://app.example.com/oauth/callback \
  -d code_verifier="$CODE_VERIFIER"
Response 200
{
  "access_token": "dfo_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dfr_…",
  "scope": "contacts:read invoices:write"
}

6. Call the API

The access token is sent like an API key: Authorization: Bearer dfo_…. It is valid for one hour. Expired or revoked tokens return 401 invalid_token; missing permissions return 403 insufficient_scope.

7. Refresh the token

Use the refresh token to obtain new tokens. Every refresh returns a new refresh token; the one you used becomes invalid. Optionally, request fewer permissions with scope – never more.

curl -X POST 'https://www.debt-flow.io/api/v1/oauth/token' \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN"
Parallel refresh and theft detection
If several processes of your app refresh at the same time with the same refresh token, all of them receive the same token pair within 30 seconds of the first refresh (grace period), as long as the new refresh token has not been used yet. If a refresh token that has already been used is presented again after that – once the grace period has expired or the new one has already been used –, DebtFlow treats this as possible theft and revokes the entire token chain; the user then has to reconnect the app. Store the new refresh token before you use it.

Revoking tokens

When a user signs out of your app, send the refresh token to the revocation endpoint (RFC 7009). This also invalidates all access tokens derived from it. The response is always 200.

curl
curl -X POST 'https://www.debt-flow.io/api/v1/oauth/revoke' \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token="$REFRESH_TOKEN"

Lifetimes

ValueValid forNote
Authorisation code5 minutessingle use
Access token1 hourexpires_in
Refresh token90 daysevery refresh starts a new 90-day period

Errors at the token endpoint

The token and revocation endpoints respond in OAuth format { "error": "…", "error_description": "…" }:

CodeStatusMeaning
invalid_request400parameter missing, duplicated or invalid
invalid_client401client ID unknown or secret wrong
invalid_grant400code or refresh token invalid, expired or already used, PKCE does not match, or connection revoked
unsupported_grant_type400only authorization_code and refresh_token
invalid_scope400unknown permissions, or more than were granted

When the user disconnects

Users see all connected apps under Settings → Apps and can revoke access at any time. All tokens of the connection then become invalid immediately; your app receives 401 or invalid_grant and must take the user through authorisation again. If you, as the developer, reduce your app's permissions, the reduced rights also apply immediately to existing connections.