Gareth Wilson Gareth Wilson

Guide to Ayrshare Webhooks: Features and Best Practices

Published


Ayrshare lets an application publish and manage posts across social networks through one API, often on behalf of many end users who each have their own User Profile. Most of what Ayrshare does finishes later than the API call that started it: a scheduled post goes out at 9am, TikTok takes a while to publish a video, a follower replies with a comment or a direct message. Ayrshare webhooks report those results to your server as they happen.

Register a webhook secret with Ayrshare. Without one, deliveries are unsigned and fail verification.

This guide explains Ayrshare's per-action, per-profile webhook model, the payload fields that matter, how to set and rotate the profile-wide signing secret, how to verify the V2 signature header, and how Ayrshare's retry rules affect your status codes.

What are Ayrshare webhooks?

Ayrshare registers webhooks by action, such as scheduled, messages or comments, and by profile. A User Profile's own webhook for an action is called first. If the User Profile has none, Ayrshare calls the Primary Profile's webhook for that action, so one registration on the Primary Profile can serve every user. The URL must be HTTPS and must not redirect.

Each delivery is a JSON POST. Signing is opt-in: until you set a secret, requests carry no signature. Once a secret is set, Ayrshare adds an HMAC-SHA256 of the raw body in two headers, a legacy one with the bare hex digest and a V2 one that can hold two signatures during a secret rotation.

Ayrshare webhook features

FeatureDetails
RegistrationPOST /hook/webhook per action, or the Developer Dashboard
ScopePrimary Profile, or a User Profile that overrides it
Signaturehex HMAC-SHA256
Signature headerx-authorization-content-sha256-v2
Signature formatv1=<hex>, or v1=<current>,v1=<previous> for 24 hours after a rotation
Legacy headerX-Authorization-Content-SHA256, bare hex of the current signature
Timestamp headerX-Authorization-Timestamp, Unix seconds, not signed
Delivery headersX-Ayrshare-Delivery-Id (per attempt), X-Ayrshare-Delivery-Attempt (0 first)
SecretOptional, one per User Profile, shared by all its actions
BodyJSON
Event routingBody field action
Response window200 within 15 seconds
Retries408, 425, 429, 5xx, timeouts and dropped connections, for up to about 16 hours

Ayrshare publishes no source IP list for webhooks.

Events and payloads

actionRefined byTriggered when
scheduledsubAction: tikTokPublishedA scheduled post is processed (not sent for immediate posts); TikTok finishes publishing
socialtype: link, unlink, refreshA social account is linked, unlinked or must be refreshed
messagessubAction: messageCreated, messageEdited, messageRead, reactionCreatedA message arrives, is edited, read or reacted to
batchtype: batch, batchTypeA batch export is ready (url expires at urlExpires)
feedA new item appears in a registered RSS feed
mentionssubAction: mentionYour Instagram account is mentioned
commentssubAction: commentA comment is created on connected content (Facebook, Instagram, Threads)
automationstype: automation.triggered, automation.completedAn automation fires or finishes
demotype: upgradeWarning, upgradeDemo plan limit events
suspendedtype: suspend, unsuspendA profile is suspended or reinstated
adstype: created, spendSynced, accountConnected, accountDisconnected, accountRevokedAd or ad account events

Not every payload carries a subAction (the documented messages example has none), so treat it as optional. Ayrshare shows subAction values in examples and prose rather than listing them per action.

FieldMeaning
actionThe registration category
hookIdIdentifies one delivery of an event; identical on every retry. Dedupe on it
refIdUser Reference ID of the profile
urlYour webhook URL
createdISO 8601 time of the event
timeStampUnix time, present when webhook security (a secret) is on
platformSocial network the event came from
idPost, message or comment id (not unique alone: combine with subAction)

Messages events add message, senderId, senderDetails.name, conversationId, recipientId and attachments. Scheduled events add status, errors and postIds, which is where you find out that a post failed on one network but succeeded on another.

This is the ads sample from the webhook samples library:

{
  "action": "ads",
  "type": "accountRevoked",
  "platform": "google",
  "accountId": null,
  "id": "c4e6a8b0d2f4e6a8c0b2d4f6e8a0c2b4d6f8e0a2c4b6d8f0a2c4e6b8d0f2a4c6",
  "created": "2026-10-21T08:15:00.000Z",
  "hookId": "c4e6a8b0d2f4e6a8c0b2d4f6e8a0c2b4d6f8e0a2c4b6d8f0a2c4e6b8d0f2a4c7",
  "refId": "9abf1426d6ce9122ef11c72bd62e59807c5cc083",
  "url": "https://api.myapp.com/Webhook/Ayrshare/Ads"
}

