All docs

REST API Reference

The SDK is a thin wrapper over this HTTP API. Use it directly from any language.

  • Base URL: https://api.sendit-whenever.com
  • Auth: Authorization: Bearer sw_live_… (or a session cookie for dashboard actions). See Authentication.
  • Content type: application/json for request bodies.

All endpoints are under /v1.

Schedules

Method Path Description
POST /v1/schedules Create one schedule.
POST /v1/schedules/bulk Create a batch of schedules, up to your plan's max batch size (partial success).
GET /v1/schedules List schedules. Query: status, q, limit, offset.
GET /v1/schedules/:id Get one schedule.
GET /v1/schedules/:id/attempts Delivery-attempt log for one schedule (within the plan retention window).
PATCH /v1/schedules/:id Reschedule (only while scheduled).
DELETE /v1/schedules/:id Cancel.
POST /v1/schedules/:id/replay Re-send the same payload as a new schedule.
POST /v1/schedules/:id/clone Re-send with a new payload.

POST /v1/schedules

POST /v1/schedules
Authorization: Bearer sw_live_xxx
Content-Type: application/json
Field Type Required Notes
targetUrl string yes HTTPS only, port 80/443, no embedded credentials. Subject to SSRF checks.
fireAt string yes ISO-8601 with timezone offset. Must be in the future.
payload string no UTF-8 string. Encrypted at rest. Size capped per plan.
method enum no GET/POST/PUT/PATCH/DELETE. Default POST.
headers object no Custom headers. Host, Content-Length, X-SendIt-Signature are reserved.
idempotencyKey string no 1–255 chars. Re-using a key returns the existing schedule.
earlyFire boolean no Opt in to Early Firing for this schedule. Defaults to your account setting (off unless changed).

Over REST, payload is a string. To send JSON, JSON-encode your object into the string and set a Content-Type: application/json entry in headers. (The SDK does this for you when you pass an object.)

