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:
- User visits
/auth/login - Redirects to Civic Auth for authentication
- Civic Auth redirects back to
/auth/callback - Server issues a JWT stored as an
auth_tokencookie
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:
- Client sends
POST /api/oauth/device/codeto request a device code - Server returns a
device_code,user_code, andverification_uri - User opens the verification URL in a browser and enters the code
- Client polls
POST /api/oauth/device/tokenuntil the user approves - 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:
- Client discovers auth server via
GET /.well-known/oauth-protected-resource - Optionally registers dynamically via
POST /oauth/register(RFC 7591) - Redirects user to
GET /oauth/authorizewith PKCE challenge - User approves and is redirected back with an authorization code
- 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