Skip to content

JavaScript and TypeScript

The Stormedo JavaScript SDK supports Node.js 22 and newer and uses ESM imports. Create one client with a project API key and reuse it throughout your server application.

npm install @stormedo/sdk
TypeScript
import { Stormedo } from "@stormedo/sdk";
const token = process.env.STORMEDO_TOKEN;
if (!token) throw new Error("Set STORMEDO_TOKEN");
const stormedo = new Stormedo(token);

Keep STORMEDO_TOKEN in server-side configuration. Do not expose a project API key to browser code.

TypeScript
const request = await stormedo.send("https://your-app.example/webhooks/stormedo", {
body: {
type: "order.created",
orderId: "ord_123",
},
delay: { minutes: 5 },
maxAttempts: 5,
});
console.log(request.id, request.status);

maxAttempts counts the initial attempt.

Use rawBody when Stormedo must forward bytes unchanged.

TypeScript
await stormedo.send("https://your-app.example/events", {
rawBody: new TextEncoder().encode("event=created"),
contentType: "application/x-www-form-urlencoded",
headers: {
Authorization: "Bearer destination-token",
},
});

The headers you provide are sent to the destination. Stormedo never forwards your project API key.

Submit multiple independent requests in one API call:

TypeScript
const requests = await stormedo.sendBatch([
{
url: "https://your-app.example/webhooks/orders",
body: { type: "order.created", orderId: "ord_123" },
},
{
url: "https://your-app.example/webhooks/inventory",
body: { type: "inventory.sync", warehouseId: "wh_7" },
delay: { hours: 1 },
},
]);
console.log(requests.map((request) => request.id));

delay and timeout accept duration objects with days, hours, minutes, and seconds. deliverAt accepts a Date.

listRequests supports status, search, method, source, time range, cursor, and limit filters:

TypeScript
const page = await stormedo.listRequests({
status: "failed",
method: "POST",
limit: 25,
});
console.log(page.items);
if (page.nextCursor) {
const nextPage = await stormedo.listRequests({
status: "failed",
method: "POST",
limit: 25,
cursor: page.nextCursor,
});
console.log(nextPage.items);
}

Load request detail and completed attempts independently:

TypeScript
const detail = await stormedo.getRequest(request.id);
const attempts = await stormedo.listRequestAttempts(request.id);
console.log(detail.status, attempts.length);
TypeScript
const cancellation = await stormedo.cancelRequest(request.id);
console.log(cancellation.status);

Cancellation preserves history. cancellation_requested acknowledges the signal; it is not a final request status. An in-flight attempt can still finish.

Separately, replay a request that you have confirmed is succeeded, failed, or cancelled:

TypeScript
const completedRequestId = "req_33uZSQsVaf8aZjDuzkuAq";
const replay = await stormedo.replayRequest(completedRequestId, {
idempotencyKey: "order-123-replay-001",
});
console.log(replay.id);

Replace the illustrative ID with your completed request’s ID. Do not cancel and immediately replay without checking state; replay of active work returns 409. Replay has a new request ID, so use a stable payload event ID if repeating the business effect must be prevented.

Create schedules with cron or fixed-interval timing:

TypeScript
import { cronTiming, intervalTiming } from "@stormedo/sdk";
const schedule = await stormedo.createSchedule({
name: "sync-inventory",
timing: cronTiming("0 */6 * * *", "Asia/Kolkata"),
url: "https://your-app.example/inventory/sync",
body: { fullSync: true },
maxAttempts: 4,
});
await stormedo.updateSchedule(schedule.id, {
timing: intervalTiming({ hours: 4 }),
});
const schedules = await stormedo.listSchedules();
const definition = await stormedo.getSchedule(schedule.id);
console.log(schedules.length, definition.status);
await stormedo.pauseSchedule(schedule.id);
await stormedo.resumeSchedule(schedule.id);
await stormedo.deleteSchedule(schedule.id);

cronTiming accepts a five-field cron expression and an IANA timezone. intervalTiming accepts days, hours, and minutes; the combined duration must be a whole number of minutes from one minute through 30 days. Pass a future Date as startsAt to choose the first run time.

API and network failures throw StormedoError. Use code for program logic and requestId when contacting Stormedo support.

TypeScript
import { StormedoError } from "@stormedo/sdk";
try {
await stormedo.getRequest("req_33uZSQsVaf8aZjDuzkuAq");
} catch (error) {
if (error instanceof StormedoError) {
console.error(error.code, error.status, error.requestId, error.details);
}
throw error;
}

