Skip to main content

Errors

Endpoint-level errors from the Smart EPD v2 API use a consistent envelope, so you can handle most failures with one code path. When an endpoint rejects a request or hits a business-rule failure, the non-2xx response has this shape:

{
"error": {
"code": "validation_error",
"message": "Name is required.",
"details": [
{ "field": "name", "message": "This field is required." }
]
}
}
KeyTypeMeaning
error.codestringStable, machine-readable error code. Branch on this, not on message.
error.messagestringHuman-readable explanation. Safe to surface to end users.
error.detailsarray | omittedOptional field-level breakdown (most common on validation_error). Each item is an object such as { "field": "...", "message": "..." }. Omitted when there is nothing field-specific to report.
tip

error.code is the contract; error.message wording may change between releases. Always switch on error.code.

Common error codes​

The codes below are the ones the API emits today. Treat the list as the common set rather than an exhaustive enumeration — handle unknown codes by falling back to the HTTP status.

HTTP statuserror.codeWhen it happens
400 Bad Requestvalidation_errorAn endpoint rejected the request on a business/validation rule (raised via make_error). Check details.
400 Bad Requestinvalid_file_typeAn uploaded file's content type is not accepted for that endpoint.
403 ForbiddenforbiddenYou are authenticated but lack the scope/role for this resource or action.
404 Not Foundnot_foundThe resource does not exist or you don't have access to it. (Access failures return 404, not 403, so resource IDs can't be probed.)
409 ConflictconflictThe operation conflicts with the resource's current state (e.g. an in-flight task already exists).
413 Payload Too Largefile_too_largeAn uploaded file exceeds the endpoint's size limit.
429 Too Many Requestsrate_limitedA throttled endpoint (the email/login auth endpoints, invite lookups, and the public EPD library) received too many requests.
500 Internal Server Errorinternal_errorAn unexpected server-side error. The message is generic by design; check server logs / Sentry.

EPD submission codes​

POST /epds/{epd_id}/submit/ returns the 400 codes below. Resubmitting a flagged EPD can return the last three. Their details items are objects specific to each code, not {field, message} pairs:

error.codeWhen it happensdetails items
shape_validation_errorThe document is missing required fields or has values of the wrong type.The blocking document violations.
tool_expiredThe project's EPD tool verification has expired.{tool_name, expires_on}
reference_inputs_missingA reference EPD whose template enforces its constraints lacks a value for an external input field that is required or needed by a constraint. Cannot be acknowledged.{field_id, label}
constraint_failuresA reference EPD's values fail error-severity constraints of an enforced template. Repeat the call with ?acknowledge_constraint_failures=true to submit anyway.The failing constraint rulings.
model_lock_driftA variant EPD's model (EPD tool or version, PCR spec version, or template definition) differs from its reference EPD's locked model, and the variant_model_lock_block feature flag is on.{coordinate, label, locked, actual}

See Submitting a reference EPD and Variant conformance.

note

A 404 is returned both when a resource genuinely doesn't exist and when it exists but you can't see it. This is intentional — it prevents callers from discovering the existence of resources they aren't authorized to access. Don't treat 404 as proof that an ID is unused.

note

Framework-level errors look different. Failures raised by the web framework before a request reaches endpoint logic — request body/query schema validation (HTTP 422) and authentication failures (HTTP 401) — currently return the framework's default body, not the error envelope above. Branch on the HTTP status for those cases. (Normalizing them onto the envelope is tracked as a separate follow-up.)

Handling errors​

resp=$(curl -s -w '\n%{http_code}' \
-H "Authorization: Bearer $TOKEN" \
"https://sandbox.smartepd.xyz/api/v2/projects/$PROJECT_ID/epds/")

status=$(printf '%s' "$resp" | tail -n1)
body=$(printf '%s' "$resp" | sed '$d')

if [ "$status" -ge 400 ]; then
code=$(printf '%s' "$body" | jq -r '.error.code')
echo "Request failed ($status): $code"
printf '%s' "$body" | jq '.error.details // empty'
fi

A robust client should:

  1. Branch on error.code for known cases.
  2. Fall back to the HTTP status class for unknown codes (4xx → caller error, 5xx → retry with backoff).
  3. Surface error.message to users only where it's safe; log the full envelope for debugging.