Integrations · Webhooks

New videos, sent to your own server.

CapSend sends a signed JSON POST to your URL when a video arrives. Send it on to your asset manager, an editing queue, a spreadsheet, or anything else that takes a request.

POST https://example.com/hooks/capsend

x-capsend-signature: t=1792843200,v1=887372fa20f16861d0b3401e7452652855de42f1e1a3bb5542371b8c8c9ad3ff

x-capsend-event: submission.received

{
  "data": {
    "duration_ms": 94200,
    "quality_merged": true,
    "recipient_name": "Priya Shah",
    "request_title": "Customer stories, Q4",
    "submission_id": "3f6c1d2a-8b7e-4c55-9a10-2e4f7b9c0d18",
    "video_id": "a71e0c94-52d3-4b8f-9e66-0b1c3d5e7f20",
    "warnings": []
  },
  "event": "submission.received",
  "image_url": "https://capsend.app/m/p/SFMyNTY…",
  "sent_at": "2026-10-24T12:00:00Z",
  "text": "Customer stories, Q4",
  "title": "Priya Shah sent a video",
  "url": "https://capsend.app/o/acme/submissions/3f6c1d2a-8b7e-4c55-9a10-2e4f7b9c0d18"
}
An example request, pretty-printed. CapSend sends the body compact, and the signature covers those exact bytes. The person and IDs are made up.

What gets posted

CapSend posts to your URL when something needs your team’s attention, not for every click. Each post links straight to the video or request in CapSend.

  • A new video arrives With a thumbnail and any quality warnings.
  • Everyone has sent a video
  • A video has quality warnings When the checks finish after the video arrived.
  • Post for every request, or pick the ones this integration covers.
  • Private requests are never posted, wherever they’d otherwise go.
  • Pause and resume without losing the setup, and send a test to check it.
  • Failed posts are retried, and the last error shows in Settings.

Check that it came from CapSend

Every request carries an x-capsend-signature header in the form t=<unix time>,v1=<signature>. The signature is a hex HMAC-SHA256 of the timestamp, a period, and the raw body, keyed with your webhook's signing secret.

  • Sign the body exactly as it arrived, before parsing the JSON.
  • Reject old timestamps, so a captured request can't be replayed.
  • Route on the x-capsend-event header or the event field.
  • Answer with any 2xx status. Other responses are retried a few times.
import crypto from "node:crypto";

// rawBody: the request body exactly as received.
// header: the x-capsend-signature header.
export function verify(rawBody, header, secret) {
  if (typeof header !== "string") return false;
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!Number.isInteger(t) || !/^[0-9a-f]{64}$/.test(parts.v1 ?? "")) return false;
  if (Math.abs(Date.now() / 1000 - t) > 300) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest();

  return crypto.timingSafeEqual(expected, Buffer.from(parts.v1, "hex"));
}
Node.js. Any language with HMAC-SHA256 works the same way.

Set it up

Any HTTPS endpoint that accepts a JSON POST will do. You need to be an owner of your CapSend organization.

  1. 1

    In CapSend, open Settings → Integrations and choose Webhook.

  2. 2

    Paste your endpoint's URL, which must start with https://, and click Connect. CapSend sends an integration.test event right away.

  3. 3

    Copy the signing secret that now shows with the webhook, and store it on your server.

  4. 4

    Once your server checks signatures, click Send test to send another integration.test event, signed like the rest.

Other integrations

All integrations

Hear about it when they hit send.

Webhooks are on the Team plan. Start with a request, then connect your endpoint.