Returns 201 with { id, fire_at, status, over_limit } for a new schedule (over_limit is true while you're in the 10% grace over your monthly quota). Re-using an idempotencyKey returns the existing schedule with 200.

The schedule object

GET /v1/schedules/:id, GET /v1/schedules items, PATCH, and DELETE return the full schedule:

{
  "id": "sch_…",
  "target_url": "https://api.example.com/hook",
  "method": "POST",
  "status": "succeeded",
  "fire_at": "2026-06-17T09:00:00.000Z",
  "fired_at": "2026-06-17T09:00:00.180Z",
  "offset_ms": 180,
  "early_fire": false,
  "attempts": 1,
  "payload_size_bytes": 42,
  "has_payload": true,
  "created_at": "2026-06-17T08:00:00.000Z",
  "updated_at": "2026-06-17T09:00:00.180Z"
}
  • fired_at / offset_ms are the measured punctuality of the first delivery attempt — null until the schedule fires. offset_ms = fired_at − fire_at (positive = late, negative = early-fire only). See Punctuality.
  • The payload plaintext is never returned — only has_payload and payload_size_bytes.

POST /v1/schedules/bulk

Create a batch of schedules in one call, up to your plan's max batch size (Free 100, Indie 500 — see Plans & Limits). Each item uses the same fields as POST /v1/schedules. The call succeeds partially — valid items are created, invalid ones are reported — and returns 200 with:

{
  "accepted": [{ "index": 0, "id": "sch_…", "fire_at": "…", "status": "scheduled" }],
  "rejected": [{ "index": 1, "code": "VALIDATION", "message": "fireAt must be in the future" }]
}

index is the item's position in your request array. Duplicate idempotencyKeys within one batch are rejected as CONFLICT.

Filtering GET /v1/schedules

?status=scheduled&q=billing&limit=50&offset=0{ items, total, limit, offset }.

Delivery log

Method Path Description
GET /v1/logs Delivery attempts across all your schedules, newest first.

A unified, time-ordered feed of every delivery attempt (one row per attempt, including retries) — joined with its schedule's target and method.

?outcome=failed&q=billing&limit=50&offset=0{ items, total, limit, offset, retention_days }.

  • outcomesucceeded (HTTP 2xx) or failed (non-2xx, including network/blocked attempts with no status code).
  • Only attempts within your plan's log retention window are returned (retention_days: Free 7, Indie 30). Older attempts are filtered out — the same window also applies to GET /v1/schedules/:id/attempts.
  • Each row: schedule_id, target_url, method, status_code, latency_ms, error_text, attempted_at. error_text is a capped snippet of the target's response — never your payload.

Overview & usage

Method Path Description
GET /v1/overview Dashboard summary for your account. Accepts API key or session.
GET /v1/usage Current month's schedule usage against your plan quota.
GET /v1/status/punctuality Public, no auth — anonymized service-wide punctuality.

GET /v1/overview?period=24h|7d|30d (default 24h) returns active count, success rate, the punctuality offset distribution, recent volume, a bucketed time series, and an attention summary — all clamped to your plan's retention window so it never disagrees with the delivery log:

{
  "period": "24h",
  "active_schedules": 12,
  "success_rate": 0.991,
  "offset_ms": { "p50": 140, "p95": 380, "min": 12, "max": 910 },
  "recent_volume": 1840,
  "series": [{ "t": "2026-06-17T08:00:00.000Z", "count": 80, "p50": 130, "p95": 360 }],
  "needs_attention": { "failed": 2, "due_soon": 5, "dead": 0 }
}

GET /v1/usage returns your monthly schedule count against the quota:

{ "period": "202606", "plan": "free", "scheduled": 1840, "limit": 2000, "grace_limit": 2200, "over_limit": false }

GET /v1/status/punctuality is public and anonymized — no account, target, or payload data — and is short-TTL cached:

{ "window": "24h", "sent": 5231, "on_time_rate": 0.987, "offset_ms": { "p50": 150, "p95": 410 }, "updated_at": "2026-06-17T09:00:00.000Z" }

offset_ms here floors early-fire offsets at zero, so an intentional early send never flatters the average; per-schedule values from GET /v1/schedules/:id remain the raw measured truth.

Webhook signing

Method Path Description
GET /v1/signing-secrets Get current and next secrets.
POST /v1/signing-secrets/rotate Promote next to current, generate a new next.

Account & keys (session-only)

Method Path Description
GET /v1/me Profile + plan. Accepts API key or session.
PATCH /v1/me Update display name.
DELETE /v1/me Delete the account.
GET /v1/api-keys List keys (never the raw value).
POST /v1/api-keys Create a key (raw value shown once, 201).
POST /v1/api-keys/:id/rotate Issue a replacement key and revoke the old one (new raw value shown once, 201).
POST /v1/api-keys/:id/revoke Revoke a key (204).

Relay settings (session-only)

Per-account delivery knobs — a downthrottled concurrency rate (CPS) and custom retry behaviour, each capped by your plan. See Reliability.

Method Path Description
GET /v1/me/relay-settings Current settings (your saved values; null = inherit plan default), effective (what actually applies after clamping/inheritance), limits, and defaults.
PATCH /v1/me/relay-settings Update cpsLimit, retryAttempts, retryBackoffMs, retryStrategy. Omit a field to leave it unchanged; send null to clear it back to the plan default. Values are clamped to your plan's limits.

Auth & billing (session)

Method Path Description
POST /v1/auth/magic/start Request a magic link (202).
GET /v1/auth/magic/verify Verify a magic link, start a session.
GET /v1/auth/oauth/:provider/start Begin OAuth (provider = google or github); redirects to the provider.
GET /v1/auth/oauth/:provider/callback OAuth callback; starts a session on success.
POST /v1/auth/logout End the session (204).
POST /v1/billing/checkout Get an upgrade checkout URL.
POST /v1/billing/portal Get a subscription-management URL.

Health

Method Path Description
GET /healthz Liveness.
GET /readyz Readiness (database + queue).
GET /metricsz Operational metrics (worker heartbeat, queue depth).

Responses & errors

Successful reads and writes return JSON objects mirroring the SDK types. Errors return a structured body — { error: { code, message } } — with an appropriate HTTP status. Note the code/message are nested under error. See Error Codes.