Skip to content

Connect your application to Logfire with OAuth

Let customers connect their Logfire organization to your application without copying an API key. OAuth 2.0 is an authorization protocol: the customer approves specific permissions, and your application receives tokens to act within that approval.

Use an OAuth app for a partner integration that connects to customers’ Logfire resources. For automation within your own organization, you can use an API key. For connecting an existing AI tool, follow the MCP server guide.

We are happy to help you integrate with Logfire. Email engineering@pydantic.dev to discuss your integration or get help with setup.

Register your app

You need permission to manage your organization’s settings. Register the app in your own organization. Each customer separately approves access to their organization and, when applicable, a project. An administrator of the customer’s organization must approve an app registered through organization settings.

  1. Open OAuth apps in your default organization.
  2. Select New OAuth app.
  3. Enter an Application Name that customers will recognize on the consent screen.
  4. Choose an Application Type:
    • Confidential: your server can keep a client secret private. Use this for a partner’s hosted backend.
    • Public: the application cannot keep a secret, such as a browser or mobile app.
  5. Add your Redirect URIs: the callback URLs where your application receives the authorization result. Use HTTPS for a hosted callback. Loopback HTTP URLs, such as http://127.0.0.1:8000/callback, are supported for local development.
  6. Select the Allowed Scopes your integration needs. A scope names a permission, such as project:read.
  7. Create the app and save its Client ID. For a confidential app, also save the Client Secret.

If OAuth apps is missing, check your organization permissions and contact us about app registration access.

Configure your OAuth client

Use the authorization-code flow with Proof Key for Code Exchange (PKCE). PKCE binds the authorization code to a random value generated by your application, so intercepting the code alone is insufficient to exchange it. Logfire requires the S256 method for both public and confidential apps.

Use one data region throughout registration, authorization, token exchange, and API calls. Register an app separately in each region you support.

SettingUSEU
Authorization URLhttps://logfire-us.pydantic.dev/api/oauth/authorizehttps://logfire-eu.pydantic.dev/api/oauth/authorize
Token URLhttps://logfire-us.pydantic.dev/api/oauth/tokenhttps://logfire-eu.pydantic.dev/api/oauth/token
Public API base URLhttps://api-us.pydantic.dev/api/v1https://api-eu.pydantic.dev/api/v1
API referenceUS APIEU API

Configure your OAuth library with your registered client ID, callback URL, and these settings:

SettingValue
Response typecode
Grant typeauthorization_code
PKCE challenge methodS256
Confidential client authenticationclient_secret_basic
Public client authenticationnone
ScopesSpace-separated permissions selected when registering your app

Run the examples

Choose a language tab below. Each example runs from your server or a local terminal. The TypeScript examples target Node.js, not browser code: confidential-client credentials must stay on your server.

  • curl: install curl, OpenSSL, and jq.
  • Python: save a Python example as example.py and run uv run --with httpx2 example.py.
  • TypeScript: save a TypeScript example as example.mts and run node example.mts with Node.js 24 or later. No packages are required.

Set the environment variables shown before each tab group, replacing placeholder values with your own. The examples print their results for manual verification. In your application, store tokens securely instead of logging them. They demonstrate the HTTP requests; your application still needs the callback handler and session storage described below.

When the customer selects Connect to Logfire in your application:

  1. Generate a fresh PKCE verifier and its S256 challenge.
  2. Generate a random state value and bind it, along with the verifier, to the customer’s session in your application.
  3. Redirect the browser to the authorization URL with the parameters below.
  4. The customer signs in to Logfire, selects the organization and any project restriction, and approves the permissions.
  5. Logfire redirects to your callback with code and state, or an OAuth error.
  6. Verify the returned state against the saved value before exchanging the code. Reject a missing or mismatched value.
Authorization parameterValue
response_typecode
client_idThe app’s client ID
redirect_uriYour registered callback URL
scopeFor example, project:read
stateThe random value bound to this connection attempt
code_challengeBase64url-encoded SHA-256 digest of the verifier, without padding
code_challenge_methodS256

The verifier must contain 43 to 128 characters from A-Z, a-z, 0-9, -, ., _, and ~. Keep it private until the token exchange. Use your OAuth library’s PKCE and state handling when available.

For a manual integration check, these examples generate fresh values and an authorization URL. Replace the client ID and callback URL with your registered values.

Terminal
export LOGFIRE_BASE_URL='https://logfire-us.pydantic.dev'
export CLIENT_ID='YOUR_CLIENT_ID'
export REDIRECT_URI='https://your-app.example.com/oauth/logfire/callback'
Terminal
export CODE_VERIFIER="$(openssl rand -hex 32)"
export STATE="$(openssl rand -hex 32)"
CODE_CHALLENGE="$(printf '%s' "$CODE_VERIFIER" \
  | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')"

jq -nr \
  --arg base "$LOGFIRE_BASE_URL" \
  --arg client "$CLIENT_ID" \
  --arg redirect "$REDIRECT_URI" \
  --arg state "$STATE" \
  --arg challenge "$CODE_CHALLENGE" \
  '$base + "/api/oauth/authorize?" + ({
    response_type: "code", client_id: $client, redirect_uri: $redirect,
    scope: "project:read", state: $state,
    code_challenge: $challenge, code_challenge_method: "S256"
  } | to_entries | map(.key + "=" + (.value | @uri)) | join("&"))'