send(url, options) returns an EnqueuedRequest. JSON sends use /requests; raw sends use /send.

Option Type and behavior
body Any JSON value. Omission sends no body; null sends JSON null.
rawBody string or Uint8Array, including Buffer. Mutually exclusive with body and metadata.
contentType String, up to 255 bytes, for raw sends only.
method GET, POST, PUT, PATCH, or DELETE; default POST.
headers Destination string-to-string map; at most 64 entries and 32 KiB combined.
delay Duration object, whole seconds from 1 second through 30 days.
deliverAt Valid Date, within 30 days of server time; cannot be combined with delay. Allowed past times run immediately.
timeout Duration object, whole seconds from 1 through 300; default 50 seconds per destination attempt.
maxAttempts Total attempts, 1 through 20. Default 3 for JSON sends and batch items, 1 for raw sends and schedules.
queue Label, 1 to 64 letters, numbers, dots, hyphens, or underscores; default default. No FIFO or concurrency guarantee.
metadata JSON sends only. Up to 32 string entries; 64-byte keys, 1024-byte values, 16 KiB combined. Not forwarded.
idempotencyKey Stable business-operation key, 1 to 255 bytes; generated per call when omitted.
signal AbortSignal for the API call. Aborting cannot undo work already accepted.

Duration objects may combine non-negative integer days, hours, minutes, and seconds; the total must satisfy the option’s bounds. Each delivered body is limited to 256 KiB. See reserved headers.

SDK results unwrap the API’s data and use camelCase. Read request.id, not request.data.id. Dates in results are RFC 3339 strings, except verifier results, which use Date.

Operation Result and extra options
send EnqueuedRequest: id, projectId, replayedFromRequestId, status, scheduledAt, createdAt.
sendBatch(items, options) Read-only array of enqueued requests in input order. 1 to 100 JSON items; optional per-item keys. options accepts batch idempotencyKey and signal.
listRequests(options) items, nextCursor, historyRetentionDays, historyCutoffAt, hasHiddenHistory. Filters include from and to as RFC 3339 strings. Default limit 50, maximum 100.
getRequest(id, options) Request definition and completed attempts. Body omitted; header values redacted.
listRequestAttempts(id, options) Read-only array of completed attempts. Empty until the first attempt completes.
cancelRequest(id, options) id and status, either cancellation_requested or cancelled.
replayRequest(id, options) New enqueued request. Options accept idempotencyKey and signal.
createSchedule(input, options) Schedule summary. Options accept idempotencyKey and signal.
listSchedules(options) Read-only array of schedule summaries.
getSchedule(id, options) Schedule detail with request replacing top-level url and maxAttempts.
updateSchedule(id, input, options) Updated schedule summary.
pauseSchedule(id, options), resumeSchedule(id, options) Updated schedule summary.
deleteSchedule(id, options) undefined, from an HTTP 204.

All operations accept signal in their options. Full result fields are described in API objects with snake-case HTTP names. For example, attempt response_status_code becomes responseStatusCode and retry.next_attempt_at becomes retry.nextAttemptAt.

A schedule input accepts name, timing, url, JSON body, headers, and maxAttempts. Occurrences use POST. In updateSchedule, supply destination changes inside request, with the full desired URL/body/attempt budget. Omitted headers preserve existing values; {} clears them. Omitted or null schedule bodies send no body, unlike explicit null in ordinary JSON sends. Never write [REDACTED] header values back into a schedule.

The default API base URL is https://api.stormedo.com. The optional constructor setting baseUrl supports controlled test endpoints. Browser use is rejected because the client holds a secret API key. Node.js is the supported runtime; using Bun as an installer does not imply a Bun runtime compatibility guarantee.

The SDK retries an interrupted transport call once for sends, batches, reads, cancel, replay, schedule creation, pause, and resume. It preserves generated or supplied idempotency keys. Schedule updates and deletions are not automatically retried. HTTP error responses, including 5xx, are not automatically retried. Explicit aborts are not retried.

There is no SDK-level API timeout by default. Use signal: AbortSignal.timeout(30_000) to bound the publisher call. This is separate from timeout, which controls each outbound delivery attempt. A timeout or abort can still leave acceptance unknown; recover with the original key and input.

API failures throw StormedoError; status and requestId may be null for transport failures. Transport codes include network_error and request_aborted. Invalid local input can throw TypeError. Log only the fields needed for investigation, propagate failures, and follow submission recovery.

Read Delivery verification before processing signed requests at your destination.