Skip to content

Error Reference

Every error response includes a machine-readable code field. This page documents each code with common causes and recommended fixes.

Authentication Errors

INVALID_API_KEY

HTTP Status401 Unauthorized
MeaningThe API key is malformed, not found, or was never created

Common causes:

  • Typo in the key — check for extra spaces or truncated characters
  • Using a key from a different environment
  • Key was deleted from the dashboard

Fix:

  1. Verify the key format: xqr_pk_{8-char-prefix}.{64-char-secret}
  2. Regenerate the key from Settings → Developers
  3. Ensure you’re not wrapping the key in quotes in your header
// Correct
headers: { Authorization: "Bearer xqr_pk_a1b2c3d4.xxxx..." }
// Wrong — double Bearer prefix
headers: { Authorization: "Bearer Bearer xqr_pk_..." }
// Wrong — missing Bearer prefix
headers: { Authorization: "xqr_pk_..." }

REVOKED_API_KEY

HTTP Status401 Unauthorized
MeaningThe API key was explicitly revoked by a workspace admin

Common causes:

  • A team member rotated or revoked the key
  • Key was revoked as part of a security incident response

Fix:

  1. Go to Settings → Developers and create a new key
  2. If you rotated the key, use the new key that was generated during rotation
  3. Update all services using the old key

EXPIRED_API_KEY

HTTP Status401 Unauthorized
MeaningThe key passed its expires_at date

Fix:

  1. Create a new key from the dashboard
  2. Consider using non-expiring keys for production services
  3. Set up key rotation reminders before expiry

Authorization Errors

INSUFFICIENT_SCOPE

HTTP Status403 Forbidden
MeaningThe API key is valid but lacks the scope required by this endpoint

Example: Calling POST /v1/links with a key that only has links:read.

Fix:

  1. Check which scope the endpoint requires (listed at the top of each endpoint’s docs)
  2. Create a new key with the required scope, or use a key with * (full access)

Scope reference:

Endpoint patternRequired scope
GET /v1/links*links:read
POST/PATCH/DELETE /v1/links*links:write
*/qr (image render)qr:render
GET /v1/qr/templates*qr:read
POST/PATCH/DELETE /v1/qr/templates*qr:write
GET /v1/analytics*analytics:read
GET /v1/assets*assets:read
POST/DELETE /v1/assets*assets:write
GET /v1/bio*bio:read
POST/PATCH/DELETE /v1/bio*bio:write
GET /v1/workspace*workspace:read
GET /v1/webhooks*webhooks:read
POST/PATCH/DELETE /v1/webhooks*webhooks:write

FEATURE_NOT_AVAILABLE

HTTP Status403 Forbidden
MeaningYour plan does not include this feature

Common causes:

  • Free plan users trying to access the API (API requires Pro+)
  • Pro plan users trying to use Enterprise-only features (e.g., advanced routing rules)

Fix:

  1. Upgrade your plan at Settings → Billing
  2. Check the plan comparison to see which features are available

PLAN_LIMIT_EXCEEDED

HTTP Status403 Forbidden
MeaningYou’ve reached your plan’s resource limit

Examples:

  • Pro plan: max 500 links, trying to create link #501
  • Business plan: max 10 API keys, trying to create key #11

Fix:

  1. Delete unused resources to free up capacity
  2. Check your usage via GET /v1/workspace/usage
  3. Upgrade your plan for higher limits

Validation Errors

VALIDATION_ERROR

HTTP Status400 Bad Request
MeaningThe request body or parameters are invalid

Common causes:

  • Missing required fields
  • Wrong field types (e.g., string instead of array)
  • Values outside allowed range (e.g., size: 100 when max is 50)
  • Invalid URL format

Response includes details:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "url: Invalid URL format. Must start with http:// or https://",
"status": 400
}
}

Fix: Read the message field — it indicates exactly which field failed and why.


SLUG_TAKEN

HTTP Status409 Conflict
MeaningThe custom_slug is already used by another link in your workspace

Fix:

  1. Choose a different slug
  2. Check existing slugs with GET /v1/links?search=your-slug
  3. Delete the existing link if it’s no longer needed

Rate Limiting Errors

RATE_LIMIT_EXCEEDED

HTTP Status429 Too Many Requests
MeaningYour monthly API quota has been exhausted

Fix:

  1. Wait for your billing cycle to reset
  2. Check remaining quota: GET /v1/workspace/usage
  3. Upgrade your plan for higher monthly limits
  4. Optimize your integration to make fewer calls (use bulk endpoints, cache responses)

BURST_LIMIT_EXCEEDED

HTTP Status429 Too Many Requests
MeaningToo many requests per second (token bucket exhausted)

Fix:

  1. Read the Retry-After header and wait that many seconds
  2. Implement exponential backoff:
async function callWithRetry(fn: () => Promise<Response>, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fn();
if (res.status !== 429) return res;
const retryAfter = parseInt(res.headers.get("Retry-After") || "1");
const backoff = retryAfter * 1000 * Math.pow(2, attempt);
await new Promise((r) => setTimeout(r, backoff));
}
throw new Error("Max retries exceeded");
}

Resource Errors

RESOURCE_NOT_FOUND

HTTP Status404 Not Found
MeaningThe resource does not exist or belongs to another workspace

Common causes:

  • Typo in the resource UUID
  • Resource was deleted
  • Trying to access another workspace’s resource

Fix:

  1. Verify the resource ID
  2. List resources to confirm it exists (e.g., GET /v1/links)
  3. Ensure you’re using the correct API key (keys are workspace-scoped)

Server Errors

INTERNAL_ERROR

HTTP Status500 Internal Server Error
MeaningAn unexpected server error occurred

Fix:

  1. Retry with exponential backoff (transient errors usually resolve quickly)
  2. If persistent, check status.xqr.co for outages
  3. Contact support with the request_id from meta.request_id

Was this page helpful?