Skip to main content

Error Format

All error responses follow a consistent structure:
{
  "data": null,
  "errors": [
    {
      "message": "Missing required field: name",
      "layer": "product"
    }
  ]
}

Reading the Errors Array

The errors array is ordered from root cause to top-level caller:
  • errors[0] — the root cause of the failure (most useful for debugging)
  • errors[n] — the top-level caller that surfaced the error
In most cases, you only need to inspect errors[0].message for the actionable error description.

HTTP Status Codes

CodeMeaningDescription
200SuccessRequest processed successfully
400Bad RequestInvalid parameters or malformed request body
401UnauthorizedMissing or invalid authentication credentials
403ForbiddenValid credentials but insufficient permissions
404Not FoundRequested resource does not exist
409ConflictIdempotent request is already being processed
429Rate LimitedToo many requests in a short period
500Server ErrorInternal server error
501Not ImplementedRequested feature is not yet available
502Bad GatewayUpstream service error

Error Layers

Each error includes a layer field indicating where in the system the error originated:
LayerDescriptionCommon Causes
gatewayRequest routingInvalid URL, server unavailable
userAuthentication / authorizationExpired token, wrong role
storeStore operationsMissing store, permission denied
productProduct operationsInvalid prices, missing fields
orderOrder operationsInvalid product version, checkout errors
graphqlGraphQL queriesInvalid query, unknown fields

Common Error Scenarios

Authentication Errors (401)

{
  "data": null,
  "errors": [
    {
      "message": "Token has expired",
      "layer": "user"
    }
  ]
}
Solutions:
  • Verify your Merchant ID and private key are correct
  • Ensure the SDK is initialized with valid credentials
  • Check that your private key matches the registered public key

Validation Errors (400)

{
  "data": null,
  "errors": [
    {
      "message": "Store name must be between 1 and 100 characters",
      "layer": "store"
    }
  ]
}
Solutions:
  • Check required fields are present
  • Verify field value formats and constraints
  • Ensure amounts are in smallest currency units (integers)

Permission Errors (403)

{
  "data": null,
  "errors": [
    {
      "message": "Only store owner can delete store",
      "layer": "store"
    }
  ]
}
Solutions:
  • Verify the API Key has the required permissions
  • Ensure the user belongs to the correct store

Idempotency Conflicts (409)

{
  "data": null,
  "errors": [
    {
      "message": "Request with this idempotency key is already being processed",
      "layer": "gateway"
    }
  ]
}
Solutions:
  • Wait for the original request to complete
  • Use a different X-Idempotency-Key for a new request

Handling Errors in Code

TypeScript SDK

import { WaffoPancake, WaffoPancakeError } from "@waffo/pancake-ts";

const client = new WaffoPancake({
  merchantId: process.env.WAFFO_MERCHANT_ID!,
  privateKey: process.env.WAFFO_PRIVATE_KEY!,
});

try {
  const { store } = await client.stores.create({ name: "My Store" });
} catch (err) {
  if (err instanceof WaffoPancakeError) {
    const rootCause = err.errors[0];
    console.error(`Error [${rootCause.layer}]: ${rootCause.message}`);
    // err.status contains the HTTP status code
  }
}

Retry Strategy

Retry these status codes with exponential backoff:
  • 429 — Rate limited (respect Retry-After header if present)
  • 500 — Internal server error
  • 502 — Bad gateway
async function retryRequest(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const status = error.status;
      if (![429, 500, 502].includes(status) || attempt === maxRetries - 1) {
        throw error;
      }
      const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
Do not retry 400, 401, 403, or 404 errors. These indicate issues that must be fixed in the request itself.

Idempotency for Safe Retries

Use the X-Idempotency-Key header to safely retry write operations without creating duplicates:
curl -X POST https://waffo-pancake-auth-service.vercel.app/v1/actions/checkout/create-session \
  -H "Content-Type: application/json" \
  -H "X-Store-Slug: your-store-slug" \
  -H "X-Environment: test" \
  -H "X-Idempotency-Key: order-abc-123" \
  -d '{"productId": "...", "productType": "onetime", "currency": "USD"}'
  • Idempotency keys are cached for 24 hours
  • Sending the same key returns the cached response
  • If the original request is still processing, returns 409 Conflict