Agent-readable docs index: /docs/llms.txt. Full docs in one file: /docs/llms-full.txt. Download /docs/docs.zip to grep all markdown files locally.

Standard Error Codes & HTTP Statuses

Shaf REST API responses use standard HTTP status codes combined with application-level error codes in the JSON response body. Every error response follows this envelope:
{ "error": { "code": 40001, "constant": "INVALID_URL", "message": "The destination URL is malformed or missing a valid protocol.", "requestId": "req_aB3cD4eF5g" } }
The requestId field can be included in support tickets to help diagnose specific failures.

Error Code Reference

4xx Client Errors

HTTP StatusCodeConstantDescription & Resolution
400 Bad Request40001INVALID_URLThe destination URL is malformed or lacks a valid protocol (http:// or https://). Validate the URL format before submitting.
400 Bad Request40002INVALID_SLUGThe slug contains invalid characters or exceeds 2,048 bytes. Slugs must match /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.
400 Bad Request40003RESERVED_SLUGThe requested slug is reserved for system use (e.g. api, auth, dashboard, docs). Choose a different slug.
400 Bad Request40004INVALID_EXPIRYThe expiration timestamp is in the past or uses an invalid ISO 8601 format.
401 Unauthorized40101UNAUTHORIZEDMissing or invalid Authorization: Bearer <token> header. Verify your API key is current and correctly formatted.
403 Forbidden40301FORBIDDENThe API key does not have write permission. Use a Full Access key for create/update/delete operations.
403 Forbidden40302QUOTA_EXCEEDEDThe organization has exceeded its plan's link limit or custom domain allocation. Upgrade or wait for billing cycle renewal.
404 Not Found40401LINK_NOT_FOUNDThe requested slug does not exist on the specified domain.
404 Not Found40402DOMAIN_NOT_FOUNDThe specified custom domain is not registered in this workspace.
409 Conflict40901SLUG_EXISTSA short link with this slug already exists on the chosen domain. Use PATCH /links/:slug to update it, or choose a different slug.
429 Too Many Requests42901RATE_LIMITEDRequest velocity exceeds the plan's rate limit window. Implement exponential backoff with jitter.

5xx Server Errors

HTTP StatusCodeConstantDescription & Resolution
500 Internal Server Error50001INTERNAL_ERRORAn unexpected edge error occurred. Retry with exponential backoff. If persistent, check status.shaf.app.
503 Service Unavailable50301EDGE_UNAVAILABLEThe edge worker encountered a transient fault. Retry after a short delay.

Handling Rate Limits

When you receive a 429 RATE_LIMITED response, the response includes a Retry-After header indicating how many seconds to wait:
HTTP/2 429 retry-after: 15 x-ratelimit-limit: 120 x-ratelimit-remaining: 0 x-ratelimit-reset: 1727390415
Implement exponential backoff with jitter to avoid thundering herd issues:
async function requestWithRetry(fn: () => Promise<Response>, maxRetries = 3): Promise<Response> { for (let attempt = 0; attempt <= maxRetries; attempt++) { const response = await fn() if (response.status === 429) { const retryAfter = Number(response.headers.get('retry-after') ?? '5') const jitter = Math.random() * 1000 await new Promise(resolve => setTimeout(resolve, (retryAfter * 1000) + jitter)) continue } return response } throw new Error('Max retries exceeded') }

Troubleshooting Common Errors

40001 INVALID_URL — my URL looks correct
Ensure the URL includes the full protocol (https://). URLs without protocols (e.g. example.com/path) are rejected. Also check for invisible Unicode characters that can be inadvertently pasted from some text editors or CMS systems.
40101 INVALID_TOKEN or TOKEN_EXPIRED with a newly generated key
Verify the key has not expired by checking Settings → API Keys in the console. Confirm there are no trailing spaces or newline characters in the token value — these are common issues when reading keys from environment variables with cat or shell substitution.
40901 SLUG_CONFLICT — how to update an existing link instead of failing
Use PATCH /v1/links/{slug} with only the fields you wish to change. The POST /v1/links endpoint is for creating new links only. See the Links API Reference for the update schema.