PAPI
API Reference

Authentication

API key auth, JWT auth, OAuth, and all auth endpoints

Authentication

PAPI uses two authentication systems:

  • API keys -- For SDK and programmatic access to market data and trading endpoints
  • JWT tokens -- For dashboard sessions and account management endpoints

Both are sent as Bearer tokens in the Authorization header.

API Key Authentication

Getting an API Key

  1. Sign up at dashboard.tylerthebuildor.com
  2. Create an API key from the dashboard
  3. Save the key immediately -- it is shown only once

Each user is limited to one API key.

Using an API Key

curl https://papi.tylerthebuildor.com/polymarket/markets \
  -H "Authorization: Bearer papi_sk_live_a1b2c3d4e5f6..."

Scopes

API keys are scoped to control access:

ScopeAccess
readMarket data endpoints: markets, events, orderbooks, trades, candlesticks
tradeTrading endpoints: orders, positions, balance. Implies read.
adminAll endpoints

Attempting to access a trading endpoint with a read-only key returns a 403 forbidden error.

How Keys Are Stored

API keys are SHA-256 hashed before storage. The raw key is never persisted. On each request, the provided key is hashed and matched against stored hashes.

Verify Your Key

curl https://papi.tylerthebuildor.com/me \
  -H "Authorization: Bearer papi_sk_live_a1b2c3d4e5f6..."
{
  "user_id": "usr_abc123",
  "key_prefix": "papi_sk_live_a1b2",
  "scopes": ["read", "trade"]
}

JWT Authentication

JWT tokens are used by the dashboard for browser-based sessions. They are issued by the auth endpoints below.

The access token is returned in the response body. The refresh token is set as an HTTP-only cookie.


Auth Endpoints

All auth endpoints are public (no Authorization header required), except /auth/logout.

Sign Up

POST /auth/signup
curl -X POST https://papi.tylerthebuildor.com/auth/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "securepassword123",
    "name": "Jane Doe"
  }'

Response depends on the current access mode:

Whitelist mode (email pre-approved):

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Doe",
    "avatar_url": null,
    "role": "user"
  }
}

Open mode (email verification required):

{
  "message": "Verification email sent",
  "user_id": "usr_abc123"
}

Log In

POST /auth/login
curl -X POST https://papi.tylerthebuildor.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "securepassword123"
  }'

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Doe",
    "avatar_url": null,
    "role": "user"
  }
}

A refresh_token cookie is also set (HttpOnly, Secure, SameSite=Strict).

Verify Email

POST /auth/verify-email
curl -X POST https://papi.tylerthebuildor.com/auth/verify-email \
  -H "Content-Type: application/json" \
  -d '{ "token": "verification-token-from-email" }'
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Doe",
    "avatar_url": null,
    "role": "user"
  }
}

Forgot Password

POST /auth/forgot-password
curl -X POST https://papi.tylerthebuildor.com/auth/forgot-password \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com" }'

Always returns 200 regardless of whether the email exists:

{
  "message": "If that email exists, a reset link has been sent"
}

Reset Password

POST /auth/reset-password
curl -X POST https://papi.tylerthebuildor.com/auth/reset-password \
  -H "Content-Type: application/json" \
  -d '{
    "token": "reset-token-from-email",
    "new_password": "newsecurepassword456"
  }'
{
  "message": "Password reset successfully"
}

Google OAuth Callback

POST /auth/google/callback
curl -X POST https://papi.tylerthebuildor.com/auth/google/callback \
  -H "Content-Type: application/json" \
  -d '{
    "code": "google-auth-code",
    "code_verifier": "pkce-code-verifier",
    "redirect_uri": "https://dashboard.tylerthebuildor.com/auth/callback"
  }'
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Doe",
    "avatar_url": "https://...",
    "role": "user"
  }
}

GitHub OAuth Callback

POST /auth/github/callback

Same request/response shape as the Google callback.

Refresh Token

POST /auth/refresh

Reads the refresh_token from the request cookie. No request body needed.

curl -X POST https://papi.tylerthebuildor.com/auth/refresh \
  -b "refresh_token=..."
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Doe",
    "avatar_url": null,
    "role": "user"
  }
}

Log Out

POST /auth/logout

Requires authentication. Reads the refresh token cookie and invalidates it.

curl -X POST https://papi.tylerthebuildor.com/auth/logout \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -b "refresh_token=..."
{
  "message": "Logged out successfully"
}

The refresh_token cookie is cleared in the response.


Exchange Credentials

After authenticating with PAPI, the API uses your stored exchange credentials to proxy trading requests. Credentials are:

  • Encrypted with AES-256-GCM at the application layer
  • Stored in PostgreSQL
  • Decrypted only at request time, in memory
  • Never logged or exposed in API responses

Credential management is done via the Account endpoints.

Security Best Practices

  • Store API keys in environment variables -- never hardcode them
  • Use the minimum scope needed (read for market data, trade only if placing orders)
  • Revoke and regenerate keys if compromised
  • Monitor last_used_at on your API key for unauthorized usage
  • Exchange credentials are encrypted at rest, but treat the PAPI API key as the master secret

On this page