Skip to content

Error Handling

When a request fails, the Flowstate API returns a structured JSON error response. This page describes the error format, common error codes, and best practices for handling errors in your integration.

Error Response Format

All errors follow a consistent structure:

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The 'email' field must be a valid email address.",
    "details": [
      {
        "field": "email",
        "message": "Invalid email format"
      }
    ],
    "errorId": "err_x7k9m2p4q1"
  }
}
FieldTypeAlways PresentDescription
codestringYesMachine-readable error code (see table below)
messagestringYesHuman-readable description of the error
detailsarrayNoAdditional context (e.g., field-level validation)
errorIdstringYesUnique identifier for this error occurrence

Error Codes

CodeHTTP StatusDescription
VALIDATION_ERROR400The request body or query parameters are invalid
UNAUTHORIZED401Missing, invalid, or expired API key
FORBIDDEN403Valid API key but insufficient permissions for this operation
NOT_FOUND404The requested resource does not exist
CONFLICT409The operation conflicts with the current state of the resource
INTERNAL_ERROR500An unexpected server error occurred

HTTP Status Codes

StatusMeaningWhen It Happens
200OKSuccessful read or update
201CreatedSuccessful resource creation
204No ContentSuccessful deletion
400Bad RequestInvalid parameters, malformed JSON, validation errors
401UnauthorizedMissing or invalid API key
403ForbiddenKey lacks required permission scope
404Not FoundResource or route does not exist
409ConflictDuplicate resource or state conflict
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server failure

Validation Errors

When a 400 VALIDATION_ERROR is returned, the details array contains one entry per invalid field:

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed.",
    "details": [
      {
        "field": "firstName",
        "message": "Required field is missing"
      },
      {
        "field": "email",
        "message": "Must be a valid email address"
      },
      {
        "field": "startDate",
        "message": "Must be a valid ISO 8601 date (YYYY-MM-DD)"
      }
    ],
    "errorId": "err_v4l1d8t10n"
  }
}

Rate Limiting

The API enforces rate limits to ensure fair usage. When you exceed the limit, you will receive a 429 Too Many Requests response:

json
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Please retry after the specified time.",
    "errorId": "err_r8t3l1m1t"
  }
}

The response includes a Retry-After header indicating how many seconds to wait before retrying:

HTTP/1.1 429 Too Many Requests
Retry-After: 30

TIP

Rate limits are generous for typical integration use cases. If you consistently hit limits, consider reducing your request frequency or batching operations.

Error ID for Support

Every error response includes a unique errorId. If you need to contact Flowstate support about an issue, include this ID so the support team can quickly locate the relevant logs:

errorId: err_x7k9m2p4q1

Best Practices

Retry Strategy

Implement exponential backoff for transient errors:

javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    // Success — return the response
    if (response.ok) return response;

    // Rate limited — wait and retry
    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get("Retry-After") || "30", 10);
      await sleep(retryAfter * 1000);
      continue;
    }

    // Server error — exponential backoff
    if (response.status >= 500) {
      const delay = Math.pow(2, attempt) * 1000;
      await sleep(delay);
      continue;
    }

    // Client error (4xx) — do not retry, return immediately
    return response;
  }

  throw new Error("Max retries exceeded");
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

Handle Specific Error Codes

javascript
const response = await fetch(url, options);
const body = await response.json();

if (!response.ok) {
  switch (body.error.code) {
    case "VALIDATION_ERROR":
      // Log field-level details and fix the request
      console.error("Validation failed:", body.error.details);
      break;
    case "UNAUTHORIZED":
      // API key is invalid or expired — rotate it
      console.error("Authentication failed — check your API key");
      break;
    case "FORBIDDEN":
      // Key lacks permissions — update key scopes
      console.error("Permission denied:", body.error.message);
      break;
    case "NOT_FOUND":
      // Resource does not exist — check the ID
      console.error("Resource not found:", body.error.message);
      break;
    default:
      // Log the errorId for support
      console.error(`Error ${body.error.code}: ${body.error.message} (${body.error.errorId})`);
  }
}

Log the Error ID

Always log the errorId from error responses. If you need to escalate an issue to Flowstate support, this ID allows the team to trace the exact request through server logs.

Flowstate Documentation