Skip to main content

Error Response Format

All errors follow a consistent JSON structure:
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "details": { }
  }
}
FieldTypeDescription
codestringMachine-readable error identifier
messagestringHuman-readable explanation
detailsobjectAdditional context (optional)

HTTP Status Codes

The API uses standard HTTP status codes:
CodeMeaningDescription
200OKRequest succeeded
201CreatedResource created successfully
400Bad RequestInvalid request data or parameters
401UnauthorizedMissing or invalid API key
403ForbiddenInsufficient permissions (scope error)
404Not FoundResource doesn’t exist
409ConflictResource already exists (e.g., duplicate email)
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error (contact support)

Common Errors

Authentication Errors (401)

No API key provided in request headers.
{
  "error": {
    "code": "MISSING_API_KEY",
    "message": "API key required. Provide via X-API-Key header or Authorization: Bearer <key>"
  }
}
Solution: Add your API key to the X-API-Key header.
API key doesn’t match the expected format.
{
  "error": {
    "code": "INVALID_API_KEY_FORMAT",
    "message": "Invalid API key format. Key must start with 'dem_'"
  }
}
Solution: Ensure your API key starts with dem_ and is 47 characters total.
API key not found in database or has been deactivated.
{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid or inactive API key"
  }
}
Solution: Generate a new API key in your dashboard settings.
API key has passed its expiration date.
{
  "error": {
    "code": "EXPIRED_API_KEY",
    "message": "API key has expired"
  }
}
Solution: Create a new API key and update your integration.

Permission Errors (403)

API key doesn’t have the required scope.
{
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "This API key does not have the required scope: contacts:write",
    "requiredScope": "contacts:write",
    "availableScopes": ["contacts:read", "surveys:read"]
  }
}
Solution: Create a new API key with the required scope, or add the scope to your existing key in the dashboard.

Validation Errors (400)

Request data failed validation.
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request data",
    "details": {
      "email": ["Invalid email address"],
      "firstName": ["First name is required"]
    }
  }
}
Solution: Fix the validation errors listed in details and retry.

Resource Errors

Requested resource doesn’t exist.
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Contact not found"
  }
}
Solution: Verify the resource ID is correct and belongs to your organization.
Contact with email already exists.
{
  "error": {
    "code": "CONTACT_EXISTS",
    "message": "A contact with this email already exists",
    "details": {
      "contactId": "uuid"
    }
  }
}
Solution: Use PATCH /api/v1/contacts/:id to update the existing contact instead.
Trying to execute an inactive sequence.
{
  "error": {
    "code": "SEQUENCE_INACTIVE",
    "message": "Sequence is not active"
  }
}
Solution: Activate the sequence in your dashboard before executing it via API.

Rate Limiting (429)

Too many requests in a short period.
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please try again later.",
    "details": {
      "retryAfter": 60
    }
  }
}
Solution: Wait for the time specified in retryAfter (seconds) before retrying. Implement exponential backoff in your integration.Rate Limits:
  • 1,000 requests per hour per organization
  • 100 requests per minute per organization

Server Errors (500)

Unexpected server-side error.
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An internal error occurred. Please try again later."
  }
}
Solution: This is a temporary error. Retry your request with exponential backoff. If the error persists, contact support@demeterrr.com.

Error Handling Best Practices

async function makeApiRequest(url, options) {
  try {
    const response = await fetch(url, options);
    const data = await response.json();

    if (!response.ok) {
      // Handle API error
      console.error(`API Error: ${data.error.code}`, data.error.message);

      if (response.status === 401) {
        // Handle authentication error
        throw new Error('Authentication failed. Check your API key.');
      } else if (response.status === 403) {
        // Handle permission error
        throw new Error(`Missing scope: ${data.error.requiredScope}`);
      } else if (response.status === 429) {
        // Handle rate limit with retry
        const retryAfter = data.error.details?.retryAfter || 60;
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        return makeApiRequest(url, options); // Retry
      }

      throw new Error(data.error.message);
    }

    return data;
  } catch (error) {
    console.error('Request failed:', error);
    throw error;
  }
}

Need Help?

If you encounter persistent errors or need assistance:

Support

Email our support team

Status Page

Check API status