Errors

Customer 360 uses conventional HTTP status codes. Successful responses wrap the payload in a { success, data } envelope. Business rejections use a structured error envelope with a stable machine code you can branch on. Auth failures and request-shape validation use FastAPI's detail shape.


The success envelope

Every successful call returns a JSON envelope:

  • success is always true on success.
  • data is the payload (an object or an array), present on most responses.
  • message is an optional short human-readable note.

Success with data

{
  "success": true,
  "data": {
    "accepted": 1,
    "duplicates": 0,
    "invalid": 0
  }
}

The error envelope

A business rejection returns success: false with an error object carrying a stable machine code, a human message, and an optional detail. Branch on the code; it is the contract, while the message is human-facing.

Auth failures (401, 403, 429) use FastAPI's plain { "detail": "..." } shape instead, and the HTTP status carries the meaning. See Authentication & scopes.

Business error

{
  "success": false,
  "error": {
    "code": "not_found",
    "message": "No customer matches the supplied selector."
  }
}

Business error with detail

{
  "success": false,
  "error": {
    "code": "member_taken",
    "message": "That memberId is already mapped to a different customer.",
    "detail": {
      "memberId": "SR-90210",
      "ownerCustomerPublicId": "cus_zzz999"
    }
  }
}

Error codes

These are the stable machine codes and their HTTP status. A consumer should branch on the code, not the message text.

CodeStatusMeaning
not_found404the resource or selector matched nothing
invalid_request422a malformed request
invalid_selector422zero or more than one lookup selector supplied
unknown_platform422the platform is not in the registry
member_taken409(loyalty) the memberId is already mapped to a different customer

Auth and rate-limit failures return { "detail": "..." } at 401, 403, and 429. Request-body validation failures return FastAPI's 422 with a detail array (below).


Per-event ingest results

Ingest is different: a bad envelope inside a batch is never an exception. POST /v1/events returns 200 when the request shape is valid, and reports each event's fate inline so one bad event never fails the batch. A result status is accepted, duplicate, or invalid; an invalid result carries an error string.

The invalid error strings come straight from the service. The common ones are an unknown type, a missing required payload field, a money field that does not parse or is negative, and an unknown or inactive platform.

Per-event results

{
  "success": true,
  "data": {
    "results": [
      { "eventId": "ok-1", "status": "accepted" },
      { "eventId": "dup-2", "status": "duplicate" },
      { "eventId": "bad-3", "status": "invalid",
        "error": "unknown event type 'foo'" },
      { "eventId": "bad-4", "status": "invalid",
        "error": "missing required payload field 'currency'" }
    ],
    "accepted": 1,
    "duplicates": 1,
    "invalid": 2
  }
}

Validation errors

When the request body itself is malformed (a missing required field, a wrong type, an out-of-range query param), the service returns 422 with a structured detail array, FastAPI's standard validation shape. Each entry names the field location and why it failed, so you can map the error straight to the offending field.

Validation error (422)

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "events", 0, "identity", "platform"],
      "msg": "Field required",
      "input": { "userId": "42" }
    }
  ]
}

Was this page helpful?