Documentation
Every API

Errors.

Auth, Mail, Waitlist, Social, Trawl and the Platform API all answer a failed request with the same JSON body, so one error handler covers all of them.


The error body

404 Not Found
{
  "status_code": 404,
  "statusCode": 404,
  "message": "Webhook endpoint not found",
  "code": "WEBHOOK_ENDPOINT_NOT_FOUND"
}
FieldTypeMeaning
status_codenumberThe HTTP status of the response, repeated in the body. Always present.
messagestring or string[]A human-readable explanation. On a validation error, one message per failing field, each naming the field as you sent it.
codestringA stable, machine-readable code. Always present. Branch on this, never on message.
detailsobject (optional)Structured context for some errors, with snake_case keys: the unknown fields you sent, the limit you hit and when to retry.
statusCodenumber (deprecated)The same value as status_code. Kept so existing clients keep working. Read status_code in new code.

Branch on code

Every error carries a code. An error specific to an operation has its own code, for example USERNAME_TAKEN, WEBHOOK_ENDPOINT_LIMIT or SERVICE_NOT_ENABLED; each API reference lists the codes an operation can return. Any other error gets the generic code for its status:

StatusGeneric code
400BAD_REQUEST, or VALIDATION_ERROR when the request body or query failed validation
401UNAUTHORIZED
403FORBIDDEN
404NOT_FOUND
409CONFLICT
413PAYLOAD_TOO_LARGE
422UNPROCESSABLE_ENTITY
429RATE_LIMITED
500INTERNAL_SERVER_ERROR
503SERVICE_UNAVAILABLE

Messages are written for people and can change. Codes do not.


Validation errors

A request that fails validation gets a 400 with code: "VALIDATION_ERROR" and one message per problem. Each message names the field the way you sent it, in snake_case. A field the operation does not accept is rejected rather than ignored.

400 Bad Request
{
  "status_code": 400,
  "statusCode": 400,
  "message": ["domain_id must be a UUID"],
  "code": "VALIDATION_ERROR"
}

statusCode is deprecated

Every other key in every ProductCraft API is snake_case. The error body used to be the exception: it carried the status as statusCode. It now carries status_code, and keeps statusCode with the same value so that nothing reading it breaks.

While the alias is sent, every error response also carries these headers (RFC 9745):

response headers
Deprecation: true
Link: <https://productcraft.co/docs/api/errors>; rel="deprecation"

There is no removal date. If statusCode is ever removed, it will be announced in the changelog well ahead of time. Read status_code (or the HTTP status itself) in new code. The official SDKs already read the HTTP status, so they need no change.