Skip to content

Getting Started

Errors

The error envelope, status codes, and error codes the Core API returns.

Every error from the Core API uses one consistent JSON envelope, so a single error handler covers the whole API:

Error envelope
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation error",
    "details": [
      { "field": "phoneNumber", "message": "phoneNumber must be E.164-formatted (e.g. +15555550123)" }
    ]
  },
  "metadata": {
    "timestamp": "2026-07-30T12:00:00.000Z",
    "version": "1.0",
    "path": "/assistants"
  }
}
FieldTypeDescription
successbooleanAlways false for errors
error.codestringMachine-readable error code — branch on this, not on message
error.messagestringHuman-readable explanation
error.detailsarray | objectOptional extra context. For validation failures it is an array of { field, message } objects, one per invalid field
metadata.timestampstringISO 8601 time the error was produced
metadata.versionstringAPI version
metadata.pathstringThe request path

Successful responses use the same envelope with "success": true and a data field instead of error.

Status codes and error codes#

StatusCodeWhen
400VALIDATION_ERRORThe body or query parameters failed validation — see error.details for the offending fields
400PARSEThe request body is not valid JSON
401UNAUTHORIZEDMissing, malformed, or invalid Authorization header — see Authentication
403FORBIDDENThe key is valid but not allowed to act on this resource (e.g. the tenant does not belong to your organization)
404RESOURCE_NOT_FOUNDThe route exists but the resource does not (or belongs to another tenant)
404NOT_FOUNDThe route itself does not exist
409CONFLICTThe request conflicts with current state. Some endpoints return more specific 409 codes (e.g. WHATSAPP_ONBOARDING_IN_PROGRESS) — always branch on error.code
500INTERNAL_SERVER_ERRORSomething went wrong on OnCore's side. Internals are never leaked — the message is generic

Codes can be more specific

error.code is the stable contract. Individual endpoints may return endpoint-specific codes on the same status (for example the WhatsApp onboarding endpoints return codes like WHATSAPP_RESEND_NOT_ALLOWED), so match codes rather than status numbers where the docs call them out.

Deliberate exceptions to the envelope#

Two embed-facing endpoints intentionally do not use the envelope for their 404 responses:

When a phone number is unknown, belongs to another tenant, or has been deleted, they all return the same fixed body:

404 response (anti-enumeration)
{ "error": "not_found" }

The body is byte-identical across all three cases on purpose: these endpoints face end-user embeds, and a distinguishable response would let someone probe which phone numbers exist on the platform. Their success responses are also bare JSON (no envelope) — see the channel guides for the exact shapes.

Handling errors#

Example error handling
const res = await fetch("https://core-api.heysadie.ai/assistants", {
  headers: { Authorization: `ApiKey ${process.env.SADIE_API_KEY}` },
});

const body = await res.json();

if (!body.success) {
  switch (body.error.code) {
    case "UNAUTHORIZED":
      // rotate / fix the key
      break;
    case "VALIDATION_ERROR":
      // body.error.details -> [{ field, message }]
      break;
    default:
      // log body.error.code + body.error.message
  }
}