Setting up Ayrshare webhooks

Choose and set the secret

There is one secret per User Profile, shared by all of that profile's webhook actions, so setting it for one action changes it for all of them. Multi-profile accounts manage a separate secret per profile, targeted with the Profile-Key header. Generate a long random value yourself, for example with openssl rand -hex 32, and set it in one of three ways:

  1. Pass secret when you register a webhook.
  2. Set or rotate it on the Webhooks page of the Developer Dashboard.
  3. Call POST /hook/webhook/secret:
curl --request POST https://api.ayrshare.com/api/hook/webhook/secret \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --header 'Profile-Key: YOUR_PROFILE_KEY' \
  --data '{ "secret": "your-new-signing-secret" }'

The secret is used as typed, as UTF-8 bytes. It is not hex- or base64-decoded.

Register each action

Register one webhook per action you want to receive:

curl --request POST https://api.ayrshare.com/api/hook/webhook \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "action": "scheduled",
    "url": "https://your-domain.com/webhooks/ayrshare",
    "secret": "your-signing-secret"
  }'

Register on the Primary Profile only if every user's events can go to the same URL. Register on individual User Profiles when a profile needs its own URL.

With Hookdeck, create a source with the Ayrshare source type, enter the profile's secret as its verification secret, and register the Hookdeck source URL with Ayrshare. If you have not registered a secret, leave verification off on the source, because those deliveries carry no signature. For local work, npx hookdeck-cli listen 3000 ayrshare --path /webhooks/ayrshare forwards events to your machine through the Hookdeck CLI.

Rotate without dropping deliveries

When you rotate the secret, Ayrshare signs deliveries with both the new and the previous secret for 24 hours, and the V2 header carries both v1= values. Update your receiver within that window. The legacy header only ever carries the current signature, which is why the V2 header is the one to verify. See verification.md in the Ayrshare skill for worked examples.

Securing Ayrshare webhooks

Split x-authorization-content-sha256-v2 on commas, keep the values that start with v1=, and accept the request if any of them matches a hex HMAC-SHA256 of the raw body keyed with the profile's secret. Check each candidate is 64 hex characters before decoding it. Ayrshare's rotation documentation describes the same check.

const crypto = require('node:crypto');

