Errors and Limits
Interpret validation errors, plan limits, permission failures, rate limits, and retry-safe operational responses.
https://api.filemorf.com/api/public
Bearer API key from a Developer or Enterprise workspace.
Signed uploads, queued jobs, retained artifacts, refreshable URLs.
Status handling
Map HTTP status codes to concrete operator actions
The public API uses standard status codes with small JSON error envelopes. Most failures are deterministic and should be handled explicitly.
| Status | Typical error | Operator action |
|---|---|---|
| 400 | Validation failed / Invalid file type / Invalid state | Fix the request payload, content type, or lifecycle ordering before retrying. |
| 401 | Unauthorized | Use a valid non-revoked Bearer API key. |
| 403 | Forbidden / Feature not available | Confirm the API key permissions and that the workspace is on a plan with API access (Developer or Enterprise). |
| 404 | Job not found / Workflow recipe not found / Workflow run not found | Check ownership, source type, and the requested resource id. |
| 409 | Idempotency conflict / job_not_startable | Do not reuse the same Idempotency-Key with a different payload, and do not start a failed or still-uploading job. |
| 413 | File too large / Batch too large | Reduce file size or batch size, or use a plan with larger limits. |
| 429 | rate_limited / quota_exceeded | Retry rate_limited responses after Retry-After. Do not automatically retry quota_exceeded; wait for resetsAt or change the plan/workload. |
| 500 | Failed to refresh URL / internal processing failure | Retry if safe, or inspect job state and webhook delivery logs. |
Rate limits
Distinguish retryable rate limits from plan quotas
Short request-window rate limits and monthly or daily plan quotas both use HTTP 429, so branch on the stable code rather than English prose.
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | The request quota window size for the current API key. |
| X-RateLimit-Remaining | Remaining requests in the current window. |
| X-RateLimit-Reset | Unix timestamp when the window resets. |
| Retry-After | Seconds to wait before retrying a code: rate_limited response. |
{
"error": "Rate limit exceeded",
"code": "rate_limited",
"retryAfter": 30
}{
"error": "Operation limit reached",
"message": "A plan-specific summary may appear here.",
"code": "quota_exceeded",
"quotaKey": "operations",
"resetsAt": "2026-08-01T00:00:00.000Z"
}Failure contract
Branch on stable codes, never English messages
The legacy error field remains for compatibility but is generic. Treat failure.code as the programmatic contract and retain failure.diagnosticId when contacting support.
{
"status": "failed",
"error": "Processing failed",
"failure": {
"code": "processing_failed",
"diagnosticId": "8d61b0d8-77ef-4bbf-9f86-c2ce00b8c699"
}
}- processing_failed is the current terminal job failure code.
- diagnosticId identifies the failed job without exposing provider responses, stack traces, paths, or document-derived content.
- Unknown codes should use a generic local message while preserving the diagnostic id for support.
- Do not parse, display, or make retry decisions from error or message prose.
Troubleshooting
Common causes of false negatives
Most integration failures come from lifecycle ordering, missing uploads, or invalid plan assumptions.
- Do not call POST /jobs/:id/start before every required upload has succeeded.
- If a start response is lost, retry the same start request or poll the existing job id; start is idempotent.
- Use the exact content type that the selected job type accepts.
- For recipe runs, pass a retained artifact or a prior job output index that actually exists.
- Refresh signed download URLs instead of assuming an older URL remains valid indefinitely.