Skip to content

Standard Response Envelope

Every API response from Curaway follows a consistent envelope structure. This makes it predictable for frontend consumers and simplifies error handling across all endpoint groups.

Envelope Schema

{
  "success": true,
  "data": { ... },
  "errors": null,
  "meta": null,
  "api_version": "v1",
  "timestamp": "2026-03-31T12:00:00.000Z"
}

Field Definitions

Field Type Description
success boolean true if the request completed without errors, false otherwise
data object \| array \| null The response payload. null when success is false
errors array \| null List of error objects. null when success is true
meta object \| null Optional metadata such as pagination cursors, total counts, or request IDs
api_version string Always "v1" for the current API version
timestamp string ISO 8601 timestamp of when the response was generated (UTC)

Success Response

When a request succeeds, success is true, data contains the result, and errors is null.

Example: Retrieve a Case

Request:

GET /api/v1/cases/case-abc-123
X-Tenant-ID: tenant-apollo-001

Response (200 OK):

{
  "success": true,
  "data": {
    "id": "case-abc-123",
    "patient_id": "pat-001",
    "status": "in_review",
    "procedure": "Hip Replacement",
    "destination_country": "DE",
    "created_at": "2026-03-15T10:30:00.000Z",
    "updated_at": "2026-03-28T14:22:00.000Z",
    "assigned_provider_id": "prov-berlin-ortho-01",
    "document_checklist": {
      "total": 5,
      "completed": 3,
      "pending": ["medical_report", "insurance_letter"]
    }
  },
  "errors": null,
  "meta": {
    "request_id": "req-7f3a2b1c"
  },
  "api_version": "v1",
  "timestamp": "2026-03-31T12:00:00.000Z"
}

Example: List Providers with Pagination

Request:

GET /api/v1/providers/?country=DE&page=1&page_size=2
X-Tenant-ID: tenant-apollo-001

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "id": "prov-berlin-ortho-01",
      "name": "Berlin Orthopedic Center",
      "country": "DE",
      "specialties": ["orthopedics", "sports_medicine"],
      "accreditations": ["JCI", "ISO_9001"]
    },
    {
      "id": "prov-munich-cardio-01",
      "name": "Munich Heart Clinic",
      "country": "DE",
      "specialties": ["cardiology", "cardiac_surgery"],
      "accreditations": ["JCI"]
    }
  ],
  "errors": null,
  "meta": {
    "page": 1,
    "page_size": 2,
    "total_count": 12,
    "total_pages": 6,
    "request_id": "req-9e4c5d2a"
  },
  "api_version": "v1",
  "timestamp": "2026-03-31T12:01:00.000Z"
}

Example: Create a Patient (201 Created)

Request:

POST /api/v1/patients/
X-Tenant-ID: tenant-apollo-001
Content-Type: application/json

{
  "first_name": "Aisha",
  "last_name": "Patel",
  "date_of_birth": "1985-06-15",
  "email": "aisha.patel@example.com",
  "phone": "+971501234567",
  "country_of_residence": "AE"
}

Response (201 Created):

{
  "success": true,
  "data": {
    "id": "pat-a1b2c3d4",
    "first_name": "Aisha",
    "last_name": "Patel",
    "date_of_birth": "1985-06-15",
    "email": "aisha.patel@example.com",
    "phone": "+971501234567",
    "country_of_residence": "AE",
    "created_at": "2026-03-31T12:05:00.000Z"
  },
  "errors": null,
  "meta": null,
  "api_version": "v1",
  "timestamp": "2026-03-31T12:05:00.000Z"
}

Error Response

When a request fails, success is false, data is null, and errors contains one or more error objects.

Error Object Schema

Each error in the errors array follows this structure:

Field Type Description
code string Machine-readable error code (e.g., AUTH_TOKEN_EXPIRED_001)
message string Human-readable error description
field string \| null The request field that caused the error, if applicable

Example: Validation Error (422)

Request:

POST /api/v1/patients/
X-Tenant-ID: tenant-apollo-001
Content-Type: application/json

{
  "first_name": "",
  "email": "not-an-email"
}

Response (422 Unprocessable Entity):

{
  "success": false,
  "data": null,
  "errors": [
    {
      "code": "INTAKE_VALIDATION_001",
      "message": "first_name must not be empty",
      "field": "first_name"
    },
    {
      "code": "INTAKE_VALIDATION_001",
      "message": "Invalid email format",
      "field": "email"
    },
    {
      "code": "INTAKE_VALIDATION_001",
      "message": "last_name is required",
      "field": "last_name"
    }
  ],
  "meta": null,
  "api_version": "v1",
  "timestamp": "2026-03-31T12:10:00.000Z"
}

Example: Authentication Error (401)

Response (401 Unauthorized):

{
  "success": false,
  "data": null,
  "errors": [
    {
      "code": "AUTH_TOKEN_EXPIRED_001",
      "message": "JWT token has expired. Please re-authenticate.",
      "field": null
    }
  ],
  "meta": null,
  "api_version": "v1",
  "timestamp": "2026-03-31T12:15:00.000Z"
}

Frontend Usage Pattern

The consistent envelope allows a single response handler in the frontend:

async function apiCall<T>(url: string, options?: RequestInit): Promise<T> {
  const response = await fetch(url, {
    ...options,
    headers: {
      'X-Tenant-ID': getTenantId(),
      'Authorization': `Bearer ${getToken()}`,
      'Content-Type': 'application/json',
      ...options?.headers,
    },
  });

  const envelope = await response.json();

  if (!envelope.success) {
    throw new ApiError(envelope.errors, response.status);
  }

  return envelope.data as T;
}

Notes

  • The timestamp field is always in UTC and uses ISO 8601 format.
  • The meta field is only populated when there is relevant metadata (pagination, request tracing, etc.).
  • Multiple validation errors can be returned in a single response -- the errors array may contain more than one entry.
  • The field property in error objects is null for errors not tied to a specific input field (e.g., authentication failures, rate limiting).