Before you start
- An endpoint on your server that accepts
POSTwith a JSON body on a public https:// address (port 443 or 8443). - Your endpoint can read the raw request body, so the signature check works.
Steps
- Step 1
Open Developer settings
Under Settings in the left-hand menu, click Developer and scroll to Webhooks.
Where to find it in Skylane- Settings
- Developer
- Webhooks
- Step 2
Add the webhook
Paste your address into Webhook URL, tick the events under Events to send and click Create webhook.
Diagram: on the Developer API keys screen, press Create webhook. - Step 3
Copy the signing secret
Under Copy your signing secret now, click Copy secret and store it in your server's secret settings. It is shown once. Then click I have saved my secret.
- Step 4
Check every signature
Each message has
Skylane-Signature: t=<unix time>,v1=<hex>: an HMAC-SHA256 of the timestamp, a full stop and the raw body, using your secret. Reject anything that does not match or is more than 5 minutes old.Verify in Node.jsjavascript import crypto from "node:crypto"; // rawBody must be the exact bytes Skylane sent. Do not parse and re-serialise the JSON first. export function verifySkylane(rawBody, header, secret, toleranceSeconds = 300) { const fields = Object.fromEntries(String(header || "").split(",").map((part) => part.split("=", 2))); const timestamp = Number(fields.t); if (!timestamp || !fields.v1) return false; if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false; const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex"); const a = Buffer.from(expected, "hex"); const b = Buffer.from(fields.v1, "hex"); return a.length === b.length && crypto.timingSafeEqual(a, b); } // Express example: keep the raw body for this route. // app.post("/skylane-webhook", express.raw({ type: "application/json" }), (req, res) => { // if (!verifySkylane(req.body, req.get("Skylane-Signature"), process.env.SKYLANE_WEBHOOK_SECRET)) return res.sendStatus(401); // const event = JSON.parse(req.body); // res.sendStatus(200); // reply first, then do the slow work // }); - Step 5
Reply fast, then work
Reply with any
2xxwithin 5 seconds.Skylane-Eventnames the event andSkylane-Deliveryis the event id, which stays the same on retries and redeliveries, so store it and ignore repeats. - Step 6
Send a test
Click Send test next to the webhook. The result shows straight away and appears in the delivery log.
- Step 7
Use the delivery log
Click Deliveries to see the last 50 messages, their state and the next retry time. View message shows exactly what was sent, and Redeliver sends it again.
Good to know
- Events:
post.scheduled,post.published,post.failed,post.needs_attention(live with a warning, such as a missing first comment, or a send that was interrupted and may already be live),post.deleted,account.connected,account.disconnected,account.reconnect_needed,approval.requestedandapproval.decided. - If your server is down or answers
429or5xx, Skylane retries after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. Other4xxanswers are not retried. - After 20 deliveries in a row fail for good, the webhook is paused. Switch on A webhook was paused in Settings, then Notifications to get an email or in-app alert. Fix your endpoint, click Resume, then redeliver what you missed.
- Webhooks belong to the workspace owner's account and are sent for every workspace they own.
{
"id": "evt_EXAMPLE",
"type": "post.failed",
"createdAt": "2026-11-02T09:30:00Z",
"data": {
"postId": "YOUR_POST_ID",
"publicationId": "YOUR_PUBLICATION_ID",
"accountId": "YOUR_ACCOUNT_ID",
"platform": "instagram",
"accountName": "Your account name",
"error": { "code": "THE_PLATFORM_CODE", "kind": "permanent", "message": "The plain-English reason.", "fixHint": "What to do about it." },
"failedAt": "2026-11-02T09:30:00Z",
"workspaceId": "YOUR_WORKSPACE_ID"
}
}Common problems
Every signature check fails
Hash the raw bytes exactly as received, not re-serialised JSON, and check you are using this webhook's secret.
Redeliver is greyed out
The webhook is paused, or the delivery is still being retried. Resume it first, or wait for the retry.
I get the same event twice
That is a retry or a redelivery. Use Skylane-Delivery (the event id) to ignore repeats.
Still stuck? Contact support
Tell us which step you reached and what you see on screen. A screenshot helps.