Handed over by webhook
Reelwire posts nothing itself. A channel with Webhook switched on hands each post over to an address you choose, as one signed request, and your own system takes it from there, with nobody in between. This page is the part every such system shares: setting it up, what arrives, how to trust it, and how to tell Reelwire what happened. What your system then does with each post is one of three things:
| Guide | When it fits |
|---|---|
| Post through the platforms' APIs | Your own script posts each channel through the platform's own developer API: Instagram, TikTok, YouTube, Facebook, LinkedIn and the rest. |
| Let an AI session post it | Your own script starts an AI assistant session for each post, which works a browser and posts it for you, unattended. |
| Post through a posting service's API | Your script or an automation (Make, n8n, Zapier) passes each post to a posting service's API, which posts it to the platforms. |
Reelwire does not ship or run any of them. What follows is the shape every receiver has, and the contract itself is on Webhooks.
Set it up
- On the Webhook screen (under Connections), set the address your receiver listens on. It must be reachable from the internet over https. Keep the signing secret somewhere your receiver can read it: it is shown once, when it is made or rotated, and it is all your receiver needs.
- Press Test delivery. Your receiver gets a signed request with
"event": "ping", and the screen shows what it answered. Use it until the signature check passes. - On each channel whose posts should come to you, switch Webhook on under When a post is due (Channels). Leave Email on as well if a person should also get each post.
What arrives
When a post is due, one request per hand-over: a post going to three channels that need no approval
arrives once, with all three in channels; a channel that needs approval arrives on its own once it
is approved. Each channel carries its video (and its cover picture), its text as that platform takes
it, and the ids to report back with. Channels of the same post left out of this request are named in
omitted, with the reason and whether they follow.
Later, the same address receives post.update when somebody changes a post that was handed over,
and post.delete when somebody asks for it to be taken down. The x-reelwire-event header says which.
Trust it
- Check the signature on the raw body, before parsing it:
x-reelwire-signatureist=<unix seconds>,v1=<hex>, andv1is the HMAC-SHA256 of<t>.<raw body>with your signing secret. Compare in constant time. - Refuse a stale timestamp: more than 300 seconds away from your clock is a replay.
- Act on a delivery once:
x-reelwire-deliveryis the same on every retry of one delivery. Record it only after you have finished with it, so a failure halfway lets the retry through. - Verify the video after downloading it against its
sha256. The links work for seven days from each send.
In Node, the check is a few lines:
import { createHmac, timingSafeEqual } from "node:crypto";
export const fromReelwire = (rawBody, header, secret) => {
const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return parts.v1?.length === expected.length && timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
};
Answer, then work
Answer with any 2xx straight away, and post afterwards. The answer only says the request arrived: it publishes nothing, and one that takes longer than 30 seconds counts as a failure and is sent again. A request that fails is retried after 10 seconds, a minute, five minutes, a quarter of an hour, an hour and two hours, and then given up; the Webhook screen's delivery log shows every attempt, and Deliver again sends one once more.
Say what happened
Reelwire shows a channel's post as Handed over until somebody says what became of it. Your receiver says it, per channel, with an API key that holds Posts write:
POST https://app.reelwire.io/v1/posts/{postId}/report
Authorization: Bearer rw_...
Content-Type: application/json
{ "status": "published", "url": "https://www.youtube.com/shorts/...", "platformPostId": "..." }
postId is channels[].postId from the request. The status is one of:
| Status | When |
|---|---|
published | It is up. Send its url, and the platform's own id if you have one: both come back to you in a later post.update or post.delete, so you can find the post again. |
skipped | You decided not to post it. |
failed | It could not be posted. Send a reason: the Posts screen shows it, and a person can post it by hand. |
deleted | You took it down after a post.delete. |
Saying the same thing twice is fine. Anything the post's state does not allow is answered 409 with the state it is in, such as skipping a post already reported published.