Receive Developer webhooks

Step-by-step guide
  • About 8 minutes
  • 7 steps
  • Growth and Agency
  • Checked 2 Nov 2026
0 of 7 steps done

Before you start

  • An endpoint on your server that accepts POST with 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

  1. Step 1

    Open Developer settings

    Under Settings in the left-hand menu, click Developer and scroll to Webhooks.

    Where to find it in Skylane
    1. Settings
    2. Developer
    3. Webhooks
  2. 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.
  3. 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.

  4. 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
    // });
  5. Step 5

    Reply fast, then work

    Reply with any 2xx within 5 seconds. Skylane-Event names the event and Skylane-Delivery is the event id, which stays the same on retries and redeliveries, so store it and ignore repeats.

  6. Step 6

    Send a test

    Click Send test next to the webhook. The result shows straight away and appears in the delivery log.

  7. 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.requested and approval.decided.
  • If your server is down or answers 429 or 5xx, Skylane retries after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. Other 4xx answers 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.
Example: post.failedjson
{
  "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.