Error Codes
All Datafly Signal API error responses follow a consistent format with an HTTP status code and an application-level error code. This page documents all HTTP status codes and application error codes returned by the platform.
Error Response Format
{
"error": {
"code": "INVALID_PIPELINE_KEY",
"message": "The provided pipeline key is not valid or has been revoked."
}
}| Field | Type | Description |
|---|---|---|
error.code | string | Machine-readable error code for programmatic handling |
error.message | string | Human-readable description of the error |
HTTP Status Codes
| Status | Meaning | When Used |
|---|---|---|
200 | OK | Successful request |
201 | Created | Resource successfully created |
204 | No Content | Successful delete operation |
400 | Bad Request | Invalid JSON, missing required fields, malformed request |
401 | Unauthorized | Missing, invalid, or expired authentication token |
403 | Forbidden | Authenticated but insufficient permissions for the requested action |
404 | Not Found | Requested resource does not exist |
409 | Conflict | Resource already exists (duplicate name, email, etc.) |
422 | Unprocessable Entity | Request is well-formed but fails validation (invalid field values, schema violations) |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Unexpected server-side error |
Application Error Codes
Authentication Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
INVALID_CREDENTIALS | 401 | Email or password is incorrect | Verify credentials and retry |
EXPIRED_TOKEN | 401 | The access token has expired | Use the refresh endpoint to obtain a new access token |
INVALID_TOKEN | 401 | The token is malformed or has an invalid signature | Re-authenticate to obtain a new token pair |
INVALID_REFRESH_TOKEN | 401 | The refresh token is invalid, expired, or has been rotated out | Re-authenticate with email and password |
ACCOUNT_LOCKED | 401 | Too many failed login attempts; account temporarily locked | Wait for the lockout period to expire (default 15 minutes) |
Authorization Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
FORBIDDEN | 403 | The authenticated user’s role does not have permission for this action | Contact an admin to upgrade your role or use an account with sufficient permissions |
OWNER_REQUIRED | 403 | This action requires the owner role | Only the organisation owner can perform this action |
Pipeline Key Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
INVALID_PIPELINE_KEY | 401 | The pipeline key is not valid or has been revoked | Check the pipeline key in your source configuration |
PIPELINE_KEY_MISSING | 401 | No pipeline key provided in the request | Include the pipeline key in the Authorization header or script tag |
Validation Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | 422 | One or more fields failed validation | Check the error message for specific field errors |
INVALID_EVENT_TYPE | 422 | The type field is not one of: track, page, identify, group | Use a valid event type |
MISSING_REQUIRED_FIELD | 422 | A required field is missing from the request | Include all required fields as documented |
INVALID_CONFIG | 422 | Integration or transformation config is invalid | Review the config format for the specific integration type |
PAYLOAD_TOO_LARGE | 422 | Single event payload exceeds the 32 KB limit | Reduce the size of the event properties |
BATCH_TOO_LARGE | 422 | Batch request exceeds 500 events or 500 KB | Split the batch into smaller batches |
Resource Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
NOT_FOUND | 404 | The requested resource does not exist | Verify the resource ID and try again |
DUPLICATE_RESOURCE | 409 | A resource with the same unique identifier already exists | Use a different name or identifier |
DUPLICATE_EMAIL | 409 | A user with this email already exists in the organisation | Use a different email address or update the existing user |
Rate Limiting Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
RATE_LIMITED | 429 | Request rate limit exceeded | Wait for the period indicated in the Retry-After header |
Consent Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
CONSENT_DENIED | — | Event dropped because required consent was not granted | This is not returned to the client; it is logged internally. Ensure users have granted the required consent categories |
CONSENT_DENIED is an internal status used by Delivery Workers when an event is dropped due to insufficient consent. It does not produce an HTTP error response. You can see consent-denied events in the Management UI under the integration’s delivery logs.
Delivery Errors
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
DELIVERY_FAILED | — | Event delivery to a vendor failed after all retry attempts | Check the integration’s dead letter queue in the Management UI. Review the vendor’s API status |
VENDOR_API_ERROR | — | The vendor API returned an error response | Check vendor API credentials and configuration. Review the vendor’s error message in the delivery logs |
TRANSFORMATION_ERROR | — | An error occurred while applying transformation rules | Review the transformation config. Use the dry-run endpoint to test with a sample event |
Delivery errors (DELIVERY_FAILED, VENDOR_API_ERROR, TRANSFORMATION_ERROR) are internal pipeline errors. They do not produce HTTP error responses to API clients. They are recorded in delivery logs and visible in the Management UI.
Error Handling Best Practices
Retry Strategy
- 4xx errors (except 429): Do not retry. Fix the request and try again.
- 429 errors: Retry after the duration specified in the
Retry-Afterheader. - 5xx errors: Retry with exponential backoff (1s, 2s, 4s, 8s, max 30s).
Example Error Handling
async function callAPI(url, options) {
const response = await fetch(url, options);
if (!response.ok) {
const body = await response.json();
const { code, message } = body.error;
if (response.status === 401 && code === 'EXPIRED_TOKEN') {
// Refresh the token and retry
await refreshToken();
return callAPI(url, options);
}
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
await sleep(parseInt(retryAfter, 10) * 1000);
return callAPI(url, options);
}
throw new Error(`API error ${code}: ${message}`);
}
return response.json();
}