The curl tab keeps CODE_VERIFIER and STATE in the current shell. The Python and TypeScript tabs return them as code_verifier and state in JSON. Save both values for this connection attempt. For a manual check with Python or TypeScript, set CODE_VERIFIER to the returned verifier before running the token-exchange example.

Open the resulting URL in a browser. Your callback handler must perform the state check described above; generating an authorization URL does not implement that check for you.

Request only the permissions the integration needs. Logfire can grant fewer scopes than requested, so read the token response’s scope field. Selecting a scope during app registration does not grant access to customer data by itself.

Exchange the authorization code

For a confidential app, send a form-encoded request from your server. These examples use HTTP Basic authentication for the client credentials. Set AUTHORIZATION_CODE only after your callback handler has validated the returned state:

Terminal
export LOGFIRE_BASE_URL='https://logfire-us.pydantic.dev'
export CLIENT_ID='YOUR_CLIENT_ID'
export CLIENT_SECRET='YOUR_CLIENT_SECRET'
export REDIRECT_URI='https://your-app.example.com/oauth/logfire/callback'
export AUTHORIZATION_CODE='CODE_FROM_YOUR_VALIDATED_CALLBACK'
: "${CODE_VERIFIER:?Set the verifier saved for this authorization attempt}"
Terminal
curl --fail-with-body --silent --show-error \
  --user "$CLIENT_ID:$CLIENT_SECRET" \
  --data-urlencode 'grant_type=authorization_code' \
  --data-urlencode "code=$AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=$REDIRECT_URI" \
  --data-urlencode "code_verifier=$CODE_VERIFIER" \
  "$LOGFIRE_BASE_URL/api/oauth/token"

Replace the values with your registered credentials and the values from this authorization attempt. Use the same redirect_uri as in the authorization request. An authorization code can be exchanged only once.

For a public app, remove client-secret configuration and the client-authentication argument or header. Send client_id in the form body instead: --data-urlencode "client_id=$CLIENT_ID" in curl, "client_id": client_id in Python, or client_id: CLIENT_ID in TypeScript. In the TypeScript example, also remove CLIENT_SECRET from the required-variable check. PKCE is still required. Do not combine HTTP Basic authentication with client credentials in the form body.

The successful JSON response includes:

FieldMeaning
access_tokenCredential to send as a bearer token in API requests
token_typeBearer
expires_inAccess-token lifetime in seconds
refresh_tokenCredential to obtain replacement tokens without repeating consent
scopeThe granted, space-separated scopes

Keep access and refresh tokens private. Use expires_in to schedule renewal instead of assuming a fixed lifetime.

Verify the connection

With project:read granted, list the projects visible to this token:

Terminal
export LOGFIRE_API_BASE_URL='https://api-us.pydantic.dev/api/v1'
export ACCESS_TOKEN='ACCESS_TOKEN_FROM_THE_TOKEN_RESPONSE'
Terminal
curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $ACCESS_TOKEN" \
  "$LOGFIRE_API_BASE_URL/projects/"

For an EU connection, use https://api-eu.pydantic.dev/api/v1. A successful request returns HTTP 200 and a JSON list of projects within the token’s permitted organization and project restrictions. Check that the returned projects belong to the customer who completed consent.

Refresh a connection

Use the token URL from the same region as the original grant:

Terminal
export LOGFIRE_BASE_URL='https://logfire-us.pydantic.dev'
export CLIENT_ID='YOUR_CLIENT_ID'
export CLIENT_SECRET='YOUR_CLIENT_SECRET'
export REFRESH_TOKEN='LATEST_REFRESH_TOKEN_FOR_THIS_CUSTOMER'
Terminal
curl --fail-with-body --silent --show-error \
  --user "$CLIENT_ID:$CLIENT_SECRET" \
  --data-urlencode 'grant_type=refresh_token' \
  --data-urlencode "refresh_token=$REFRESH_TOKEN" \
  "$LOGFIRE_BASE_URL/api/oauth/token"

For a public app, use the same form-body client_id and omit client-secret configuration and authentication, as described for the code exchange.

Refresh tokens rotate: save the replacement refresh_token together with the new access token. Coordinate refreshes for each connection so workers do not independently keep using an older refresh token. If Logfire returns invalid_grant, ask the customer to reconnect instead of repeatedly retrying the same credential.

Manage or disconnect an app

Open OAuth apps in the app owner’s organization settings to view the app’s details, manage redirect URLs and scopes, or manage confidential-client secrets. When rotating a secret, create a replacement, update your server to use it, and then revoke the old secret.

A customer can revoke an individual OAuth session from their account’s Security settings. Your integration must handle a revoked grant by asking the customer to reconnect.

Troubleshooting

SymptomCheck
You cannot find OAuth AppsOpen OAuth apps in your default organization and check your organization permissions.
invalid_clientCheck the region, client ID, and active client secret. Public apps send a client ID without a secret.
Authorization fails or returns access_deniedThe customer must be an administrator of the selected organization. Check for declined consent and a callback URL that does not match registration.
invalid_grant during exchange or refreshCheck the original PKCE verifier and callback URL. An expired or already-used code, or an invalid refresh token, requires a new authorization attempt.

Use the regional API reference above to choose further endpoints and their required scopes.