All API errors follow a consistent JSON format with a human-readable message, a machine-readable code, and optional details.
{
"error": "Tenant not found",
"code": "TENANT_NOT_FOUND",
"details": {},
"hint": "Check that the tenant_id is correct"
}
| Field |
Type |
Description |
| error |
string |
Human-readable error message |
| code |
string |
Machine-readable error code (see table below) |
| details |
object |
Additional context (validation errors, field-level info) |
| hint |
string |
Suggested action to resolve the error |
Error codes
Authentication
| Code |
HTTP |
Description |
| AUTH_REQUIRED |
401 |
No authentication token provided |
| AUTH_INVALID |
401 |
Token is invalid, expired, or revoked |
| FORBIDDEN |
403 |
Authenticated but insufficient permissions |
| MFA_REQUIRED |
403 |
Login requires a second factor |
| MFA_INVALID_CODE |
401 |
TOTP or recovery code is incorrect |
| MFA_NOT_ENABLED |
400 |
MFA action requires MFA to be enabled first |
| MFA_ALREADY_ENABLED |
400 |
MFA is already active on this account |
| OAUTH_FAILED |
400 |
GitHub OAuth exchange or profile fetch failed |
| OAUTH_STATE_INVALID |
400 |
OAuth state parameter mismatch |
| EMAIL_NOT_VERIFIED |
403 |
Action requires a verified email address |
| VERIFICATION_TOKEN_INVALID |
400 |
Email verification token is invalid |
| VERIFICATION_TOKEN_EXPIRED |
400 |
Email verification token has expired |
| RESET_TOKEN_INVALID |
400 |
Password reset token is invalid or already used |
| RESET_TOKEN_EXPIRED |
400 |
Password reset token has expired |
Resources
| Code |
HTTP |
Description |
| TENANT_NOT_FOUND |
404 |
Organization does not exist |
| FUNCTION_NOT_FOUND |
404 |
Function does not exist |
| BINDING_NOT_FOUND |
404 |
Binding does not exist |
| VERSION_NOT_FOUND |
404 |
Function version does not exist |
| CUSTOM_DOMAIN_NOT_FOUND |
404 |
Custom domain does not exist |
| NOT_FOUND |
404 |
Generic resource not found |
| CONFLICT |
409 |
Resource already exists or conflicts with another |
Validation
| Code |
HTTP |
Description |
| VALIDATION_ERROR |
400 |
Request body or parameters failed validation |
| CODE_INVALID |
400 |
Deployed code has syntax errors or security issues |
| BINDING_NOT_ALLOWED |
400 |
Binding type is not permitted for this organization |
Operations
| Code |
HTTP |
Description |
| DEPLOY_FAILED |
500 |
Deploy operation failed |
| ROLLBACK_FAILED |
500 |
Rollback operation failed |
| DISPATCH_FAILED |
502 |
Function dispatch failed |
| REVIEW_FAILED |
500 |
Automated code review failed |
| CUSTOM_DOMAIN_ALREADY_EXISTS |
409 |
Domain is already registered |
| CUSTOM_DOMAIN_API_ERROR |
502 |
External API error during domain operation |
Limits
| Code |
HTTP |
Description |
| RATE_LIMITED |
429 |
Too many requests — wait and retry |
| LIMIT_EXCEEDED |
429 |
Generic limit exceeded (API calls, storage, etc.) |
| STORAGE_LIMIT_EXCEEDED |
429 |
Storage quota exceeded for this organization |
| TEAM_LIMIT_EXCEEDED |
429 |
Maximum team members reached for this plan |
| PRO_PLAN_REQUIRED |
403 |
Feature requires a Pro plan upgrade |
Server
| Code |
HTTP |
Description |
| INTERNAL_ERROR |
500 |
Unexpected server error |
Handling errors
Check the code field for programmatic error handling:
const res = await fetch("https://api.fold.run/deploy", { ... });
if (!res.ok) {
const body = await res.json();
switch (body.code) {
case "RATE_LIMITED":
// Wait and retry
break;
case "VALIDATION_ERROR":
// Fix request body — check body.details for field-level errors
break;
case "AUTH_REQUIRED":
// Re-authenticate
break;
default:
console.error(body.error, body.hint);
}
}
Validation errors
When code is VALIDATION_ERROR, the details field contains field-level errors:
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"details": {
"url": ["Expected string, received number"],
"event": ["Invalid enum value"]
}
}
Rate limiting
When you receive RATE_LIMITED, the response includes a Retry-After header with the number of seconds to wait before retrying.
Rate limits are configured per-organization. Free plans default to 100 requests per minute. Pro plans support up to 10,000 requests per minute. See billing for plan details.