Skip to main content

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

  1. Client Linking - You link a client to your platform
  2. Token Issuance - Debitura issues a bearer token specific to that client
  3. Delegated Actions - Use the token to create cases on the client's behalf
  4. 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_ or test_
  • 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"
}
caution

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:

FieldTypeDescription
reasonstringReason for rotation (for audit)
revoke_immediatelybooleanIf 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

AspectBearer TokenAPI Key
ScopeSingle clientAll clients
LifetimeLong-livedLong-lived
RotationEasyComplex
AttributionAutomaticManual
SecurityClient-specificPlatform-wide

Bearer Token vs OAuth

AspectBearer TokenOAuth
ComplexitySimpleComplex
User consentImplicit in linkingExplicit
RefreshManual rotationAutomatic refresh
ScopesFixedConfigurable
Best forB2B platformsUser-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

  1. Encrypt tokens at rest in your database
  2. Rotate tokens on a regular schedule
  3. Monitor usage for suspicious patterns
  4. Cache tokens to reduce database load
  5. Handle revocation gracefully in your application
  6. Test rotation in staging before production
  7. Document token lifecycle for your team

Next Steps