OAuth2
Authenticate via OAuth2 for MCP clients and third-party applications
BeeL. supports OAuth2 authorization_code with PKCE for third-party applications and MCP clients. Users log in on BeeL., consent the requested scopes, and your app receives a JWT access token.
Discovery
The OAuth2 Authorization Server publishes its configuration automatically:
GET https://app.beel.es/.well-known/oauth-authorization-serverAny standard OAuth2 client library can auto-configure from this URL.
Endpoints
| Endpoint | URL |
|---|---|
| Authorize | https://app.beel.es/oauth2/authorize |
| Token | https://app.beel.es/oauth2/token |
| JWKS | https://app.beel.es/oauth2/jwks |
| Revoke | https://app.beel.es/oauth2/revoke |
Client Registration
OAuth2 client registration is currently manual. Contact us at it@beel.es with:
- Your application name
- Your
redirect_uri
We'll provide a client_id and client_secret. Authentication method: client_secret_basic.
Tokens
| Token | Format | TTL |
|---|---|---|
| Access token | JWT (RS256) | 1 hour |
| Refresh token | Opaque | 30 days |
The JWT access token contains:
| Claim | Description |
|---|---|
sub | User UUID |
user_id | User UUID (same as sub) |
scope | Granted scopes (array) |
environment | PRODUCTION or SANDBOX |
iss | Issuer URL |
exp / iat | Expiration and issued-at timestamps |
Access tokens are validatable offline via the JWKS endpoint (RSA public key).
Authorization Code Flow with PKCE
Step 1: Generate PKCE Challenge
PKCE (S256) is required for all authorization requests.
# Generate code_verifier (random 43-128 chars)
CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=/+' | head -c 43)
# Generate code_challenge (SHA256 + base64url)
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" \
| openssl dgst -sha256 -binary \
| base64 | tr '+/' '-_' | tr -d '=')Step 2: Redirect User to Authorize
GET https://app.beel.es/oauth2/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://your-app.com/callback
&scope=invoices:read customers:read
&code_challenge=CODE_CHALLENGE
&code_challenge_method=S256The user logs in on BeeL. and is redirected to your redirect_uri with a code parameter.
Step 3: Exchange Code for Token
curl -X POST https://app.beel.es/oauth2/token \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://your-app.com/callback" \
-d "code_verifier=CODE_VERIFIER" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET"Response:
{
"access_token": "eyJraWQi...",
"refresh_token": "abc123...",
"token_type": "Bearer",
"expires_in": 3599,
"scope": "invoices:read customers:read"
}Step 4: Use Access Token
curl https://app.beel.es/api/v1/invoices \
-H "Authorization: Bearer ACCESS_TOKEN"Step 5: Refresh Token
Access tokens expire after 1 hour. Use the refresh token to get a new one:
curl -X POST https://app.beel.es/oauth2/token \
-d "grant_type=refresh_token" \
-d "refresh_token=REFRESH_TOKEN" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET"Refresh tokens are single-use. Each refresh returns a new refresh token. The previous one is invalidated.
Scopes
Request only the scopes your application needs. See Available Scopes for the full list.
scope=invoices:read customers:read products:readIf your token doesn't have the required scope for an endpoint, the API returns 403 Forbidden.
Environments
By default, OAuth2 tokens operate in the production environment. To use sandbox, include the sandbox scope in your authorization request:
scope=invoices:read customers:read sandboxThe environment is embedded in the JWT as the environment claim (PRODUCTION or SANDBOX). No additional headers are needed — the token itself determines the environment.
Scope includes sandbox? | Environment | Effect |
|---|---|---|
| No | Production | Real invoices, AEAT submissions via VeriFactu |
| Yes | Sandbox | Test data, no AEAT submissions, no quota consumed |
Unlike API keys (which use prefixes like beel_sk_test_), OAuth2 tokens determine the environment via the sandbox scope at authorization time.
Rate Limits
OAuth2 token requests are rate-limited like any other endpoint — see Rate limits.
Security Notes
- PKCE (
code_challenge_method=S256) is required - All communication must use HTTPS
- Revoke tokens on user logout via the revocation endpoint
- Store
client_secretsecurely — never expose in client-side code - Access tokens are short-lived (1h) — use refresh tokens for long sessions