function verifySignature(rawBody, headers, secret) {
  if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret) return false;
  const signature = headers['x-authorization-content-sha256-v2'];
  if (typeof signature !== 'string') return false;
  const key = Buffer.from(secret, 'utf8');
  const message = rawBody;
  const candidates = signature.split(',').map((part) => part.trim())
    .filter((part) => part.startsWith('v1=')).map((part) => part.slice(3));
  const expected = crypto.createHmac('sha256', key).update(message).digest();
  return candidates.some((candidate) => {
    if (!/^[0-9a-fA-F]{64}$/.test(candidate)) return false;
    const received = Buffer.from(candidate, 'hex');
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

A receiver that routes on action and records hookId before acting:

const express = require('express');

const app = express();

app.post('/webhooks/ayrshare', express.raw({ type: '*/*' }), async (req, res) => {
  const secret = process.env.AYRSHARE_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500);
  if (!verifySignature(req.body, req.headers, secret)) return res.sendStatus(401);

  let hook;
  try {
    hook = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }
  if (!hook || typeof hook !== 'object' || Array.isArray(hook)) return res.sendStatus(400);
  try {
    if (await alreadyHandled(hook.hookId)) return res.sendStatus(200);

    if (hook.action === 'scheduled') {
      await updatePostStatus(hook.refId, hook.id, hook.status, hook.errors);
    } else if (hook.action === 'messages' || hook.action === 'comments') {
      await addToInbox(hook.refId, hook);
    } else if (hook.action === 'social' && hook.type === 'unlink') {
      await flagDisconnectedAccount(hook.refId, hook.platform);
    }
  } catch {
    return res.sendStatus(503);
  }

  res.sendStatus(200);
});

alreadyHandled, updatePostStatus, addToInbox and flagDisconnectedAccount stand for your own storage and business logic.

X-Authorization-Timestamp is not part of the signed content, so it gives no protection against a replayed request. Ayrshare's overview mentions replay attacks, but only hookId deduplication actually stops a repeat from being processed twice. Hookdeck's Ayrshare source verifies the V2 header, tries every v1= value, and applies no timestamp check. It answers Ayrshare with 200 and records a failed verification afterwards, rather than returning the 401 shown above.

Make Ayrshare webhooks production-ready. Hookdeck Event Gateway verifies Ayrshare's V2 signature through secret rotations, stores every post, message and comment event, and delivers it to your service with retries you control.

Ayrshare webhook limitations and pain points

A 4xx is final

The Problem: A short-lived error on your side loses the event for good.

Why It Happens: Ayrshare retries 408, 425, 429, 5xx, timeouts and dropped connections. Any other 4xx is treated as final. A receiver that returns 401 because it still has the old secret after the 24-hour rotation window, or 404 during a bad deploy, gets no second chance.

Workarounds:

  • Return 5xx for errors that might clear up, such as a database outage, and reserve 4xx for requests you never want again.
  • Update every receiver's secret well inside the 24-hour rotation window.

How Hookdeck Can Help: Hookdeck answers Ayrshare itself, so Ayrshare's retry rules apply only to Hookdeck. Your service's errors, including 4xx, are retried on Hookdeck's schedule and can be replayed later.

Signing is optional and easy to miss

The Problem: Requests arrive with no signature header, and anyone who learns the URL can send fake events.

Why It Happens: Ayrshare signs only when the profile has a secret. Each User Profile has its own secret, so a new profile may start unsigned even if the Primary Profile is signed.

Workarounds:

  • Set a secret on every profile as part of onboarding a user.
  • Reject unsigned requests in production rather than skipping verification.

How Hookdeck Can Help: Hookdeck's Ayrshare source records a failed verification for any unsigned delivery once verification is on, so missing secrets show up in the request log instead of passing silently.

Retries carry different bytes

The Problem: Deduplicating on a body hash or the signature lets retries through.

Why It Happens: Each attempt rebuilds the payload, so timeStamp and the signature change between attempts. X-Ayrshare-Delivery-Id also changes, because it identifies the attempt rather than the event.

Workarounds:

  • Deduplicate on hookId, which Ayrshare keeps the same across retries.
  • Keep processed hookId values for at least 24 hours, since retries can arrive up to about 16 hours after the event.

How Hookdeck Can Help: Hookdeck's deduplication can key on the body's hookId, so retried attempts are dropped before they reach your service.

Sub-actions aren't fully listed

The Problem: A new subAction or type value arrives that your code has never seen.

Why It Happens: Ayrshare documents subAction values in examples and descriptions, not as a complete list per action, and some examples omit the field.

Workarounds:

  • Treat subAction and type as optional, and acknowledge values you don't recognise with 200.
  • Log unknown values so you can add handling later.

How Hookdeck Can Help: Hookdeck filters can route on action and subAction, and the request log shows every value Ayrshare has sent.

Best practices

Verify the V2 header, not the legacy one

The V2 header keeps working through a secret rotation because it can carry both signatures. The bare X-Authorization-Content-SHA256 header only holds the current one.

Use the raw request body

Verify before parsing. Use express.raw({ type: '*/*' }) in Express, await request.arrayBuffer() in Next.js and await request.body() in FastAPI, and never re-serialise the JSON before checking it.

Answer within 15 seconds

Store the verified event, return 200, and then update posts or inboxes. A slow answer counts as a failure and triggers a retry. Read more in why you should process webhooks asynchronously.

Route by profile with refId

When many users share the Primary Profile's webhook, refId tells you which User Profile an event belongs to. Look up the user by refId before you touch any data.

Deduplicate on hookId

Store each hookId with the change it caused. See our guide to webhook idempotency.

Conclusion

Ayrshare webhooks are registered per action and per profile, signed only once a profile has a secret, and verified through the V2 header that survives a 24-hour rotation. Give every profile a secret, return 5xx rather than 4xx for errors that might pass, deduplicate on hookId, and use refId to find the user each event belongs to.

Hookdeck Event Gateway takes Ayrshare's deliveries, checks the V2 signature, and retries your service on its own schedule, so a 4xx on your side no longer ends the event's life.

Get started with Hookdeck for free and handle Ayrshare webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.