OAuth2 Integration
Let users authorize your app to sign X402 transactions on their behalf
OAuth2 Integration
OAuth2 lets your application request permission from Agnic users to sign X402 transactions on their behalf. Users set their own spending limits and can revoke access anytime.
Why OAuth2?
- User-controlled limits — Users set daily/monthly spending caps at consent
- Revocable access — Users can disconnect your app anytime
- Network selection — Users choose which networks to allow
- Long-lived tokens — Access tokens last 30–60 days with refresh
Register Your Application
Before your first OAuth2 call, register your app and get it approved.
1. Create a client in the dashboard
Go to app.agnic.ai/oauth-clients and click New Client. Registration is a three-step form.
Step 1 — KYC information
Required to operate an OAuth client on Agnic. These fields lock after approval.
| Field | Required | Notes |
|---|---|---|
| Full name | Yes | Legal name of the responsible party |
| Phone number | Yes | |
| Country | Yes | |
| Address | Yes |
Step 2 — App information
Shown to your users on the consent screen; editable any time.
| Field | Required | Notes |
|---|---|---|
| App name | Yes | Displayed to users when they authorize |
| User support email | Yes | Where users reach you about your app |
| Contact emails | No | Where Agnic reaches you about your client (add as many as you like) |
Step 3 — OAuth configuration
| Field | Required | Notes |
|---|---|---|
| Authorized JavaScript origins | No | CORS-origin allowlist for browser flows (e.g. https://yourapp.com) |
| Authorized redirect URIs | Yes, ≥1 | Exact-match callback URLs. Add one per environment |
| Accept terms | Yes |
2. Save your credentials
On creation you receive:
- Client ID — always visible on the client's detail page (copyable)
- Client Secret — shown once in a modal. Copy it immediately; it is never displayed again. You can regenerate from the detail page, which invalidates the previous secret.
Store the secret in a server-side vault. Public clients (SPAs, mobile, CLIs) should use PKCE instead of embedding a secret.
3. Wait for approval
New clients start in Pending Approval status. You can see this as a badge on the client detail page.
While pending, the /oauth/authorize endpoint will reject your client_id. Your client becomes usable only after Agnic approves it. You'll be notified at the contact emails you provided.
Status badges you may see on the detail page:
| Status | Meaning |
|---|---|
| Pending Approval | Awaiting Agnic review — cannot run OAuth flows |
| Approved | Ready for production |
| Rejected | Review rejected — reason shown on the page |
| Revoked | Access permanently disabled |
Authorization Flow
Step 1: Redirect to Authorization
Redirect users to our authorization endpoint. Include PKCE for any non-confidential client.
https://api.agnic.ai/oauth/authorize?
client_id=<your client id>
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=payments:sign+balance:read
&state=<random csrf token>
&code_challenge=<base64url(sha256(verifier))>
&code_challenge_method=S256Step 2: User Grants Permission
The user logs in, sets spending limits (per-transaction, daily, monthly), selects allowed networks, and approves your app.
Step 3: Receive Authorization Code
User is redirected back to your app with an authorization code:
https://yourapp.com/callback?code=abc123&state=<original state>On failure, the callback carries error and (usually) error_description instead:
https://yourapp.com/callback?error=access_denied&error_description=User%20declinedAlways verify state matches the value you sent before doing anything else.
Step 4: Exchange for Tokens
Exchange the code for access and refresh tokens. redirect_uri must match the one you sent in Step 1 exactly.
curl -X POST https://api.agnic.ai/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "abc123",
"redirect_uri": "https://yourapp.com/callback",
"client_id": "<your client id>",
"code_verifier": "<the PKCE verifier>"
}'Confidential clients (server-side apps with a stored secret) include client_secret instead of, or in addition to, code_verifier.
Token Response
{
"access_token": "agnic_at_abc123...",
"refresh_token": "agnic_rt_xyz789...",
"token_type": "Bearer",
"expires_in": 2592000,
"scope": "payments:sign balance:read"
}Token Expiration:
- Access tokens: 30–60 days (varies by client type)
- Refresh tokens: 90 days
- N8N tokens: 1 year (for automation workflows)
Persist expires_at = now + expires_in * 1000 and refresh before expiry (a 5-minute buffer is a good default) so an in-flight request never hits a 401.
Using the Access Token
# Check user's balance
curl https://api.agnic.ai/api/balance \
-H "Authorization: Bearer agnic_at_abc123..."
# Make an AI Gateway request
curl https://api.agnic.ai/v1/chat/completions \
-H "Authorization: Bearer agnic_at_abc123..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}]
}'
# Get transaction history
curl https://api.agnic.ai/api/transactions \
-H "Authorization: Bearer agnic_at_abc123..."Refreshing Tokens
When the access token is about to expire, exchange the refresh token for a new pair:
curl -X POST https://api.agnic.ai/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"refresh_token": "agnic_rt_xyz789...",
"client_id": "<your client id>"
}'If refresh fails (expired, revoked, or user disconnected your app), destroy the local session and restart the authorization flow.
Authorization Parameters
| Parameter | Required | Description |
|---|---|---|
client_id | Yes | The Client ID issued in /oauth-clients |
redirect_uri | Yes | Must exact-match one of your registered Authorized Redirect URIs |
response_type | Yes | Must be code |
scope | No | Space-separated scopes (default: payments:sign balance:read) |
state | Yes | CSRF token — returned unchanged on the callback |
code_challenge | Recommended | Base64url(SHA256(code_verifier)) — required for public clients |
code_challenge_method | With code_challenge | Must be S256 |
prompt | No | Controls consent behavior — see below |
display | No | popup for popup-style auth windows |
login_hint | No | Pre-fill the email on the login screen |
Scopes
See the full list at Available Scopes. Common combinations:
payments:sign balance:read— most apps (charge the user, read balance)payments:sign balance:read transactions:read— apps that show spend history
Prompt Parameter
The prompt parameter follows the OpenID Connect standard:
| Value | Behavior |
|---|---|
| (omitted) | Auto-approve if user previously consented |
none | Silent auth only — error if consent needed |
consent | Force consent screen even if previously consented |
login | Force re-authentication before authorization |
Returning users are auto-approved by default (like Google/GitHub). Use prompt=consent to force users to review permissions again.
PKCE (Required for Public Clients)
Public clients — single-page apps, mobile, CLIs — cannot safely store a client_secret. PKCE replaces it with a per-flow secret.
// 1. Generate a verifier (random 32+ bytes, base64url)
const codeVerifier = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));
// 2. Derive the challenge
const codeChallenge = base64UrlEncode(
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier))
);
// 3. Send the challenge on /oauth/authorize
const authUrl = new URL('https://api.agnic.ai/oauth/authorize');
authUrl.search = new URLSearchParams({
client_id: '<your client id>',
redirect_uri: 'https://yourapp.com/callback',
response_type: 'code',
scope: 'payments:sign balance:read',
state: crypto.randomUUID(),
code_challenge: codeChallenge,
code_challenge_method: 'S256',
}).toString();
// 4. After the callback, send the verifier on /oauth/token
const tokenResponse = await fetch('https://api.agnic.ai/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
code: authCode,
redirect_uri: 'https://yourapp.com/callback',
client_id: '<your client id>',
code_verifier: codeVerifier,
}),
});Store the code_verifier in a short-lived session cookie or server-side session between Step 1 and Step 4, and clear it after use.
CLI Client
The agnic CLI (v2.0.0+) uses OAuth2 Authorization Code + PKCE to authenticate via the browser — the same pattern as gh auth login, vercel login, and stripe login.
Client ID: agnic_cli
Flow:
- CLI starts a localhost HTTP server on a random port
- Browser opens to
api.agnic.ai/oauth/authorizewith PKCE challenge - User signs in and sets spending limits on the consent screen
- Browser redirects to
http://localhost:<port>/callbackwith auth code - CLI exchanges code + PKCE verifier for tokens (90-day expiry)
Security:
- PKCE is required for CLI clients (server rejects without
code_challenge) - Localhost redirect only (
http://localhost:<port>/callbackorhttp://127.0.0.1:<port>/callback) - HTTP is correct for loopback per RFC 8252 §7.3
- Server binds to
127.0.0.1only (no network exposure) - Random ephemeral port, state parameter verified, 5-minute timeout
agnic auth login
# Opens browser → sign in → set limits → authenticated!Reference Implementation
A full working OAuth2 integration (Next.js, PKCE, refresh, popup flow, session storage) lives in the PixelAI reference app. Use it as a template when wiring up your own client.