Skip to main content

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 TypeObtained ViaHas User ContextUse Case
User TokenAuthorization Code flowYes (sub claim present)User-facing applications
Client TokenClient Credentials flowNoServer-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 sub claim (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 sub claim (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.

EndpointServiceNotes
POST /v1/document/validateDocumentValidate a template against the schema
POST /v1/document/upgradeDocumentMigrate a template to a newer schema version
GET/POST /v1/publisher/jobsPublisherCreate/list publisher jobs
GET /v1/publisher/jobs/{...}PublisherJob details, output files
GET/POST /v1/converter/jobsConverterCreate/list converter jobs
GET /v1/converter/jobs/{...}ConverterJob details, output files
GET /oauth/userinfoIdentityOpenID 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.

ServicePathNotes
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/meProfile, organizations, scope introspection
Why this split?

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:

FieldUser TokenClient Token
grant_typeauthorization_code or refresh_tokenclient_credentials
auth_typeuserclient
subUser UUIDNot 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 sub claim is preserved
  • The grant_type in introspection shows refresh_token
  • The auth_type remains user
{
"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

  1. 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.

  2. Check endpoint requirements - Before integrating, verify whether the endpoint requires a user token.

  3. Handle errors gracefully - If you receive invalid_token_type, check your authentication flow.

  4. Store tokens securely - For background processing with user context, securely store and refresh user tokens.

  5. Use introspection - When debugging, use token introspection to verify your token type.