Error Codes
Every error code the Bitelio API can return, what it means, and how to fix it
Overview
Bitelio API errors follow one consistent shape, designed so you can pinpoint the problem fast. Each error carries:
- A machine-readable error code your code can branch on
- A human-readable message describing the failure
- A suggestion pointing at the likely fix
- A request ID for debugging and support conversations
- Field-level validation details where they apply
HTTP Status Codes
200 OK
The request went through.
201 Created
The resource was created.
400 Bad Request
Something is wrong with the request format or its parameters — the error details say what.
401 Unauthorized
Authentication is missing or didn't succeed. Double-check your API key.
402 Payment Required
A billing cap was hit, or the operation needs a higher plan. Accompanies BILLING_LIMIT_EXCEEDED and UPGRADE_REQUIRED.
403 Forbidden
You're not allowed to touch this resource — review permissions and project status. Also used when the account's email still needs verifying (EMAIL_VERIFICATION_REQUIRED) or the project was disabled (PROJECT_DISABLED).
404 Not Found
There's no resource with that ID. Confirm the ID is right.
409 Conflict
The request clashes with existing state — e.g. creating a contact whose email is already registered, or renaming to an email that's taken. Accompanies CONFLICT.
422 Unprocessable Entity
Validation rejected the request. The errors array lists the offending fields.
429 Too Many Requests
You've gone over a rate limit. Back off before retrying, or move to a higher plan.
500 Internal Server Error
Something broke on our end. Reach out to support with the request ID.
Error Response Format
Every error uses this one structure:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"statusCode": 422,
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"errors": [
{
"field": "email",
"message": "Invalid email",
"code": "invalid_string"
}
],
"suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}Response Fields
| Field | Type | Description |
|---|---|---|
success | boolean | On errors this is always false |
error.code | string | Machine-readable identifier (catalogued below) |
error.message | string | Plain-language account of what failed |
error.statusCode | number | The HTTP status |
error.requestId | string | Per-request identifier for debugging |
error.errors | array | Per-field breakdown (validation failures only) |
error.details | object | Extra context, when available (optional) |
error.suggestion | string | A pointer toward the fix (optional) |
timestamp | string | When the error happened, as ISO 8601 |
Error Codes Reference
The code field is present on every error so you can handle them programmatically. The complete set:
Authentication & Authorization
| Code | Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Authentication failed for an unspecified reason |
INVALID_CREDENTIALS | 401 | The login details don't match |
MISSING_AUTH | 401 | No usable Authorization header on the request |
INVALID_API_KEY | 401 | That API key doesn't exist or isn't valid |
REVOKED_API_KEY | 401 | That key was valid and has been revoked — issue a new one |
EXPIRED_API_KEY | 401 | That key passed its expiry date — issue a new one |
FORBIDDEN | 403 | This action isn't permitted |
PROJECT_ACCESS_DENIED | 403 | You don't have access to this project |
PROJECT_DISABLED | 403 | The project is currently disabled |
EMAIL_VERIFICATION_REQUIRED | 403 | Verify the account's email address before doing this |
Validation & Input Errors
| Code | Status | Description |
|---|---|---|
BAD_REQUEST | 400 | Generic problem with the request |
VALIDATION_ERROR | 422 | Validation rejected the request (field errors attached) |
INVALID_EMAIL | 422 | The email address isn't well-formed |
INVALID_REQUEST_BODY | 400 | The body couldn't be parsed |
MISSING_REQUIRED_FIELD | 422 | A required field wasn't supplied |
Resource Errors
| Code | Status | Description |
|---|---|---|
RESOURCE_NOT_FOUND | 404 | No resource matches (generic) |
CONTACT_NOT_FOUND | 404 | No such contact |
TEMPLATE_NOT_FOUND | 404 | No such template |
CAMPAIGN_NOT_FOUND | 404 | No such campaign |
WORKFLOW_NOT_FOUND | 404 | No such workflow |
CONFLICT | 409 | Clashes with existing state (e.g. a duplicate) |
Rate Limiting & Billing
| Code | Status | Description |
|---|---|---|
RATE_LIMIT_EXCEEDED | 429 | Request rate is too high |
BILLING_LIMIT_EXCEEDED | 402 | A billing cap was reached |
UPGRADE_REQUIRED | 402 | The feature needs a higher plan |
Server Errors
| Code | Status | Description |
|---|---|---|
INTERNAL_SERVER_ERROR | 500 | Unexpected failure on our side |
DATABASE_ERROR | 500 | A database operation didn't complete |
EXTERNAL_SERVICE_ERROR | 500 | A downstream service was unreachable |
Common Error Examples
Authentication Errors
Invalid API Key
{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid secret API key. This endpoint requires a secret key (sk_*), not a public key.",
"statusCode": 401,
"requestId": "abc-123",
"suggestion": "Verify your API key is correct and starts with \"sk_\" for secret keys or \"pk_\" for public keys."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}Solution: Confirm the key itself and its type — endpoints that need a secret key expect a sk_ prefix, while event tracking uses pk_ keys.
Missing Authorization Header
{
"success": false,
"error": {
"code": "MISSING_AUTH",
"message": "Authorization header is required",
"statusCode": 401,
"requestId": "abc-123",
"suggestion": "Include an Authorization header with format: \"Authorization: Bearer YOUR_API_KEY\""
},
"timestamp": "2025-11-30T10:30:00.000Z"
}Solution: Send your key as a Bearer token in the Authorization header.
Validation Errors
Invalid Email Format
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"statusCode": 422,
"requestId": "abc-123",
"errors": [
{
"field": "email",
"message": "Invalid email",
"code": "invalid_string"
}
],
"suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}Solution: Send a well-formed email address. Look at the errors array to see exactly which fields were rejected.
Missing Required Fields
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"statusCode": 422,
"requestId": "abc-123",
"errors": [
{
"field": "event",
"message": "Required",
"code": "invalid_type"
},
{
"field": "email",
"message": "Required",
"code": "invalid_type"
}
],
"suggestion": "Required fields are missing. Ensure all required fields are included in your request."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}Solution: Add every required field to the request body.
Resource Errors
Template Not Found
{
"success": false,
"error": {
"code": "TEMPLATE_NOT_FOUND",
"message": "Template with ID \"tpl_abc123\" was not found",
"statusCode": 404,
"requestId": "abc-123",
"details": {
"resource": "Template",
"id": "tpl_abc123"
},
"suggestion": "Ensure the template ID is correct and belongs to your project. You can list available templates via the API."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}Solution: Make sure the template ID exists and lives in your project.
Rate Limiting
Rate Limit Exceeded
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please try again later.",
"statusCode": 429,
"requestId": "abc-123",
"suggestion": "You have exceeded the rate limit. Wait a moment before retrying, or upgrade your plan."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}Solution: Add retry logic with exponential backoff. If you routinely hit the ceiling, a plan upgrade raises it.
Success Response Format
When a request succeeds, the response comes back as success: true with a data object:
{
"success": true,
"data": {
"contact": "cnt_abc123",
"event": "evt_xyz789",
"timestamp": "2025-11-30T10:30:00.000Z"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
success | boolean | On success this is always true |
data | object | Endpoint-specific payload |
Handling Errors in Your Code
JavaScript/TypeScript Example
try {
const response = await fetch('https://api.bitelio.com/v1/track', {
method: 'POST',
headers: {
'Authorization': `Bearer ${publicKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
event: 'purchase',
email: 'user@example.com'
})
});
const data = await response.json();
if (!data.success) {
// Something went wrong
console.error(`Error [${data.error.code}]:`, data.error.message);
// Surface the API's suggested fix
if (data.error.suggestion) {
console.log('Suggestion:', data.error.suggestion);
}
// Keep the request ID around for support
console.log('Request ID:', data.error.requestId);
// Branch on the specific code
switch (data.error.code) {
case 'VALIDATION_ERROR':
// Print each field that failed
data.error.errors?.forEach(err => {
console.log(`${err.field}: ${err.message}`);
});
break;
case 'INVALID_API_KEY':
// Ask the user to re-check their key
break;
case 'REVOKED_API_KEY':
case 'EXPIRED_API_KEY':
// The key WAS valid: rotate it rather than asking anyone to re-check a typo
break;
case 'RATE_LIMIT_EXCEEDED':
// Retry later with backoff
break;
}
return;
}
// Success path
console.log('Event tracked:', data.data);
} catch (error) {
console.error('Network error:', error);
}Python Example
import requests
response = requests.post(
'https://api.bitelio.com/v1/track',
headers={
'Authorization': f'Bearer {public_key}',
'Content-Type': 'application/json'
},
json={
'event': 'purchase',
'email': 'user@example.com'
}
)
data = response.json()
if not data.get('success'):
error = data['error']
print(f"Error [{error['code']}]: {error['message']}")
# Surface the suggested fix
if 'suggestion' in error:
print(f"Suggestion: {error['suggestion']}")
# Keep the request ID around
print(f"Request ID: {error['requestId']}")
# Print field-level failures
if error['code'] == 'VALIDATION_ERROR':
for err in error.get('errors', []):
print(f"{err['field']}: {err['message']}")
else:
print(f"Event tracked: {data['data']}")Troubleshooting Guide
Using Request IDs
Each error ships with a requestId that follows the request across our whole stack. That makes it valuable in two directions:
For Developers:
- The ID is stamped on every log line for that request
- Grep your logs by ID and every related entry surfaces at once
- Follow one request across API → Database → Queue → Worker
For Support:
- Paste the request ID into your message
- Explain what you were attempting
- Attach the complete error response when you can
Example: Given the request ID f47ac10b-58cc-4372-a567-0e02b2c3d479, a log search on our side reveals:
- The request body exactly as it arrived
- The database queries that ran
- Any background jobs it spawned
- The full stack trace, if one exists
That means we can usually diagnose the problem straight away, without a back-and-forth asking you for more detail.
How to Find Request IDs
The request ID shows up in three places:
- Error responses: the
error.requestIdfield - Response headers: the
X-Request-IDheader (present on successes too) - Your application logs: write the header into your own logs so you can correlate
// Capture the request ID in your own logging
const response = await fetch('https://api.bitelio.com/v1/send', {
// ... your request
});
const requestId = response.headers.get('X-Request-ID');
console.log('Request ID:', requestId); // Store it for correlation
const data = await response.json();
if (!data.success) {
console.error('Error:', data.error.message);
console.error('Request ID:', data.error.requestId); // Matches the header
}Common Issues and Solutions
Best Practices
- Inspect
successfirst, before touching the rest of the response - Record request IDs so debugging and support requests go faster
- Fail gracefully, translating errors into messages your users understand
- Retry transient failures using exponential backoff
- Watch your error rates so problems surface early
- Branch on error codes, not just HTTP statuses, in your handling logic
Getting Help
Still stuck? Work through this list:
- Start with the
suggestionfield in the error itself - Compare your request against the API Reference
- Look up your exact error code in these docs
- Write to support and include your request ID
- Ask the community — other developers have often hit the same thing