Token Types
The Typograph API uses two types of access tokens, each designed for different use cases. Understanding which token type to use is essential for successful API integration.
Overview
| Token Type | Obtained Via | Has User Context | Use Case |
|---|---|---|---|
| User Token | Authorization Code flow | Yes (sub claim present) | User-facing applications |
| Client Token | Client Credentials flow | No | Server-to-server integrations |
User Tokens
User tokens represent a specific user's authorization to your application. They are obtained through the Authorization Code flow, where a user explicitly grants your application access.
Characteristics
- Contains a
subclaim (user ID) - Can access all API endpoints
- Actions are attributed to the user
- Required for user-specific data
When to Use
- Building user-facing applications
- Accessing user-specific data (e.g.,
/v1/identity/users/me) - Performing actions on behalf of a user
- Any operation that requires user context
Obtaining a User Token
# Step 1: Redirect user to authorization
https://api.typograph.nl/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://your-app.com/callback
&scope=openid profile file
&state=random_state
&code_challenge=CODE_CHALLENGE
&code_challenge_method=S256
# Step 2: Exchange authorization code for tokens
curl -X POST https://api.typograph.nl/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://your-app.com/callback" \
-d "code_verifier=CODE_VERIFIER"
Introspection Response
{
"active": true,
"client_id": "019b28fb-a11e-7641-a28f-e978f892ec06",
"token_type": "Bearer",
"grant_type": "authorization_code",
"auth_type": "user",
"scope": "openid profile file",
"sub": "019b28fb-a11e-7641-a28f-e978f892ec07",
"exp": 1734567890,
"iat": 1734395090
}
Client Tokens
Client tokens represent your application itself, without any user context. They are obtained through the Client Credentials flow.
Characteristics
- No
subclaim (no user context) - Limited to specific endpoints
- Actions are attributed to the application
- Cannot access user-specific endpoints
When to Use
- Server-to-server integrations
- Background jobs and scheduled tasks
- Webhook subscription management
- Automated processing pipelines
Obtaining a Client Token
curl -X POST https://api.typograph.nl/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=webhook publisher converter"
Introspection Response
{
"active": true,
"client_id": "019b28fb-a11e-7641-a28f-e978f892ec06",
"token_type": "Bearer",
"grant_type": "client_credentials",
"auth_type": "client",
"scope": "webhook publisher converter",
"exp": 1734567890,
"iat": 1734395090
}
Endpoint Requirements
Token-type requirements are set per route at the gateway. There are three buckets:
Endpoints Accepting Either Token Type (any)
These endpoints work with both user tokens and client tokens.
| Endpoint | Service | Notes |
|---|---|---|
POST /v1/document/validate | Document | Validate a template against the schema |
POST /v1/document/upgrade | Document | Migrate a template to a newer schema version |
GET/POST /v1/publisher/jobs | Publisher | Create/list publisher jobs |
GET /v1/publisher/jobs/{...} | Publisher | Job details, output files |
GET/POST /v1/converter/jobs | Converter | Create/list converter jobs |
GET /v1/converter/jobs/{...} | Converter | Job details, output files |
GET /oauth/userinfo | Identity | OpenID userinfo |
Endpoints Requiring User Tokens
Everything under /v1/file/*, /v1/webhook/*, /v1/subscription/*, and /v1/identity/* requires a user token. This is because the request needs user context to resolve resource ownership, webhook ownership, subscription quota, and so on.
| Service | Path | Notes |
|---|---|---|
| File | /v1/file/* | Teams, projects, templates, media, sharing |
| Webhook | /v1/webhook/* | All webhook endpoints — subscriptions, deliveries, retries, events list |
| Subscription | /v1/subscription/* | Subscription status, usage, add-ons |
| Identity | /v1/identity/users/me, /v1/identity/organizations/*, /v1/identity/oauth/scopes/me | Profile, organizations, scope introspection |
User-owned resources (files, webhook subscriptions, subscription plans) need user context to resolve ownership and quota. Stateless operations (document validate/upgrade, publisher and converter jobs) don't, so they accept either token type.
Token Type Validation
The API validates token types at the gateway level. If you use the wrong token type, you'll receive a clear error message.
Error Response
{
"error": "invalid_token_type",
"error_description": "This endpoint requires a user token obtained via authorization_code flow. You provided a client token from client_credentials flow."
}
HTTP Status
Token type errors return 403 Forbidden with error code invalid_token_type.
Identifying Token Type
Via Introspection
Use the token introspection endpoint to check your token's type:
curl -X POST https://api.typograph.nl/oauth/introspect \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=YOUR_ACCESS_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
Check these fields in the response:
| Field | User Token | Client Token |
|---|---|---|
grant_type | authorization_code or refresh_token | client_credentials |
auth_type | user | client |
sub | User UUID | Not present |
Via Token Response
The token response includes the scope, but doesn't directly indicate token type. Use introspection to verify.
Refreshed Tokens
When you refresh a token using the refresh_token grant, the new token preserves the original token type:
- Refreshing a user token produces another user token
- The
subclaim is preserved - The
grant_typein introspection showsrefresh_token - The
auth_typeremainsuser
{
"grant_type": "refresh_token",
"auth_type": "user",
"sub": "019b28fb-a11e-7641-a28f-e978f892ec07"
}
Common Scenarios
Scenario 1: Web Application
A web application where users log in and manage their templates.
Solution: Use Authorization Code flow with PKCE to obtain user tokens.
// User authenticates via OAuth
const userToken = await authenticateUser();
// Access user's templates
const templates = await fetch('/v1/file/teams/my-team/templates', {
headers: { 'Authorization': `Bearer ${userToken}` }
});
Scenario 2: Backend Publisher / Converter Service
A backend service that generates PDFs or converts files on a schedule.
Solution: Client Credentials works — publisher and converter endpoints accept either token type.
const clientToken = await getClientToken();
// Works — publisher accepts client tokens
const job = await fetch('https://api.typograph.nl/v1/publisher/jobs', {
method: 'POST',
headers: {
'Authorization': `Bearer ${clientToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
input: {
template: { url: 'https://example.com/template.json' },
manifest: { url: 'https://example.com/manifest.json' }
},
output: {
upload: { url: 'https://example.com/upload-target' },
config: { type: 'pdf' }
}
})
});
// Would FAIL — /v1/file/* requires a user token
const templates = await fetch('https://api.typograph.nl/v1/file/templates', {
headers: { 'Authorization': `Bearer ${clientToken}` }
});
For file operations you'll need to capture a user token (Authorization Code + PKCE) during a prior interactive flow and store the refresh token securely for background use.
Scenario 3: Webhook Consumer
A service that manages its own webhook subscriptions.
Solution: Use a user token. All /v1/webhook/* endpoints require user context because subscriptions are owned by a user within an organization.
const userToken = await getUserToken(); // from Authorization Code + PKCE
const subscription = await fetch('https://api.typograph.nl/v1/webhook/subscriptions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Pipeline notifications',
client_id: '<your-oauth-client-uuid>',
endpoint: { url: 'https://your-service.com/webhook' },
event_types: ['publication.completed', 'conversion.completed']
})
});
Scenario 4: Hybrid Application
A service that needs both user context and background processing.
Solution: Use user tokens for user-initiated actions, store refresh tokens for background tasks.
// User authenticates
const { accessToken, refreshToken } = await authenticateUser();
// Store refresh token securely for later use
await secureStorage.store(userId, refreshToken);
// Later, in a background job
const storedRefreshToken = await secureStorage.get(userId);
const newAccessToken = await refreshAccessToken(storedRefreshToken);
// Now you have a user token for the background job
Best Practices
-
Choose the right flow - Use Authorization Code for user-facing apps, Client Credentials for server-to-server only when accessing endpoints that accept client tokens.
-
Check endpoint requirements - Before integrating, verify whether the endpoint requires a user token.
-
Handle errors gracefully - If you receive
invalid_token_type, check your authentication flow. -
Store tokens securely - For background processing with user context, securely store and refresh user tokens.
-
Use introspection - When debugging, use token introspection to verify your token type.
Related Documentation
- Authentication - Complete OAuth 2.0 guide
- Scopes - API permission scopes
- Rate Limiting - Request limits and quotas