Workflow: Bearer Token Model
A deep dive into Debitura's bearer token authentication model for referral partners, enabling secure delegated access on behalf of linked clients.
Overview
The bearer token model allows referral partners to act on behalf of their linked clients without requiring customers to share their Debitura credentials or API keys directly with the platform.
How It Works
┌─────────────────┐
│ Your Platform │
└────────┬────────┘
│ 1. Link Client
▼
┌─────────────────┐ 2. Bearer Token
│ Debitura API │─────────────────────►
└────────┬────────┘
│ 3. Use Token
│ for Actions
▼
┌─────────────────┐
│ Client Account │
└─────────────────┘
Flow Steps
- Client Linking - You link a client to your platform
- Token Issuance - Debitura issues a bearer token specific to that client
- Delegated Actions - Use the token to create cases on the client's behalf
- Revenue Attribution - Cases created via token are attributed to your partnership
Token Characteristics
Scope and Permissions
Each bearer token is:
- Client-Specific - Works only for one linked client
- Partner-Scoped - Associated with your referral partnership
- Long-Lived - Does not expire (unless revoked)
- Action-Limited - Can only perform allowed operations
Allowed Operations:
- Create debt collection cases
- List cases created through your platform
- Update cases you created
- Cancel cases you created
Restricted Operations:
- Cannot access client's payment methods
- Cannot modify client settings
- Cannot access cases from other sources
- Cannot manage client's team members
Token Format
Bearer tokens follow this format:
tok_live_abc123def456ghi789jkl012mno345pqr678stu901
Structure:
- Prefix:
tok_(all tokens) - Environment:
live_ortest_ - Random component: High-entropy random string
Obtaining Bearer Tokens
During Client Linking
Bearer tokens are issued when you link a client:
POST /v1/referral-partners/clients
Response:
{
"client_id": "cli_abc123",
"bearer_token": "tok_live_xyz789abc012",
"status": "active"
}
After White-Label Onboarding
For white-label onboarding, the token is issued after completion:
# Webhook notification
{
"event": "client.onboarding_completed",
"data": {
"client_id": "cli_abc123",
"bearer_token": "tok_live_xyz789abc012",
"onboarding_completed_at": "2024-01-15T10:30:00Z"
}
}
Using Bearer Tokens
Authentication Header
Include the token in the Authorization header:
curl https://api.debitura.com/v1/cases \
-H "Authorization: Bearer tok_live_xyz789abc012" \
-H "Content-Type: application/json" \
-d '{...}'
Example: Create a Case
const BEARER_TOKEN = 'tok_live_xyz789abc012';
const response = await fetch('https://api.debitura.com/v1/cases', {
method: 'POST',
headers: {
'Authorization': `Bearer ${BEARER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
debtor: {
name: 'John Doe',
email: 'john@example.com',
country: 'DK'
},
amount: {
value: 5000,
currency: 'DKK'
},
invoice_number: 'INV-001',
due_date: '2024-01-01'
})
});
const case = await response.json();
Token Storage
Security Best Practices
Do:
- ✅ Store tokens encrypted at rest
- ✅ Use environment variables or secret management
- ✅ Restrict database access to token table
- ✅ Encrypt tokens in backups
- ✅ Use HTTPS for all API calls
Don't:
- ❌ Store tokens in plain text
- ❌ Log tokens in application logs
- ❌ Expose tokens in client-side code
- ❌ Include tokens in URLs or query parameters
- ❌ Commit tokens to version control
Storage Example
// Good: Encrypted storage
const encryptedToken = await encrypt(bearerToken, ENCRYPTION_KEY);
await db.clients.update(clientId, {
bearer_token_encrypted: encryptedToken
});
// Usage
const encryptedToken = await db.clients.getBearerToken(clientId);
const bearerToken = await decrypt(encryptedToken, ENCRYPTION_KEY);
Token Management
Retrieving Stored Tokens
Retrieve a client's bearer token when needed:
GET /v1/referral-partners/clients/{client_id}/token
Response:
{
"bearer_token": "tok_live_xyz789abc012",
"issued_at": "2024-01-15T10:00:00Z",
"status": "active"
}
This endpoint returns the token in plain text. Use it sparingly and securely.
Token Rotation
Rotate a token if it's been compromised:
POST /v1/referral-partners/clients/{client_id}/rotate-token
Request:
{
"reason": "suspected_compromise",
"revoke_immediately": false
}
Response:
{
"new_bearer_token": "tok_live_new_token_456",
"old_bearer_token": "tok_live_xyz789abc012",
"old_token_expires_at": "2024-01-20T10:00:00Z"
}
Parameters:
| Field | Type | Description |
|---|---|---|
reason | string | Reason for rotation (for audit) |
revoke_immediately | boolean | If true, old token revoked instantly. If false, 24-hour grace period |
Grace Period
By default, token rotation provides a 24-hour grace period:
- Old token: Valid for 24 hours
- New token: Valid immediately
- Benefit: Zero-downtime rotation
Use revoke_immediately: true only for confirmed compromises.
Token Revocation
Permanently revoke a token:
POST /v1/referral-partners/clients/{client_id}/revoke-token
Use cases:
- Client relationship terminated
- Security incident
- Customer request
Effect:
- Token becomes invalid immediately
- All requests with token return
401 Unauthorized - Cannot be undone - must create new client linking
Token Validation
Validate Token Status
Check if a token is valid before use:
POST /v1/auth/validate-token
Request:
{
"token": "tok_live_xyz789abc012"
}
Response:
{
"valid": true,
"client_id": "cli_abc123",
"partner_id": "ref_partner_123",
"scopes": ["cases.create", "cases.read", "cases.update"],
"status": "active",
"issued_at": "2024-01-15T10:00:00Z"
}
Error Responses
401 Unauthorized - Invalid token
{
"error": {
"type": "authentication_error",
"message": "Invalid bearer token",
"code": "invalid_token"
}
}
403 Forbidden - Valid token, insufficient permissions
{
"error": {
"type": "permission_error",
"message": "Token does not have permission for this action",
"required_scope": "payments.read",
"token_scopes": ["cases.create", "cases.read"]
}
}
Security Considerations
Token Compromise Detection
Monitor for signs of token compromise:
- Requests from unexpected IP addresses
- Unusual request patterns or volumes
- Geographic anomalies
- Multiple failed authentication attempts
Automatic Revocation Triggers
Debitura may automatically revoke tokens when:
- Client relationship is terminated
- Suspicious activity detected
- Client requests revocation
- Partner account suspended
Webhook Notifications
Subscribe to token lifecycle events:
{
"event": "token.revoked",
"data": {
"client_id": "cli_abc123",
"token_prefix": "tok_live_xyz789",
"revoked_at": "2024-01-15T10:00:00Z",
"reason": "client_request"
}
}
Implementation Patterns
Pattern 1: Token Caching
Cache tokens to avoid repeated database lookups:
class TokenManager {
constructor() {
this.cache = new Map();
this.ttl = 3600; // 1 hour cache
}
async getToken(clientId) {
// Check cache
const cached = this.cache.get(clientId);
if (cached && cached.expiresAt > Date.now()) {
return cached.token;
}
// Fetch from database
const token = await db.clients.getBearerToken(clientId);
// Cache it
this.cache.set(clientId, {
token,
expiresAt: Date.now() + (this.ttl * 1000)
});
return token;
}
invalidate(clientId) {
this.cache.delete(clientId);
}
}
Pattern 2: Token Refresh Strategy
Proactively rotate tokens on a schedule:
async function scheduleTokenRotation(clientId) {
// Rotate every 90 days for security
const ROTATION_INTERVAL = 90 * 24 * 60 * 60 * 1000;
setInterval(async () => {
await rotateToken(clientId, {
reason: 'scheduled_rotation',
revoke_immediately: false // Grace period
});
}, ROTATION_INTERVAL);
}
Pattern 3: Secure Token Transmission
When webhook delivers tokens:
app.post('/webhooks/debitura', async (req, res) => {
const { event, data } = req.body;
if (event === 'client.onboarding_completed') {
const { client_id, bearer_token } = data;
// Encrypt immediately
const encrypted = await encrypt(bearer_token, ENCRYPTION_KEY);
// Store encrypted
await db.clients.update(client_id, {
bearer_token_encrypted: encrypted,
token_received_at: new Date()
});
// Never log the token
console.log(`Token received for client ${client_id}`);
res.json({ received: true });
}
});
Comparison with Other Models
Bearer Token vs API Key
| Aspect | Bearer Token | API Key |
|---|---|---|
| Scope | Single client | All clients |
| Lifetime | Long-lived | Long-lived |
| Rotation | Easy | Complex |
| Attribution | Automatic | Manual |
| Security | Client-specific | Platform-wide |
Bearer Token vs OAuth
| Aspect | Bearer Token | OAuth |
|---|---|---|
| Complexity | Simple | Complex |
| User consent | Implicit in linking | Explicit |
| Refresh | Manual rotation | Automatic refresh |
| Scopes | Fixed | Configurable |
| Best for | B2B platforms | User-facing apps |
Troubleshooting
Issue: Token works in sandbox but not production
Solution: Ensure you're using production token with production API URL
Issue: Token suddenly returns 401
Solution: Check if token was revoked or client relationship changed
Issue: Cannot find token after client linking
Solution: Verify webhook delivery or fetch via GET endpoint
Best Practices
- Encrypt tokens at rest in your database
- Rotate tokens on a regular schedule
- Monitor usage for suspicious patterns
- Cache tokens to reduce database load
- Handle revocation gracefully in your application
- Test rotation in staging before production
- Document token lifecycle for your team
Next Steps
- Client Linking Workflow - Complete linking guide
- Integration Examples - Real-world token usage
- Troubleshooting - Common issues and solutions