Troubleshooting
Common issues, solutions, and debugging tips for integrating with the Debitura Customer API. This guide helps you quickly resolve problems and optimize your integration.
Common Issues
Authentication Problems
Issue: "Invalid API credentials"
Symptoms:
- 401 Unauthorized responses
- "Invalid API key" error messages
- Authentication endpoint returns error
Solutions:
-
Verify credentials:
// Check your credentials are correct
console.log('API Key:', process.env.DEBITURA_API_KEY);
console.log('API Secret length:', process.env.DEBITURA_API_SECRET?.length); -
Check credential format:
- API keys should start with
dk_live_ordk_test_ - Ensure no leading/trailing whitespace
- Verify credentials are from the correct environment (test vs production)
- API keys should start with
-
Regenerate credentials:
- Log into Debitura portal
- Navigate to Settings > API Credentials
- Generate new credentials if needed
-
Environment configuration:
// Ensure environment variables are loaded
require('dotenv').config();
if (!process.env.DEBITURA_API_KEY) {
throw new Error('DEBITURA_API_KEY not configured');
}
Issue: "Token expired"
Symptoms:
- 401 responses after initial success
- "Token has expired" error messages
- Intermittent authentication failures
Solutions:
-
Implement token refresh:
async function ensureValidToken() {
if (!accessToken || Date.now() >= tokenExpiry) {
await authenticate();
}
} -
Add buffer time:
// Refresh 5 minutes before actual expiry
const tokenExpiry = Date.now() + ((expiresIn - 300) * 1000); -
Handle 401 responses:
async function request(endpoint) {
let response = await fetch(endpoint, { headers });
if (response.status === 401) {
// Token expired, re-authenticate
await authenticate();
response = await fetch(endpoint, { headers });
}
return response;
}
Case Creation Issues
Issue: "Validation error: Invalid debtor information"
Symptoms:
- 400 Bad Request responses
- Validation error messages
- Cases not created
Common causes and solutions:
-
Invalid CVR number:
// CVR must be exactly 8 digits
const cvr = debtorCvr.replace(/\D/g, '').padStart(8, '0');
if (cvr.length !== 8) {
throw new Error('CVR must be 8 digits');
} -
Missing required fields:
// Required fields checklist
const requiredFields = [
'debtorName',
'debtorEmail',
'principalAmount',
'currency',
'invoiceNumber',
'dueDate'
];
for (const field of requiredFields) {
if (!caseData[field]) {
throw new Error(`Missing required field: ${field}`);
}
} -
Invalid date format:
// Dates must be ISO 8601 format
const dueDate = new Date(originalDate).toISOString().split('T')[0];
// Result: "2024-01-15" -
Invalid email format:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(debtorEmail)) {
throw new Error('Invalid email format');
}
Issue: "Duplicate case detected"
Symptoms:
- 409 Conflict responses
- "Case already exists" error
- Cannot create case for same debtor/invoice
Solutions:
-
Check for existing cases:
async function createCaseIfNotExists(caseData) {
// Search for existing case
const existing = await debituraClient.getCases({
referenceNumber: caseData.invoiceNumber
});
if (existing.cases.length > 0) {
console.log('Case already exists:', existing.cases[0].caseId);
return existing.cases[0];
}
return await debituraClient.createCase(caseData);
} -
Handle duplicates gracefully:
try {
const newCase = await createCase(caseData);
return newCase;
} catch (error) {
if (error.statusCode === 409) {
// Duplicate - retrieve existing case
const existing = await getCaseByReference(caseData.invoiceNumber);
return existing;
}
throw error;
}
Payment Recording Issues
Issue: "Payment amount exceeds remaining balance"
Symptoms:
- 400 Bad Request when recording payment
- "Amount exceeds outstanding balance" error
Solutions:
-
Check current balance first:
async function recordPaymentSafely(caseId, paymentData) {
const caseDetails = await debituraClient.getCase(caseId);
if (paymentData.amount > caseDetails.remainingBalance) {
// Adjust payment amount or handle overpayment
console.warn('Payment exceeds balance, adjusting amount');
paymentData.amount = caseDetails.remainingBalance;
}
return await debituraClient.recordPayment(caseId, paymentData);
} -
Handle partial payments:
const payment = {
amount: Math.min(paymentAmount, caseDetails.remainingBalance),
isPartial: paymentAmount < caseDetails.remainingBalance
};
Issue: "Payment date in the future"
Symptoms:
- Validation error on payment date
- Payment not recorded
Solution:
// Ensure payment date is not in the future
const paymentDate = new Date(originalDate);
const today = new Date();
if (paymentDate > today) {
console.warn('Payment date in future, using today');
paymentData.paymentDate = today.toISOString().split('T')[0];
}
Webhook Issues
Issue: "Webhook deliveries failing"
Symptoms:
- Webhooks marked as failed in logs
- Not receiving webhook events
- Timeout errors
Solutions:
-
Respond quickly:
app.post('/webhooks/debitura', (req, res) => {
// Acknowledge immediately (within 5 seconds)
res.status(200).send('OK');
// Process asynchronously
processWebhook(req.body).catch(console.error);
}); -
Check endpoint accessibility:
# Test webhook endpoint is accessible
curl -X POST https://your-app.com/webhooks/debitura \
-H "Content-Type: application/json" \
-d '{"test": true}' -
Verify HTTPS certificate:
# Check SSL certificate is valid
openssl s_client -connect your-app.com:443 -servername your-app.com -
Review firewall rules:
- Ensure Debitura IPs are not blocked
- Check application firewall settings
- Verify load balancer configuration
Issue: "Invalid webhook signature"
Symptoms:
- Signature verification fails
- 401 responses to webhook deliveries
Solutions:
-
Verify signature correctly:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
// Use raw body, not parsed JSON
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload) // Raw string, not object
.digest('hex');
return signature === expectedSignature;
}
// Express configuration
app.use('/webhooks', express.raw({ type: 'application/json' })); -
Check webhook secret:
// Ensure using correct webhook secret
const secret = process.env.DEBITURA_WEBHOOK_SECRET;
if (!secret) {
console.error('DEBITURA_WEBHOOK_SECRET not configured');
}
Rate Limiting
Issue: "Rate limit exceeded"
Symptoms:
- 429 Too Many Requests responses
- "Rate limit exceeded" errors
- Requests being throttled
Solutions:
-
Implement exponential backoff:
async function requestWithRetry(endpoint, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await debituraClient.request(endpoint);
} catch (error) {
if (error.statusCode === 429) {
const delay = Math.pow(2, i) * 1000;
console.log(`Rate limited, waiting ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
} -
Implement request queuing:
class RateLimitedClient {
constructor(requestsPerSecond) {
this.queue = [];
this.processing = false;
this.interval = 1000 / requestsPerSecond;
}
async request(endpoint) {
return new Promise((resolve, reject) => {
this.queue.push({ endpoint, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
const { endpoint, resolve, reject } = this.queue.shift();
try {
const result = await debituraClient.request(endpoint);
resolve(result);
} catch (error) {
reject(error);
}
setTimeout(() => {
this.processing = false;
this.processQueue();
}, this.interval);
}
} -
Use batch endpoints:
// Instead of creating cases individually
for (const data of casesData) {
await createCase(data); // Don't do this
}
// Use batch endpoint if available
await createCasesBatch(casesData);
Debugging Tips
Enable Debug Logging
// Enable detailed logging
const DEBUG = process.env.DEBUG === 'true';
async function request(endpoint, options) {
if (DEBUG) {
console.log('Request:', {
endpoint,
method: options.method,
headers: options.headers,
body: options.body
});
}
const response = await fetch(endpoint, options);
if (DEBUG) {
console.log('Response:', {
status: response.status,
headers: Object.fromEntries(response.headers.entries())
});
}
return response;
}
Validate Requests Before Sending
function validateCaseData(caseData) {
const errors = [];
if (!caseData.debtorName || caseData.debtorName.length < 2) {
errors.push('Debtor name must be at least 2 characters');
}
if (caseData.principalAmount <= 0) {
errors.push('Principal amount must be greater than 0');
}
if (!['DKK', 'EUR', 'USD'].includes(caseData.currency)) {
errors.push('Invalid currency code');
}
const dueDate = new Date(caseData.dueDate);
if (dueDate > new Date()) {
errors.push('Due date cannot be in the future');
}
if (errors.length > 0) {
throw new Error(`Validation failed:\n${errors.join('\n')}`);
}
return true;
}
Test in Sandbox Environment
// Use sandbox for testing
const baseUrl = process.env.NODE_ENV === 'production'
? 'https://api.debitura.com'
: 'https://sandbox-api.debitura.com';
const client = new DebituraClient({
apiKey: process.env.DEBITURA_API_KEY,
apiSecret: process.env.DEBITURA_API_SECRET,
baseUrl
});
Performance Optimization
Reduce API Calls
-
Cache frequently accessed data:
const caseCache = new Map();
async function getCaseCached(caseId, ttl = 60000) {
const cached = caseCache.get(caseId);
if (cached && Date.now() - cached.timestamp < ttl) {
return cached.data;
}
const data = await debituraClient.getCase(caseId);
caseCache.set(caseId, { data, timestamp: Date.now() });
return data;
} -
Use webhooks instead of polling:
// Don't poll for status changes
setInterval(async () => {
const cases = await getCases(); // Avoid this
}, 60000);
// Use webhooks instead
app.post('/webhooks/debitura', handleWebhook); -
Batch operations when possible:
// Get multiple cases efficiently
const cases = await debituraClient.getCases({
caseIds: ['id1', 'id2', 'id3'].join(',')
});
Getting Help
Support Resources
- API Documentation: API Reference
- Integration Examples: Integration Examples
- Workflow Guides:
Contact Support
If you cannot resolve an issue:
-
Gather information:
- API request details
- Response codes and error messages
- Timestamps
- Case IDs or reference numbers
- Environment (test/production)
-
Check status page:
- Visit status.debitura.com
- Check for ongoing incidents
- Review scheduled maintenance
-
Contact support:
- Email: api-support@debitura.com
- Include all gathered information
- Provide reproduction steps
Report Bugs
To report bugs:
- Verify issue in sandbox environment
- Provide minimal reproduction case
- Include API version and client library versions
- Submit detailed bug report with examples
Error Code Reference
Common Error Codes
| Code | Meaning | Solution |
|---|---|---|
| 400 | Bad Request | Check request validation, review required fields |
| 401 | Unauthorized | Verify API credentials, check token expiry |
| 403 | Forbidden | Check permissions, verify API key scope |
| 404 | Not Found | Verify case ID, check resource exists |
| 409 | Conflict | Check for duplicate cases, review business rules |
| 422 | Unprocessable Entity | Fix validation errors in request data |
| 429 | Too Many Requests | Implement rate limiting, add retry logic |
| 500 | Internal Server Error | Retry request, contact support if persists |
| 503 | Service Unavailable | Check status page, retry with backoff |
Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid debtor information",
"details": [
{
"field": "debtorCvr",
"message": "CVR number must be 8 digits"
}
],
"requestId": "req_123456"
}
}
Related Resources
For urgent production issues, contact our 24/7 support line. See your account dashboard for contact details.