View raw .mdDownload

REST API

Implement OAuth2 + PKCE and call AI Pass endpoints from any stack: Flutter, iOS, Android, desktop, CLI, or server. No SDK required.

Building a browser/web app instead? See the Web SDK. It handles PKCE, token storage, and the auth button for you.

Managing OAuth clients, payments, gift cards, or apps? See Management API.

Building with an AI agent? Open the one-click agent setup to create or reuse an app and copy a personalized implementation brief. The canonical raw instructions live at aipass.one/agent.md.

TL;DR flow

  1. Create an OAuth2 client →
  2. Generate PKCE + state →
  3. Send user to /oauth2/authorize
  4. Exchange code at /oauth2/token
  5. Store access_token + refresh_token
  6. Call /v1/* with the Bearer token

The same /v1/* resource URLs also accept AI Pass API keys. Existing /oauth2/v1/* and /apikey/v1/* integrations remain supported as aliases, and each old prefix keeps its previous OAuth-or-API-key credential contract. OAuth protocol URLs such as /oauth2/authorize and /oauth2/token do not move.

CORS is open on the shared and legacy resource APIs and on /oauth2/token, so mobile/web apps can exchange codes and call models directly without a custom backend.


1) Get a client ID

Register your app and obtain client_id (and optional client_secret) from the Developer Dashboard or via REST.

POST /api/v1/oauth2/clients
Content-Type: application/json

{
  "clientName": "My Flutter App",
  "redirectUri": "myapp://auth/callback",
  "requestedScopes": ["api:access", "profile:read"]
}

Save the client secret if you generate one. It is only shown once. Public apps (Flutter, web) typically use PKCE without a client secret.

2) Generate PKCE + state

Generate these per login:

  • code_verifier: random 43-128 chars
  • code_challenge: BASE64URL(SHA256(code_verifier))
  • state: random string to prevent CSRF

JavaScript

const verifier  = generateRandom(64);
const challenge = await sha256Base64Url(verifier);
const state     = generateRandom(24);

Dart (Flutter)

final verifier = generateRandomString(64);
final challenge = base64UrlEncode(
  sha256.convert(utf8.encode(verifier)).bytes
).replaceAll('=', '');
final state = generateRandomString(24);

Python

import secrets, hashlib, base64
code_verifier = secrets.token_urlsafe(64)[:64]
code_challenge = base64.urlsafe_b64encode(
    hashlib.sha256(code_verifier.encode()).digest()
).rstrip(b"=").decode()
state = secrets.token_urlsafe(16)

Keep code_verifier and state until the redirect returns so you can validate and exchange the code.

3) Launch the OAuth screen

Open the user's browser (or url_launcher in Flutter) to:

GET https://aipass.one/oauth2/authorize
  ?client_id=YOUR_CLIENT_ID
  &response_type=code
  &redirect_uri=YOUR_REDIRECT_URI
  &scope=api:access profile:read
  &state=STATE_VALUE
  &code_challenge=PKCE_CHALLENGE
  &code_challenge_method=S256

Use custom schemes like myapp://auth/callback on mobile. Validate the returned state before exchanging the code.

4) Exchange code & refresh

Exchange the authorization code

POST https://aipass.one/oauth2/token
Content-Type: application/json

{
  "grantType": "authorization_code",
  "code": "CODE_FROM_REDIRECT",
  "codeVerifier": "ORIGINAL_CODE_VERIFIER",
  "clientId": "YOUR_CLIENT_ID",
  "redirectUri": "YOUR_REDIRECT_URI"
}

Response fields: access_token, refresh_token, expires_in, token_type (Bearer), scope.

Refresh tokens

POST https://aipass.one/oauth2/token
Content-Type: application/json

{
  "grantType": "refresh_token",
  "refreshToken": "YOUR_REFRESH_TOKEN",
  "clientId": "YOUR_CLIENT_ID"
}

On 401 responses from the API, refresh once, then restart OAuth if the refresh fails too.

5) Discover models at runtime

Models change. Always list them at runtime. The default response is the OpenAI-compatible { "object": "list", "data": [...] } envelope:

curl "https://aipass.one/v1/models?type=image&method=image_edit" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: $CLIENT_ID" \
  | jq -r '.data[].id'
# Returns stable public IDs for the available image-edit models.

Use type, capability, and method query parameters instead of inferring behavior from provider prefixes or suffixes. Clients that only need the historical ID array can explicitly request ?detailed=false.

TaskCatalog filtersExample stable public IDs
Image edittype=image&method=image_editnano-banana-2-edit, gpt-image-2-edit
Image generationtype=image&method=image_generationnano-banana-2, imagen-4-ultra
Chat / texttype=text&method=chat_completionsgpt-5-mini, claude-haiku-4-5
TTStype=audio&method=audio_speechtts-1, gpt-4o-mini-tts
Transcriptiontype=audio&method=audio_transcriptionwhisper-1
Embeddingstype=embedding&method=embeddingstext-embedding-3-small

6) Make AI calls

Send the Bearer token in every request. The proxy mirrors OpenAI-style payloads.

Chat completion

curl -X POST https://aipass.one/v1/chat/completions \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{
        "model": "gpt-5-mini",
        "messages": [
          {"role":"system","content":"You answer briefly."},
          {"role":"user","content":"One tip for better focus?"}
        ],
        "max_tokens": 200,
        "stream": false
      }'

For streaming, set "stream": true and consume Server-Sent Events. Budget errors arrive as JSON; surface them to the user.

Image generation

curl -X POST https://aipass.one/v1/images/generations \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: $CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "imagen-4-ultra",
    "prompt": "A futuristic city at sunset",
    "size": "1024x1024",
    "n": 1,
    "response_format": "url"
  }'

# Always check both `url` and `b64_json`; different models return different shapes.

Image editing: single image

curl -X POST https://aipass.one/v1/images/edits \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: $CLIENT_ID" \
  -F "image=@selfie.jpg" \
  -F "prompt=Change the hairstyle to a sleek bob cut. Preserve the face and lighting." \
  -F "model=nano-banana-2-edit" \
  -F "size=1024x1024" \
  -F "response_format=url"
import requests
with open("selfie.jpg", "rb") as f:
    r = requests.post(
        "https://aipass.one/v1/images/edits",
        headers={"Authorization": f"Bearer {access_token}"},
        files={"image": f},
        data={
            "model": "nano-banana-2-edit",
            "prompt": "Change the hairstyle to a sleek bob cut. Preserve the face and lighting.",
            "size": "1024x1024",
            "response_format": "url",
        },
    )
url = r.json()["data"][0].get("url") or f"data:image/png;base64,{r.json()['data'][0]['b64_json']}"

Image editing: multi-image

Pass multiple -F image=@… for models that support multi-image input (nano-banana-2-edit, gpt-image-2-edit, nano-banana-pro-edit):

curl -X POST https://aipass.one/v1/images/edits \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: $CLIENT_ID" \
  -F "image=@target.jpg" \
  -F "image=@reference.jpg" \
  -F "prompt=Apply the hairstyle from the second image to the person in the first." \
  -F "model=nano-banana-2-edit"

The server treats repeated image form fields as an array.

7) Vision (multimodal)

Same /v1/chat/completions endpoint, but content is an array:

{
  "model": "gpt-5-mini",
  "messages": [{
    "role": "user",
    "content": [
      {"type": "text", "text": "What's in this image?"},
      {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQ..."}}
    ]
  }],
  "max_tokens": 2000
}

Compress images to ~800KB before encoding to base64. Vision models support: JPEG, PNG, GIF, WebP.

8) Audio + embeddings

# TTS
curl -X POST https://aipass.one/v1/audio/speech \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: $CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{"model":"tts-1","input":"Hello world","voice":"nova","response_format":"mp3"}' \
  --output speech.mp3

# Transcribe
curl -X POST https://aipass.one/v1/audio/transcriptions \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: $CLIENT_ID" \
  -F "file=@audio.mp3" \
  -F "model=whisper-1" \
  -F "language=en"

# Embeddings
curl -X POST https://aipass.one/v1/embeddings \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-AIPass-OAuth-Client-Id: $CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{"model":"text-embedding-3-small","input":["First text","Second text"]}'

9) Usage & balance tracking

Show users their remaining budget:

GET https://aipass.one/api/v1/usage/me/summary
Authorization: Bearer ACCESS_TOKEN
X-AIPass-OAuth-Client-Id: CLIENT_ID

// Response:
{
  "success": true,
  "data": {
    "totalCost": 2.45,
    "maxBudget": 10.00,
    "remainingBudget": 7.55
  }
}

Cache balance data and refresh periodically (every 5-10 minutes) to reduce API calls.

10) All allowed endpoints

All OAuth resource requests require both Authorization: Bearer ACCESS_TOKEN and X-AIPass-OAuth-Client-Id: CLIENT_ID. The server rejects a missing header or a value that differs from the signed token's client. AI endpoints also require the api:access scope. Base URL: https://aipass.one/v1.

CategoryEndpoints
ModelsGET /models, GET /models/{id}
ChatPOST /chat/completions
EmbeddingsPOST /embeddings
ImagesPOST /images/generations, POST /images/edits, POST /images/variations
AudioPOST /audio/speech, POST /audio/transcriptions
VideoPOST /videos, POST /videos/{id}/remix, GET /videos/{id}, GET /videos/{id}/content
User profileGET /oauth2/userinfo (needs profile:read)
Usage & BalanceGET /api/v1/usage/me/summary
RevokePOST /oauth2/revoke?token=ACCESS_TOKEN

Only allowlisted endpoints above are proxied. All calls require api:access scope.

For management endpoints (OAuth2 clients, payments, gift cards, spaces, apps), see the Endpoints Reference.

Want a fresh agent brief?

Choose your agent and copy one secure brief. It inspects the project, requests narrow setup approval, and creates only the public configuration it needs.

Run one-click setup

Stuck? We're happy to help on Discord

Active Discord community with the AI Pass team. Get unblocked on integration, ask about models, share what you're building.

Join AI Pass Discord