Documentation

Login with Navy (OAuth)

Let people sign into your app with their NavyAI account and call models on their behalf — no manual API-key pasting. NavyAI is a standard OAuth 2.0 authorization-code provider with PKCE. This is ideal if you run a chat UI or tool and want your users to bring their own NavyAI plan.

How it works

  1. You register an OAuth app in your NavyAI dashboard and get a client_id and client_secret.
  2. Your site sends the user to NavyAI's /v1/oauth/authorize screen.
  3. The user signs in with Discord (if needed) and approves the requested scopes.
  4. NavyAI redirects back to your redirect_uri with a single-use code.
  5. Your backend exchanges that code at /v1/oauth/token for an access_token (+ refresh_token).
  6. You use the access token to read the user's profile at /v1/oauth/me, and — if you requested the inference scope — to call /v1/chat/completions and every other inference endpoint, billed to the user's own plan.

Registering an app

Open the Dashboard, go to Developer → OAuth Apps, and create an app. You'll set:

  • Name & description — shown to users on the consent screen.
  • Redirect URIs — exact-match allowlist. NavyAI only ever redirects back to a URI you registered here.
  • Homepage / logo URL — optional branding for the consent screen.

The client_secret is shown once at creation. Store it server-side; if you lose it, rotate it (the old one stops working immediately). Every user can register up to 5 apps.

Scopes

Request the least you need, space-separated:

  • identity — read the user's NavyAI id, username, avatar and current plan.
  • email — read the email on the user's account.
  • inference — send API requests (chat, images, audio, embeddings) billed to the user's plan. Also makes /v1/oauth/me return the user's API key.

Step 1 — Send the user to the authorize screen

Redirect the browser to:

Code
1https://api.navy/v1/oauth/authorize
2  ?client_id=navy-client-...
3  &redirect_uri=https://yourapp.com/auth/navy/callback
4  &response_type=code
5  &scope=identity%20inference
6  &state=RANDOM_ANTI_CSRF_STRING
7  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
8  &code_challenge_method=S256
  • state — an opaque random string you generate and later verify to prevent CSRF.
  • code_challenge / code_challenge_method — PKCE. Generate a random code_verifier, then send code_challenge = BASE64URL(SHA256(code_verifier)) with method S256. Required for public clients (SPAs/mobile); recommended for everyone.

After the user approves, NavyAI redirects to:

Code
1https://yourapp.com/auth/navy/callback?code=ONE_TIME_CODE&state=YOUR_STATE

If the user denies, you get ?error=access_denied&state=... instead.

Step 2 — Exchange the code for tokens

From your backend (so the secret stays private), POST to the token endpoint:

Bash
1curl -X POST https://api.navy/v1/oauth/token \
2  -H "Content-Type: application/json" \
3  -d '{
4    "grant_type": "authorization_code",
5    "code": "ONE_TIME_CODE",
6    "redirect_uri": "https://yourapp.com/auth/navy/callback",
7    "client_id": "navy-client-...",
8    "client_secret": "navy-secret-...",
9    "code_verifier": "THE_ORIGINAL_VERIFIER"
10  }'

Response:

JSON
1{
2  "access_token": "navy-oat-...",
3  "token_type": "Bearer",
4  "expires_in": 3600,
5  "refresh_token": "navy-ort-...",
6  "scope": "identity inference"
7}

Access tokens live for 1 hour. Keep the refresh token to mint new ones without sending the user through consent again.

Step 3 — Read the user's profile

1curl https://api.navy/v1/oauth/me \
2  -H "Authorization: Bearer navy-oat-..."

api_key is only included when the inference scope was granted.

Step 4 — Call models on the user's behalf

The OAuth access token works directly as a Bearer credential on the inference endpoints — usage is billed to the user's own key and plan:

Bash
1curl -X POST https://api.navy/v1/chat/completions \
2  -H "Authorization: Bearer navy-oat-..." \
3  -H "Content-Type: application/json" \
4  -d '{
5    "model": "claude-sonnet-4.6",
6    "messages": [{"role": "user", "content": "Hello from my app!"}]
7  }'

You can point any OpenAI-compatible SDK at https://api.navy/v1 and pass the OAuth access token as the API key. Alternatively, use the api_key returned by /v1/oauth/me if you prefer a stable credential.

Refreshing an expired access token

Bash
1curl -X POST https://api.navy/v1/oauth/token \
2  -H "Content-Type: application/json" \
3  -d '{
4    "grant_type": "refresh_token",
5    "refresh_token": "navy-ort-...",
6    "client_id": "navy-client-...",
7    "client_secret": "navy-secret-..."
8  }'

Each refresh rotates both tokens: use the new access_token and refresh_token from the response and discard the old ones.

Revoking access

Either side can disconnect:

  • Users revoke an app from Dashboard → Connected Apps at any time.
  • Your app can revoke a token programmatically:
Bash
1curl -X POST https://api.navy/v1/oauth/revoke \
2  -H "Content-Type: application/json" \
3  -d '{ "token": "navy-oat-... or navy-ort-..." }'

Revoking kills the whole grant — both the access and refresh tokens for that user+app die together, and the user must re-authorize.

Endpoint reference

  • GET /v1/oauth/authorize — start the flow (browser redirect).
  • POST /v1/oauth/token — exchange a code, or refresh (grant_type: authorization_code | refresh_token). Client auth via body or HTTP Basic.
  • GET /v1/oauth/me — userinfo; authenticated with the Bearer access token. Returns fields gated by scope.
  • POST /v1/oauth/revoke — revoke an access or refresh token.

Security notes

  • Always keep the client_secret server-side. For browser/mobile clients that can't hold a secret, use PKCE and omit the secret.
  • Always generate and verify state to defend against CSRF.
  • redirect_uri must exactly match one you registered — no wildcards, no trailing-slash mismatches.
  • Tokens and secrets are stored hashed on NavyAI's side; treat the plaintext values you receive as sensitive.
  • OAuth error responses follow the OAuth 2.0 shape: { "error": "...", "error_description": "..." }.