Customer Tokens
Customer tokens are short-lived bearer tokens that scope API access to a specific customer. They enable multi-tenant applications where customers make requests from frontends or mobile apps, with tier limits applied when the customer is subscribed.
When to Use Customer Tokens
Use customer tokens when:
- Customers need to call the AI API from your frontend
- Building mobile apps with AI features
- You want usage tracked and limited per customer
- You need per-customer tracking (tiered or tierless)
Use secret keys instead when:
- Making server-side API calls from your backend
- Running batch jobs or background processing
- Requests aren’t attributable to a specific customer
Token Minting (Backend Required)
Customer tokens are minted by your backend using a secret key. This is the supported flow for client apps.
sequenceDiagram
participant Client as Browser/Mobile
participant Backend as Your Backend
participant MR as ModelRelay
Client->>Backend: 1. Request token
Backend->>MR: 2. Mint token (secret key)
MR-->>Backend: Customer token
Backend-->>Client: 3. Return token
Client->>MR: 4. API request (bearer token)
MR-->>Client: Response
Backend Minting
Project scope is derived from the secret key. Do not include projectId/project_id in the request body—requests that include it are rejected.
Important: The customer must exist before minting a token. If you call customerToken() with a non-existent customerExternalId, you’ll get a 404 error. Use getOrCreateCustomerToken() to automatically create the customer if they don’t exist.
import { ModelRelay } from "@modelrelay/sdk";
const mr = ModelRelay.fromSecretKey(process.env.MODELRELAY_SECRET_KEY!);
// Mint a token for a customer (creates customer if needed)
const token = await mr.auth.getOrCreateCustomerToken({
externalId: "user_42", // Your user ID
email: "user@example.com", // Required for customer creation
});
// Return to frontend
res.json({
token: token.token,
expiresAt: token.expiresAt,
});
// Mint a token for a customer (creates customer if needed)
token, err := client.Auth.GetOrCreateCustomerToken(ctx, sdk.GetOrCreateCustomerTokenRequest{
ExternalID: sdk.NewCustomerExternalID("user_42"),
Email: "user@example.com",
})
if err != nil {
return err
}
// Return to frontend
json.NewEncoder(w).Encode(map[string]any{
"token": token.Token,
"expires_at": token.ExpiresAt,
})
// Mint a token for a customer (creates customer if needed)
let token = client
.auth()
.get_or_create_customer_token(GetOrCreateCustomerTokenRequest {
external_id: "user_42".into(),
email: "user@example.com".into(),
..Default::default()
})
.await?;
// Return to frontend
let response = serde_json::json!({
"token": token.token,
"expires_at": token.expires_at,
});
Using customerToken() Directly
If you’ve already created the customer separately (e.g., during user registration), you can use customerToken() directly:
// Customer must already exist - returns 404 if not found
const token = await mr.auth.customerToken({
customerExternalId: "user_42",
});
// Customer must already exist - returns 404 if not found
token, err := client.Auth.CustomerToken(ctx, sdk.CustomerTokenRequest{
CustomerExternalID: sdk.NewCustomerExternalID("user_42"),
})
// Customer must already exist - returns 404 if not found
let token = client
.auth()
.customer_token(CustomerTokenRequest::for_external_id("user_42"))
.await?;
Identifying Customers
Provide exactly one of:
customerId— ModelRelay’s internal UUIDcustomerExternalId— Your own user identifier
Using customerExternalId is recommended since it maps to your user system.
Token Lifetime
| Setting | Default | Maximum |
|---|---|---|
| TTL | 7 days | 30 days |
Override the default with ttlSeconds:
const token = await mr.auth.customerToken({
customerExternalId: "user_42",
ttlSeconds: 3600, // 1 hour
});
Shorter TTLs are more secure but require more frequent token refreshes.
Using Tokens
On the frontend, use the token as a bearer token:
Raw Fetch
const response = await fetch("https://api.modelrelay.ai/api/v1/responses", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
input: [{ role: "user", content: "Hello!" }],
}),
});
With the SDK
import { ModelRelay } from "@modelrelay/sdk";
// Initialize with the customer token
const mr = ModelRelay.fromToken(customerToken);
const answer = await mr.responses.text(
"claude-sonnet-4-5",
"You are a helpful assistant.",
"Hello!"
);
Token Providers
For automatic token refresh when using the backend minting approach:
import { ModelRelay } from "@modelrelay/sdk";
// In your backend: create a /api/token endpoint
app.get("/api/token", async (req, res) => {
const mr = ModelRelay.fromSecretKey(process.env.MODELRELAY_SECRET_KEY!);
const token = await mr.auth.customerToken({
customerExternalId: req.user.id,
});
res.json({ token: token.token, expiresAt: token.expiresAt });
});
// In your frontend: use a token provider
const mr = new ModelRelay({
tokenProvider: {
getToken: async () => {
const res = await fetch("/api/token");
const { token } = await res.json();
return token;
},
},
});
Token Response Fields
When you obtain a token, the response includes:
| Field | Type | Description |
|---|---|---|
token |
string | The JWT bearer token |
expiresAt |
Date | When the token expires |
expiresIn |
number | Seconds until expiration |
projectId |
string | Project the token is scoped to |
customerId |
string | Internal customer UUID |
customerExternalId |
string | Your customer identifier |
tierCode |
string | Customer’s tier (e.g., “free”, “pro”) |
Security Considerations
Token Storage
- Store tokens in memory or short-lived browser storage (sessionStorage)
- Avoid localStorage for sensitive tokens
- Never expose secret keys to the frontend
Token Validation
Tokens are validated on every request:
- Signature verification (tamper-proof)
- Expiration check
- Customer and project scope enforcement
- Tier limits applied automatically
Best Practices
- Keep secret keys server-side — Use backend minting, never expose secret keys
- Use short TTLs for sensitive apps — 1 hour is reasonable for high-security apps
- Handle expiration gracefully — Implement token refresh before expiry
- Log token issuance — Track which users get tokens for auditing
Next Steps
- Authentication — API key types overview
- First Request — Make your first API call
- Streaming — Real-time response streaming