Documentation

Authentication

Promptmark uses JWT (JSON Web Tokens) as the primary API authentication mechanism. Tokens are issued during OAuth login and expire after 24 hours.

Bearer Token

All API and MCP requests require a JWT in the Authorization header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

The token contains the user’s ID and expiration time. The server validates it on every request.

How to Get a Token

1. OAuth 2.0 (Web Login)

The standard web login flow uses Civic Auth as the OAuth provider:

  1. User visits /auth/login
  2. Redirects to Civic Auth for authentication
  3. Civic Auth redirects back to /auth/callback
  4. Server issues a JWT stored as an auth_token cookie

This flow is for browser sessions. The cookie is HttpOnly and Secure.

2. Device Flow (MCP Clients)

For headless MCP clients that can’t open a browser directly:

  1. Client sends POST /api/oauth/device/code to request a device code
  2. Server returns a device_code, user_code, and verification_uri
  3. User opens the verification URL in a browser and enters the code
  4. Client polls POST /api/oauth/device/token until the user approves
  5. Server returns a JWT access token
# Step 1: Request device code
curl -X POST https://your-instance.com/api/oauth/device/code \
  -H "Content-Type: application/json" \
  -d '{"client_id": "your-client-id"}'

# Response:
# {
#   "device_code": "abc123",
#   "user_code": "ABCD-1234",
#   "verification_uri": "https://your-instance.com/auth/device",
#   "expires_in": 900,
#   "interval": 5
# }

# Step 2: User visits verification_uri and enters user_code

# Step 3: Poll for token
curl -X POST https://your-instance.com/api/oauth/device/token \
  -H "Content-Type: application/json" \
  -d '{"device_code": "abc123", "client_id": "your-client-id", "grant_type": "urn:ietf:params:oauth:grant-type:device_code"}'

# Response (after user approves):
# {
#   "access_token": "eyJhbGciOiJIUzI1NiIs...",
#   "token_type": "Bearer",
#   "expires_in": 86400
# }

3. OAuth 2.0 Authorization Code (MCP Clients)

For MCP clients that support full OAuth 2.0 with PKCE:

  1. Client discovers auth server via GET /.well-known/oauth-protected-resource
  2. Optionally registers dynamically via POST /oauth/register (RFC 7591)
  3. Redirects user to GET /oauth/authorize with PKCE challenge
  4. User approves and is redirected back with an authorization code
  5. Client exchanges code for token via POST /oauth/token

See OAuth Integration for the complete protocol details.

4. API Key

Each user has a personal API key visible in Settings. Pass it as a Bearer token:

Authorization: Bearer pk_your_api_key_here
Info
API keys provide the same access as JWT tokens. They don’t expire but can be regenerated in Settings, which invalidates the previous key.

Token Expiration

Token Type Expiration
JWT (cookie/OAuth) 24 hours
API Key No expiration (until regenerated)
Device code 15 minutes (for approval)
Authorization code 10 minutes (for exchange)

Checking Authentication

Use the ping endpoint to verify your token is valid:

curl https://your-instance.com/api/ping \
  -H "Authorization: Bearer YOUR_TOKEN"

# 200: {"status": "ok", "user_id": "123"}
# 401: Token is invalid or expired