Error Codes
Every API failure returns a structured body with an appropriate HTTP status. The code and message are nested under a top-level error key:
{ "error": { "code": "CONFLICT", "message": "schedule already fired" } }
The SDK unwraps this and surfaces the same information as a SendItError:
import { SendItError } from "@sendithq/sdk";
try {
await sendit.reschedule("sch_123", { in: "1h" });
} catch (err) {
if (err instanceof SendItError) {
err.code; // e.g. "CONFLICT"
err.status; // e.g. 409 — undefined for network failures
}
}
Codes
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
VALIDATION |
400 | Input failed validation — bad fireAt, oversized payload, malformed URL, reserved header. |
Fix the request. The message names the problem. |
UNAUTHORIZED |
401 | Missing, malformed, revoked key, or deactivated account. | Check the key; re-issue if needed. |
FORBIDDEN |
403 | Allowed to authenticate but not to do this — e.g. monthly schedule quota exhausted (past the 10% grace), blocked target, or a session-only action attempted with an API key. | Upgrade plan, fix the target, or use session login. |
NOT_FOUND |
404 | No such schedule/resource, or it isn't yours. | Verify the id. |
CONFLICT |
409 | State conflict — e.g. rescheduling a schedule that already fired. | Re-read current state before acting. |
RATE_LIMITED |
429 | Too many requests from this key or IP. | Back off and retry; the SDK retries idempotent calls automatically. |
INTERNAL |
500 | Server-side error. | Retry later; details are intentionally not exposed. |
NETWORK |
— | The request never reached us (DNS, connection, timeout). status is undefined. |
Retry; idempotent calls and keyed schedules are retried by the SDK. |
How the SDK retries
The SDK retries automatically, but only where it's safe:
- Idempotent calls —
list,get,getAttempts,cancel, and anyschedule/scheduleManycarrying idempotency keys — are retried onNETWORKfailures and5x(INTERNAL) responses, up tomaxRetrieswith exponential backoff. - Non-idempotent calls —
reschedule,replay,clone, and aschedulewithout anidempotencyKey— are not retried, to avoid firing twice. A failure throws immediately.
Add an idempotencyKey to make schedule/scheduleMany safely retriable. See Scheduling.
Common cases
VALIDATIONonfireAt— the time is in the past or missing a timezone offset. Use an explicit offset (Zor+09:00).VALIDATIONon payload — it exceeds your plan's size cap. See Plans & Limits.FORBIDDENon create — you've used your full monthly schedule quota plus the 10% grace, or the target resolves to a blocked address (see Security). CheckGET /v1/usageto see where you stand.CONFLICTonreschedule— the schedule already fired or was cancelled; it's no longerscheduled.