SDK Reference
The @sendithq/sdk package (Node 18+, zero runtime dependencies). Every wire field is normalized to camelCase for you.
import { SendIt, SendItError } from "@sendithq/sdk";
Prefer Python? The
sendithqpackage mirrors this surface (sync + async). Method names aresnake_caseand the relative-time argument isin_. Jump to the Python SDK section.
Constructor
new SendIt(apiKey: string, options?: SendItOptions)
apiKey must look like sw_live_… or sw_test_…; an empty or malformed key throws a SendItError synchronously.
SendItOptions
| Option | Type | Default | Description |
|---|---|---|---|
baseUrl |
string |
https://api.sendit-whenever.com |
API endpoint. |
timeoutMs |
number |
30000 |
Per-request timeout. |
maxRetries |
number |
2 |
Extra retries on idempotent paths. |
signingSecret |
string |
— | Default secret for verifySignature. |
Methods
schedule(input) → ScheduleRef
Create one schedule. Retried automatically only when idempotencyKey is set.
const ref = await sendit.schedule({
url: "https://api.myapp.com/hook",
in: "2h", // or fireAt: string | Date
payload: { userId: 42 }, // object → JSON, string → verbatim
method: "POST", // default POST
headers: { "X-Tenant": "acme" },
idempotencyKey: "trial-end:42",
earlyFire: true, // opt in to Early Firing (default: account setting)
});
// ref = { id, fireAt, status }
earlyFire is optional — omit it to use your account default (off unless changed). See Early Firing.
scheduleMany(items) → BulkScheduleResult
Create a batch of schedules in one call, up to your plan's max batch size. Partial success: each item succeeds or fails independently.
const { accepted, rejected } = await sendit.scheduleMany([
{ url, in: "1h", payload: a, idempotencyKey: "k1" },
{ url, in: "2h", payload: b, idempotencyKey: "k2" },
]);
// accepted: { index, id, fireAt, status }[]
// rejected: { index, code, message }[]
The batch is retried only if every item carries an idempotencyKey. Duplicate keys within one batch are rejected.
list(options?) → ListResult
List schedules with optional filtering and pagination. Idempotent — retried automatically.
const { items, total, limit, offset } = await sendit.list({
status: "scheduled", // scheduled | firing | succeeded | failed | cancelled | dead
q: "billing", // substring match on target URL
limit: 50,
offset: 0,
});
get(id) → Schedule
Fetch one schedule. Idempotent.
getAttempts(id) → DeliveryAttempt[]
Fetch the delivery-attempt log for a schedule. Idempotent. See Reliability.
reschedule(id, input) → Schedule
Move a schedule's fire time. Only valid while scheduled; otherwise 409 CONFLICT. Not retried.
await sendit.reschedule("sch_123", { in: "3h" }); // or { fireAt }
await sendit.reschedule("sch_123", { in: "3h", earlyFire: true }); // also toggle Early Firing
cancel(id) → Schedule
Cancel a schedule before it fires. Idempotent.
replay(id) → ScheduleRef
Re-send the same encrypted payload as a brand-new schedule. Not retried (non-idempotent).
await sendit.replay("sch_123"); // fire now
clone(id, input) → ScheduleRef
Create a new schedule from an existing one with a new payload. Not retried.
await sendit.clone("sch_123", { payload: { v: 2 }, in: "1d" }); // optional: earlyFire
verifySignature(req, opts?) → boolean
Verify an inbound webhook signature. See Verifying Webhooks.
const ok = sendit.verifySignature(
{ headers, rawBody }, // rawBody must be the raw bytes
{ secret: process.env.SENDIT_SIGNING_SECRET }, // optional if set in constructor
);
signingSecrets.get() / signingSecrets.rotate() → SigningSecretPair
Read or rotate your signing secrets. Both return { current, next }.
const { current, next } = await sendit.signingSecrets.get();
await sendit.signingSecrets.rotate();
Types
ScheduleRef — returned by schedule, reschedule, cancel, replay, clone:
{ id: string; fireAt: string; status: ScheduleStatus }
Schedule — returned by get, list items:
{
id: string;
targetUrl: string;
method: HttpMethod;
status: ScheduleStatus;
fireAt: string;
firedAt: string | null; // actual first-fire time; null until it fires
offsetMs: number | null; // firedAt − fireAt (ms); + late, − early-fire only; null until fired
earlyFire: boolean; // whether Early Firing was enabled
attempts: number;
payloadSizeBytes: number;
hasPayload: boolean; // payload plaintext is never returned
createdAt: string;
updatedAt: string;
}
firedAt / offsetMs are the measured punctuality of the schedule — see Punctuality. These are raw per-schedule values; the public status numbers floor early-fire offsets at zero.
DeliveryAttempt:
{
attemptNo: number;
statusCode: number | null;
latencyMs: number | null;
errorText: string | null; // snippet of the target's response, never your payload
attemptedAt: string;
}
ScheduleStatus = scheduled | firing | succeeded | failed | cancelled | dead.
HttpMethod = GET | POST | PUT | PATCH | DELETE.
Errors
Every failure throws a SendItError with a code and optional status. See Error Codes.
import { SendItError } from "@sendithq/sdk";
try {
await sendit.get("missing");
} catch (err) {
if (err instanceof SendItError && err.code === "NOT_FOUND") {
// handle
}
}
Python SDK
The sendithq package (Python 3.9+, single dependency httpx) mirrors this surface with both a synchronous SendIt and an asynchronous AsyncSendIt.
pip install sendithq
from sendithq import SendIt, SendItError
sendit = SendIt("sw_live_xxx")
ref = sendit.schedule(
url="https://api.myapp.com/hook",
in_="2h", # 'in' is reserved in Python → in_
payload={"user_id": 42}, # dict → JSON, str → verbatim
idempotency_key="trial-end:42",
)
The async client has the identical surface; every method is a coroutine:
from sendithq import AsyncSendIt
async with AsyncSendIt("sw_live_xxx") as sendit:
ref = await sendit.schedule(url="https://api.myapp.com/hook", in_="2h")
Naming map (Node → Python)
Node (@sendithq/sdk) |
Python (sendithq) |
|---|---|
new SendIt(key, opts) |
SendIt(key, **opts) — baseUrl→base_url, timeoutMs→timeout (seconds), maxRetries→max_retries, signingSecret→signing_secret |
schedule({ url, in, fireAt, idempotencyKey, earlyFire }) |
schedule(url=, in_=, fire_at=, idempotency_key=, early_fire=) |
scheduleMany(items) |
schedule_many(items) — items is a list of dicts with the same keys |
list, get, reschedule, replay, clone, cancel |
same names; reschedule(id, in_=...) |
getAttempts(id) |
get_attempts(id) |
verifySignature(req, opts) |
verify_signature(body, headers, secrets=[...]) |
signingSecrets.get()/rotate() |
signing_secrets.get()/rotate() |
SendItError (err.code) |
SendItError (err.code) |
Response objects are frozen dataclasses with snake_case fields (schedule.target_url, schedule.fire_at, schedule.fired_at, schedule.offset_ms, schedule.early_fire, attempt.status_code, …). Errors raise SendItError with the same code set. Signature verification details are in Verifying Webhooks.