/// REFERENCE

What ASHDOCS API error codes should I handle?

Every error returns JSON with detail.code and detail.message. Full catalog of codes and remediation steps.

Published: Last updated:

Answer: Every ASHDOCS error is JSON. Endpoint errors return {"detail": {"code", "message", and sometimes "hint"}}; transport-level errors from the rate limiter return {"success": false, "error": {"code", "message"}}. The code field is stable — branch on it, not on message text.

The two envelopes

Endpoint validation and business errors (the vast majority) come from the API handler and look like the first example. Rate limiting happens in middleware before the handler and uses the second envelope. Both carry the same machine-readable code idea.

// endpoint error (400/402/403/404/410/413/500)
{ "detail": { "code": "insufficient_credits",
              "message": "You need 3 credits but have 1 remaining...",
              "hint": "Upgrade or top up at /pricing." } }

// rate limiter (429)
{ "success": false, "error": { "code": "rate_limited", "message": "Rate limit exceeded" } }

Retry guidance

Retryable: rate_limited and concurrency_limit_reached (back off using the Retry-After header), and tool_failed (a transient conversion failure — retry once; failed jobs are never charged, so a retry never double-bills). Everything else is a request problem: fix the payload instead of retrying. insufficient_credits clears when your credits reset or you top up.

Triggering errors on purpose

Three quick reproductions you can run against a sandbox key: send unknown-tool to get 404 unknown_tool; send an 11 MB file on the Free plan to get 413 file_too_large; fire 11 requests inside a minute on Free to get 429 rate_limited with Retry-After set.

curl -s -X POST https://www.ashdocs.com/api/v1/tools/not-a-tool \
  -H "X-API-Key: ash_test_xxxxxxxx" -H "Content-Type: application/json" -d '{}'
# -> 404 { "detail": { "code": "unknown_tool", ... } }

Error code catalog

Derived from the backend source; HTTP status first, then the stable code.

HTTPcodeWhat it meansRetryable?
400invalid_options / bad_optionsThe options JSON is malformed or has wrong typesNo — fix the request
400missing_field / schema_required / url_required / file_requiredA required input wasn't sentNo
400no_files / too_many_filesZero files, or more than the tool acceptsNo
400unsupported_file_typeThe file's type isn't accepted by this toolNo
400bad_file_url / file_url_is_webpage / file_url_fetch_failedfile_url didn't resolve to a downloadable documentOnly fetch failures
400virus_detectedThe upload failed the malware scanNo
401— (detail is the string "Invalid API key")Missing, wrong or revoked keyNo — rotate the key
402insufficient_creditsNot enough credits for this job (checked before running; the job never starts)After top-up / reset
403ssrf_blockedThe URL points at a private or metadata IPNo
403batch_not_available / webhooks_not_availableThat feature starts on a higher planNo — upgrade
404unknown_tool / document_not_found / template_not_foundThe slug or ID doesn't exist (or isn't yours)No
410document_expired / link_expiredThe signed URL or stored document passed its expiryNo — re-run the job
413file_too_largeThe file exceeds your plan's per-file cap (10/25/50/100/200 MB)No — or upgrade
429rate_limitedPlan requests/minute exceeded — see Retry-AfterYes, with backoff
429concurrency_limit_reachedYour plan's concurrent-jobs cap is in useYes, when a job finishes
500tool_failedThe conversion itself failed; the job is marked failed and never chargedOnce

Frequently asked questions

Am I charged for failed jobs?

Never. Credits are only consumed when a job completes successfully — 4xx validation failures don't even start a job, and 500 tool_failed jobs are finalized without any charge.

Is detail.code stable enough to branch on?

Yes — codes are treated as API contract. Messages and hints are for humans and may be reworded.