Skip to main content
Every non-2xx response returns a consistent error object.

Error response structure

{
  "error": {
    "type": "validation_error",
    "code": "invalid_param",
    "message": "data_points[0].input_value must be a number",
    "param": "data_points[0].input_value",
    "request_id": "req_01abc...",
    "doc_url": "https://docs.cula.tech/technical-reference/error-handling",
    "details": [
      {
        "code": "invalid_type",
        "param": "data_points[0].input_value",
        "message": "Expected number, received string"
      }
    ]
  }
}
type
string
required
Error category. See Error types for the full list.
code
string
required
Machine-readable error code (e.g. external_id_in_use, field_invalid, rate_limit_exceeded).
message
string
required
Human-readable summary of the error. Safe to surface to operators or end users.
param
string
The field path that caused the error (e.g. data_points[0].input_value). Present for validation errors and some conflict errors.
doc_url
string
Link to relevant documentation when the error has a canonical doc anchor.
request_id
string
required
Unique request identifier. Also returned as the Cula-Request-Id response header. Include this value when contacting support.
details
array
Per-field errors for multi-field validation failures. Each entry has code, param, and message. See Multi-field validation.
existing
object
Present only on 409 external_id_in_use responses. Contains the existing resource’s id and external_id so you can resolve the conflict with a PUT.

Error types

TypeDescription
validation_errorRequest body or parameters failed validation
conflictResource state conflict (duplicate external_id, finalised submission)
not_foundResource does not exist or is outside the consumer’s scope
auth_errorMissing, invalid, or expired access token; or consumer not authorized for the requested organisation
rate_limitToo many requests — see Rate limits
internal_errorServer-side failure

HTTP status codes

StatusMeaning
200Success (GET, PUT)
201Created (POST)
204No content (DELETE)
400Bad request (malformed JSON, missing required fields or headers)
401Unauthorized (missing or invalid access token)
403Forbidden (consumer not authorized for the requested organisation)
404Not found
409Conflict
422Unprocessable entity (valid JSON, but semantically invalid)
429Rate limited
5xxServer error

Conflict handling

Duplicate external ID (409)

If you POST a resource with an external_id that already exists, the server returns 409 with the existing resource reference:
{
  "error": {
    "type": "conflict",
    "code": "external_id_in_use",
    "message": "A material-sourcing resource with this external_id already exists",
    "request_id": "req_01xyz...",
    "existing": {
      "id": "stp_01kqzcjrpxf27tge33jwvjhkff",
      "external_id": "MY-STEP-001"
    }
  }
}
Use the returned id with PUT to update the resource.

Finalised submission (409)

A PUT or PATCH on a resource whose downstream submission has been finalised returns:
{
  "error": {
    "type": "conflict",
    "code": "resource_in_finalised_submission",
    "message": "This resource belongs to a finalised submission and cannot be modified",
    "request_id": "req_01xyz..."
  }
}
Resources in finalised submissions are immutable. Contact support if you need to correct data after finalisation.

Authentication and authorisation errors

Missing organisation header (400)

Every request must include the Cula-Organisation-Id header. Omitting it returns:
{
  "error": {
    "type": "validation_error",
    "code": "missing_header",
    "message": "The Cula-Organisation-Id header is required. Every request must specify the target organisation.",
    "param": "Cula-Organisation-Id",
    "request_id": "req_01abc..."
  }
}

Unauthorised organisation (403)

If the authenticated consumer is not authorized for the organisation specified in Cula-Organisation-Id:
{
  "error": {
    "type": "auth_error",
    "code": "org_not_authorized",
    "message": "The authenticated consumer is not authorized to access this organisation.",
    "request_id": "req_01abc..."
  }
}

Invalid or expired token (401)

If the access token is missing, malformed, or expired:
{
  "error": {
    "type": "auth_error",
    "code": "invalid_token",
    "message": "The provided access token is invalid or has expired.",
    "request_id": "req_01abc..."
  }
}

Idempotency

POST with the same external_id is not idempotent. It returns 409 if the resource already exists, even when the payload matches. To update an existing resource, use PUT.

Multi-field validation

When multiple fields fail validation, the top-level message provides a summary and details[] contains individual field errors:
{
  "error": {
    "type": "validation_error",
    "code": "invalid_params",
    "message": "2 validation errors",
    "request_id": "req_01abc...",
    "details": [
      { "code": "required", "param": "config_id", "message": "config_id is required" },
      { "code": "invalid_type", "param": "data_points[0].input_value", "message": "Expected number" }
    ]
  }
}
The request_id is also returned as the Cula-Request-Id response header on every response, including successful ones. See Request IDs.