FeniciaDocs
DocumentaciónAPICambiosSoporte
Buscar⌘K
Orders APIList OrdersCreate an OrderGet an OrderSearch OrdersOrder Status TransitionsUpdate an OrderFulfillment & ShippingReturns & RefundsAttachmentsActivity Log & ExportAbandoned CartsError Catalog

Producto

  • Características
  • Precios
  • Integraciones

Recursos

  • Documentación
  • API
  • Cambios
  • Blog

Empresa

  • Sobre nosotros
  • Contacto
  • Carreras

Legal

  • Privacidad
  • Términos
FeniciaLa plataforma de e-commerce para merchants profesionales

© 2026 Hobbio Inc. Todos los derechos reservados.

Error Catalog

All Fenicia API errors follow a consistent JSON shape. This page is the canonical reference for every error code the Orders API may return.

Error response shape

{
  "code": "INVALID_API_KEY",
  "message": "Invalid or expired API key"
}

Some errors include additional fields (e.g. retryAfter on rate-limit responses, or details on validation errors).

{
  "code": "validation:invalid_format",
  "message": "Field 'customerInfo.email' has an invalid format",
  "details": { "field": "customerInfo.email", "expected": "email" }
}

Authentication errors (401)

CodeDescription
MISSING_AUTHORIZATIONThe Authorization header was not sent
INVALID_AUTHORIZATION_FORMATHeader does not follow Bearer <key> format
INVALID_API_KEYAPI key does not exist, is expired, or has been revoked
{ "code": "INVALID_API_KEY", "message": "Invalid or expired API key" }

Permission errors (403)

CodeDescription
INSUFFICIENT_PERMISSIONSThe API key lacks the required scope for this endpoint
account/billing_restrictedTenant is suspended due to billing issues
{
  "code": "INSUFFICIENT_PERMISSIONS",
  "message": "API key is missing required scope: orders:create"
}

Validation errors (400)

CodeDescription
validation:missing_fieldA required field was not provided
validation:invalid_formatField has a wrong format (email, date, UUID, etc.)
validation:invalid_valueValue not allowed (invalid enum, out of range)
{
  "code": "validation:missing_field",
  "message": "Field 'customerInfo.email' is required",
  "details": { "field": "customerInfo.email" }
}

Not found (404)

CodeDescription
order_not_foundNo order exists with the given ID for this tenant
return_not_foundNo return exists with the given ID
attachment_not_foundNo attachment exists with the given ID
{ "code": "order_not_found", "message": "Order ord_123 not found" }

Business logic errors (409)

CodeDescription
invalid_transitionCannot transition to the target status from the current one
order_already_cancelledThe order is already in cancelled state
insufficient_inventoryNot enough stock to fulfill the order
{
  "code": "invalid_transition",
  "message": "Cannot transition from 'cancelled' to 'fulfilled'"
}

Rate limit (429)

CodeDescription
RATE_LIMIT_EXCEEDEDRequest rate exceeded. Inspect retryAfter (seconds) to know when to try again
{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Too many requests",
  "retryAfter": 45
}

Advertencia

Always respect retryAfter. Retrying sooner will keep you locked out and may trigger longer cooldowns.


Server errors (5xx)

CodeStatusDescription
INTERNAL_ERROR500Generic server error. Transient, safe to retry with backoff
EXTERNAL_SERVICE_UNAVAILABLE503Downstream channel or payment provider is unavailable
{
  "code": "EXTERNAL_SERVICE_UNAVAILABLE",
  "message": "Shopify API is currently unavailable"
}

Best practices

Retry with exponential backoff

For transient errors (500, 503, network timeouts), retry with exponential backoff — doubling the delay on each attempt.

async function requestWithRetry(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch(url, options);
 
    if (res.status === 429) {
      const { retryAfter = 1 } = await res.json();
      await sleep(retryAfter * 1000);
      continue;
    }
 
    if (res.status >= 500) {
      const delay = Math.min(1000 * 2 ** attempt, 30_000);
      await sleep(delay);
      continue;
    }
 
    return res;
  }
  throw new Error("Max retries exceeded");
}
 
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

When to retry vs fail fast

StatusRetry?Strategy
400 validationNoFix the request, surface error to caller
401 authNoRotate key or re-authenticate
403 permissionNoRequest a scope upgrade
404 not foundNoThe resource does not exist
409 business logicSometimesOnly if you can mutate state to resolve conflict
429 rate limitYesWait retryAfter then retry
500 internalYesExponential backoff, up to ~5 attempts
503 external downYesLonger backoff — dependency is recovering

Idempotency

Mutating endpoints (POST, PUT, DELETE) accept an Idempotency-Key header. Use it when retrying to avoid creating duplicate resources:

curl -X POST https://api.fenicia.io/orders \
  -H "Authorization: Bearer fn_live_your_api_key" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{...}'

Tip

Generate a fresh idempotency key per logical operation, but reuse the same key across retries of that same operation. Store it alongside the operation until you see a successful response.

Log the code, not the message

Error messages may be translated or refined over time. Error codes are stable — branch on code in your integration, not on message text.

// Good
if (error.code === "insufficient_inventory") {
  await notifyMerchant(order);
}
 
// Fragile
if (error.message.includes("stock")) { ... }

Need help?

  • Authentication
  • Contact support: support@fenicia.io