Errors
The API uses standard HTTP status codes and a consistent JSON error envelope.
Envelope
{
"error": {
"code": "validation_error",
"message": "Request failed validation.",
"details": { "issues": [{ "path": ["recipient", "email"], "message": "Invalid email address" }] }
}
}
code— a stable machine-readable string. Switch on this in your integration.message— human-readable explanation. Safe to log; do not switch on this.details— optional structured data (e.g. Zod issues, quota info).
Status / code table
| HTTP | code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing Authorization header. |
| 401 | invalid_api_key | Key not found, malformed, or revoked. |
| 402 | quota_exceeded | Monthly certificate quota exhausted. details includes remaining, limit, resetAt. |
| 403 | forbidden | The key is valid but the account is not entitled to API access — Business plan or an active partner contract required. Also returned by POST /certificates/:id/send when email delivery is disabled for the plan. |
| 403 | insufficient_scope | Key lacks the scope this endpoint requires. |
| 404 | not_found | Resource does not exist or is not yours. |
| 405 | method_not_allowed | HTTP method not supported on this route. |
| 409 | conflict | Resource state blocks the operation. Returned by POST /certificates/:id/send when the certificate is not in the Issued state. |
| 422 | validation_error | Request body/query/path failed schema validation. details.issues lists the fields. |
| 429 | rate_limited | Too many requests. See rate limits. |
| 500 | internal_error | Something went wrong on our side. Safe to retry with backoff. |
Telling the two 403s apart
They mean different things and need different fixes, so always read code rather than the status:
insufficient_scope— the key is missing a scope. Issue a new key with the right scopes.forbidden— the key is fine; the account lost API entitlement (plan downgrade, lapsed partner contract) or the specific feature is off for that plan. No amount of key rotation fixes it.
Quota is not uniform across issue modes
POST /certificates treats quota differently by mode, and only one of them produces a 402:
- Single (
recipient) — fails with402 quota_exceededwhen the period quota is exhausted. - Batch (
recipients) — partial. It issues as many as the remaining quota allows and returns201with the rest marked failed inresults. Checkcreatedandfailed; a201does not mean every recipient succeeded.
Best practices
- Log
code, notmessage. Messages may change; codes are contractual. - Retry only idempotent failures.
500and429are retry-safe;402/403/404/409/422are not. - Surface
detailsto your users. For validation errors, thedetails.issuesarray maps directly to form fields.