Retry temporary failures with backoff, stop on permanent ones, and report anything a person must fix as a Pipe17 Exception so it shows up in the app.
What each error means
| HTTP code | Meaning | What to do |
|---|---|---|
| 400 | Request failed validation | Do not retry unchanged. Fix the data or raise an Exception |
| 401 | Key missing or not recognized | Stop the connector and alert. Check which key you used |
| 403 | Key lacks permission, or the record belongs to another organization | Check the flow is enabled and the access scope |
| 404 | Record not found (or soft-deleted) | Remove the stale mapping; do not retry |
| 409 | Duplicate key | Treat as already done, then look the record up |
| 412 | Concurrent inventory update | Resend the same request unchanged |
| 429 | Rate limited | Retry with backoff |
| 500, 502, 503 | Server or downstream failure | Retry with backoff |
Batch endpoints (POST /fulfillments, POST /inventory) return HTTP 202 with a status per item. Always check each item; a 202 can contain failures.
Retry with backoff
const RETRYABLE = new Set([412, 429, 500, 502, 503]);
export async function withRetry(fn, { attempts = 5, baseMs = 1000 } = {}) {
for (let i = 1; ; i++) {
try {
return await fn();
} catch (err) {
const retryable = RETRYABLE.has(err.status) || err.status === undefined; // undefined = network error
if (!retryable || i >= attempts) throw err;
const delay = baseMs * 2 ** (i - 1) * (0.5 + Math.random()); // exponential + jitter
await new Promise((r) => setTimeout(r, delay));
}
}
}
// usage: await withRetry(() => p17('POST', '/inventory', { body: rows }));In-process retries cover short blips. Anything still failing goes to the retry_queue table (Chapter 4), and a scheduled job retries it with longer waits (for example 5 minutes, 30 minutes, 2 hours). After the last attempt, raise an Exception and stop retrying.
Report problems as Pipe17 Exceptions
When a record cannot be processed without human help (unknown SKU, invalid address, a rejection from your system), create an Exception on it. Users see it in the app next to the record.
export async function reportException(entityType, entityId, details) {
await p17('POST', '/exceptions', {
body: { exceptionType: 'eSyncFailure', entityType, entityId, exceptionDetails: details },
});
}curl -s -X POST "$PIPE17_BASE_URL/exceptions" \
-H "X-Pipe17-Key: $PIPE17_INTEGRATION_KEY" \
-H "Content-Type: application/json" \
-d '{"exceptionType":"eSyncFailure","entityType":"shipments","entityId":"<shipmentId>","exceptionDetails":"SKU TSHIRT-BLK-XL is not set up in the warehouse"}'Only one active Exception exists per integration, record and exception type, so repeating the call does not create duplicates. When the problem is fixed, set it to resolved with PUT /exceptions/{exceptionId}. The API reference lists more specific types (for example eInvalidAddress, eOutOfStock) to use where they fit.
# Find the active exceptions on a record, then resolve one
curl -s -G "$PIPE17_BASE_URL/exceptions" \
-H "X-Pipe17-Key: $PIPE17_INTEGRATION_KEY" \
--data-urlencode "entityId=<shipmentId>" \
--data-urlencode "status=active"
curl -s -X PUT "$PIPE17_BASE_URL/exceptions/<exceptionId>" \
-H "X-Pipe17-Key: $PIPE17_INTEGRATION_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "resolved"}'Logging and monitoring
- Log every API call with the method, path, HTTP code, Pipe17 record ID and your ID. Never log API keys or full addresses.
- Track, per job, the last successful run time, the records processed and the number failed. Alert when a job has not succeeded for three intervals.
- Alert when the retry queue grows, or when any item reaches its last attempt.
Need Help?
If you need additional assistance:
- Use Ask Pippen, our AI agent, located at the top of the app page.
- Submit a support request with as much relevant detail as possible. Learn how to submit a request.
We're here to help you succeed with your operations.
Comments
0 comments