Errors
The Syntext API uses conventional HTTP status codes and returns a consistent error shape on every failure.
Error Response Format
{
"error": {
"code": "not_found",
"message": "Project does not exist",
"details": { }
}
}
A stable, machine-readable error code. Match on this, not the message.
A human-readable description of what went wrong.
Optional. Additional context — e.g. field-level validation errors.
HTTP Status Codes
| Status | Meaning |
|---|---|
200 |
Success |
201 |
Resource created |
202 |
Accepted for async processing (analytics beacons) |
400 |
Bad request — malformed body or invalid parameters |
401 |
Unauthorized — missing or invalid credentials |
403 |
Forbidden — valid credentials, but the plan limit was reached or access is denied |
404 |
Not found — the resource doesn't exist or belongs to another project |
409 |
Conflict — e.g. concurrent edit conflict in the editor |
422 |
Validation failed |
429 |
Rate limited — see Rate Limits |
500 |
Internal error — safe to retry with backoff |
503 |
Service unavailable — a dependency (e.g. the AI provider) is not configured or reachable |
Error Codes
| Code | Status | Description |
|---|---|---|
bad_request |
400 | The request body or parameters are invalid |
validation |
400/422 | A field failed schema validation — check details |
unauthorized |
401 | Missing, expired, or invalid API key / token |
forbidden |
403 | You don't have access to this resource |
usage_limit_exceeded |
403 | A monthly plan limit was reached (builds, AI queries, …). The error includes current and limit fields. |
not_found |
404 | Resource doesn't exist |
conflict |
409 | The resource changed since you last read it |
rate_limited |
429 | Too many requests. The error includes retryAfterSeconds. |
internal_error |
500 | Unexpected server error |
service_unavailable |
503 | A required service is not available |
Error codes are case-insensitive — treat NOT_FOUND and not_found as equivalent when matching.
Usage Limit Errors
When an organization exceeds a plan limit, the API returns 403 with the current usage:
{
"error": {
"code": "USAGE_LIMIT_EXCEEDED",
"message": "Monthly build limit reached (100/100). Upgrade your plan for more builds.",
"current": 100,
"limit": 100
}
}
Handling Errors in the SDK
The TypeScript SDK throws typed errors you can catch by class:
import { Syntext, NotFoundError, RateLimitError } from '@syntext/sdk'
try {
await client.builds.trigger('prj_abc123')
} catch (err) {
if (err instanceof RateLimitError) {
// wait err.retryAfterSeconds and retry
} else if (err instanceof NotFoundError) {
// project doesn't exist
} else {
throw err
}
}
Retry Guidance
429/500: retry with exponential backoff. The SDKs do this automatically.4xx(other): do not retry — fix the request.- Build triggers are idempotent per commit: re-triggering the same commit creates a new build but never corrupts state.