Runtime

How to receive webhooks for sandbox events

Call runtime.webhooks.create({ url, events }); Runtime POSTs each event to your HTTPS URL, signed, retrying for about three days.

On Runtime you hear about a sandbox that stopped, failed to start or could not wake the moment it happens, with no polling loop to run. Thirteen event types cover sandboxes, snapshots and volumes. Every delivery is signed with HMAC-SHA256, retried ten times over about three days when your endpoint is down, and logged with your endpoint's status and timing. Webhooks cost nothing extra (metrics and webhooks).

Create a webhook

TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();const hook = await runtime.webhooks.create({  url: "https://example.com/hooks/runtime",  events: ["sandbox.stopped", "sandbox.start_failed", "sandbox.wake_failed"],});console.log(hook.id, hook.secret); // the secret is shown once: store it nowconst answer = await runtime.webhooks.test(hook.id); // a signed webhook.test, sent nowconsole.log(answer.lastStatus);
Pythonfrom withruntime import Runtimeruntime = Runtime()hook = runtime.webhooks.create(    url="https://example.com/hooks/runtime",    events=["sandbox.stopped", "sandbox.start_failed", "sandbox.wake_failed"],)print(hook["id"], hook["secret"])  # shown onceprint(runtime.webhooks.test(hook["id"]))
Terminalnpx withruntime webhooks create https://example.com/hooks/runtime --events sandbox.stopped,sandbox.start_failednpx withruntime webhooks test <id>

Leave events out to receive every type. A webhook belongs to the account, so every key and every owner sees the same ones, and owners can manage them on the Webhooks page. An account can have 16.

The events

Type Sent when
sandbox.created A sandbox is made and starting
sandbox.running It is ready
sandbox.paused It paused, keeping its memory and files
sandbox.woken A paused sandbox runs again
sandbox.stopped It stopped; stopReason says why
sandbox.start_failed It stopped before it ever ran; sandbox.stopped follows
sandbox.wake_failed A wake did not complete
snapshot.ready, snapshot.failed, snapshot.deleted A snapshot's lifecycle
volume.ready, volume.failed, volume.deleted A volume's lifecycle

Each body is the event with the resource as it was: its id, type, createdAt and data.sandbox (or data.snapshot, data.volume) with the state, previous state, labels, size, region and funding. Labels you set at create come back here, so a handler can route an event to the job that owns the sandbox.

Write the endpoint

Check the signature, act, and answer 204 quickly. Heavy work belongs in a queue after the answer:

TypeScriptimport { verifyWebhook } from "withruntime";const seen = new Set<string>(); // use your database in productionexport async function POST(request: Request) {  let event;  try {    event = await verifyWebhook(      await request.text(),      request.headers.get("runtime-signature"),      process.env.RUNTIME_WEBHOOK_SECRET!,    );  } catch {    return new Response("bad signature", { status: 400 });  }  const id = request.headers.get("runtime-webhook-id") ?? event.id;  if (seen.has(id)) return new Response(null, { status: 204 }); // a retry of one already handled  seen.add(id);  if (event.type === "sandbox.stopped") {    const sandbox = event.data.sandbox as { id: string; stopReason?: string };    console.log("stopped", sandbox.id, sandbox.stopReason);  }  return new Response(null, { status: 204 });}

How the signature check works, and how to do it without the SDK, is on verify a webhook signature.

Answers, retries and the log

Rule What happens
Success Any 2xx within 10 seconds
Failure Any other status, a timeout or a redirect
Retries After 1 min, 5 min, 15 min, 1 h, 3 h, 6 h, 12 h, 24 h and 24 h: ten attempts in about three days
Same event, retried Same Runtime-Webhook-Id header on every attempt
Order Not guaranteed; order by createdAt
Delivery log Kept 14 days, with attempts, your status and duration
Where it may go HTTPS on the public internet; private, loopback and link-local addresses are refused
Terminalnpx withruntime webhooks deliveries <id>npx withruntime webhooks retry <deliveryId>npx withruntime webhooks update <id> --disable

runtime.webhooks.deliveries(id) and runtime.webhooks.retry(deliveryId) do the same from code, and the webhook shows failingSince while its deliveries fail. An agent manages webhooks with the runtime_webhooks_manage MCP tool.

Mistakes and how Runtime handles them

  • Parsing JSON before checking the signature. The signature covers the raw body. Read it as text first, verify, then trust it.
  • Doing slow work before answering. An answer after 10 seconds counts as a failure and is sent again. Answer first, then work.
  • Handling a retry twice. A retry carries the same Runtime-Webhook-Id; skip one you have already handled.
  • An endpoint on a private address. A URL naming a private address is refused when the webhook is made, and each delivery resolves the name again and connects only to the public address it checked.
  • Missing events while the endpoint was down. Nothing is lost for 14 days: runtime events (or runtime.events.list()) lists every lifecycle event newest first, for the account or one sandbox.

Start

Terminalnpx withruntime webhooks create https://example.com/hooks/runtime

The first command prints a link to approve in your browser; new accounts get 50 free sandbox hours, no card.

Facts on this page were checked on 25 September 2026.