Guides
Authentication

Authentication

Pharlo supports three authentication mechanisms depending on how you access it.


API Key Authentication

The primary method for programmatic access. Every tenant receives an ApiClient with a key in the format:

ds_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Sending the key

Pass the key as a Bearer token in every request:

curl https://pharlo.io/api/v1/connections \
  -H "Authorization: Bearer $DELIVERY_API_KEY"

Finding your key

Go to Settings → Credentials (opens in a new tab) in the console. Your key is shown there and can be rotated from the same page.

Security best practices

  • Store the key in an environment variable, never hardcode it
  • Never send it to client-side (browser) code
  • Rotate immediately if you suspect it has been compromised
  • Use the console to rotate: old key is invalidated, new key is issued

JWT Authentication (console only)

Used by the console SPA and by custom UIs built on top of the console API. Not intended for server-to-server integrations — use API keys for those.

Register

curl -X POST https://pharlo.io/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "password": "secret"}'

Response includes a JWT:

{"token": "eyJhbGciOiJSUzI1NiJ9..."}

Login

curl -X POST https://pharlo.io/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "password": "secret"}'

Using the JWT

curl https://pharlo.io/api/v1/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."

Tokens expire after a fixed period. Re-login to get a new token.


MCP OAuth (AI clients)

MCP clients (Claude.ai, and any MCP-compatible AI assistant) authenticate via OAuth 2.1 with PKCE. This is handled automatically by the client; you only need a valid API key to complete the consent step.

OAuth endpoints

MethodEndpointDescription
GET/.well-known/oauth-authorization-serverServer metadata discovery (RFC 8414)
GET/.well-known/oauth-protected-resourceProtected resource metadata (RFC 9728)
POST/oauth/mcp/registerDynamic client registration (RFC 7591)
GET POST/oauth/mcp/authorizeAuthorization + consent page
POST/oauth/mcp/tokenToken exchange
POST/oauth/mcp/revokeToken revocation (RFC 7009)

Authorization flow

MCP client discovers the server

Reads /.well-known/oauth-authorization-server to obtain server metadata.

Dynamic registration

Client registers itself at POST /oauth/mcp/register.

Authorization

Browser opens /oauth/mcp/authorize. User enters their ds_live_... API key to grant consent.

Token exchange

POST /oauth/mcp/token returns access + refresh tokens.

Requests

Client calls /_mcp with Authorization: Bearer <access_token>.

Scopes

ScopeAccess
assignments:readRead assignments
assignments:writeCreate, update, retry, cancel assignments
connections:readRead connections and stats
connections:writeUpdate connections
platforms:readRead platform capabilities
webhooks:readRead webhook delivery logs
stats:readRead all analytics and stats

Token lifetimes

TokenTTL
Authorization code5 minutes
Access token1 hour
Refresh token30 days

Troubleshooting

invalid_grant on token exchange

  • Authorization code is single-use. Restart the OAuth flow if it was already consumed.
  • Check that redirect_uri exactly matches the URI used during registration.
  • Verify the code_verifier produces the correct SHA-256 challenge.

401 on /_mcp requests

  • Access token expired — use the refresh token to get a new one.
  • Access token revoked — re-authorize.

For Claude.ai setup, see the MCP Connection Setup guide.