Handle API errors well
Every non-2xx response uses the same envelope — { error, code, ... } — and the code set is closed and stable. That means you can write a single, small handler that covers every case, instead of chasing string messages. Here's the strategy.
Branch on code, never on the message or status alone
The human error text can change; two 404s (not_found vs flow_not_published) mean different things. Switch on code:
async function runFlow(flowUuid: string, body: unknown) {
const res = await fetch(`https://api.zerowidth.ai/1.0/flows/${flowUuid}/runs`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ZW_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
})
if (res.ok) return res.json()
const err = await res.json() // { error, code, ... }
switch (err.code) {
case "rate_limited":
// Wait and retry — see below.
case "plan_limit":
case "plan_gate":
// Billing problem: surface an upgrade / add-credit prompt.
case "auth_invalid":
case "scope_missing":
// Credential problem: don't retry, fix the key.
default:
throw new Error(`${err.code}: ${err.error}`)
}
}
Retry the retryable — and only those
5xx(internal_error,service_unavailable) — transient. Retry with exponential backoff.429 rate_limited— you're going too fast. Honor theRetry-Afterheader, then retry.504 timeout— the run took too long. Retrying as-is will likely time out again; raisetimeoutMsor simplify the work first.
Everything else — validation, not_found, auth_invalid, scope_missing, plan_limit — is a client-side problem. Retrying won't help; fix the request, the credential, or the plan.
Handle the money cases gracefully
402 plan_limit and plan_gate mean the workspace hit a spend or plan ceiling. In a user-facing product, catch these and show an "add credit" or "upgrade" path rather than a generic error; the response names the limit that tripped.
Don't leak the raw error to end users
The error message is written for a developer reading logs. In a customer-facing surface, map the code to your own copy — not_found → "That flow doesn't exist," plan_limit → "You're out of credit" — rather than surfacing "API 402: ..." framing.
The full error reference lists every code and status.