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
| Code | Meaning | Description |
|---|
| 200 | Success | Request processed successfully |
| 400 | Bad Request | Invalid parameters or malformed request body |
| 401 | Unauthorized | Missing or invalid authentication credentials |
| 403 | Forbidden | Valid credentials but insufficient permissions |
| 404 | Not Found | Requested resource does not exist |
| 409 | Conflict | Idempotent request is already being processed |
| 429 | Rate Limited | Too many requests in a short period |
| 500 | Server Error | Internal server error |
| 501 | Not Implemented | Requested feature is not yet available |
| 502 | Bad Gateway | Upstream service error |
Error Layers
Each error includes a layer field indicating where in the system the error originated:
| Layer | Description | Common Causes |
|---|
gateway | Request routing | Invalid URL, server unavailable |
user | Authentication / authorization | Expired token, wrong role |
store | Store operations | Missing store, permission denied |
product | Product operations | Invalid prices, missing fields |
order | Order operations | Invalid product version, checkout errors |
graphql | GraphQL queries | Invalid 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