> ## Documentation Index
> Fetch the complete documentation index at: https://www.ayrshare.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks Overview

> Webhook API Endpoints to register webhooks to receive updates on events

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

<PlansAvailable plans={["premium"]} maxPackRequired={false} />

## What is a Webhook?

A Webhook allows you to be notified when certain system *actions* occur via a call to a URL you provide. Webhooks are also known as "URL Callbacks" or "HTTP push calls". Your URL must use SSL and begin with HTTPS.

<Card title="Webhook Actions" icon="link" href="/docs/apis/webhooks/actions" horizontal>
  See the available actions for webhooks.
</Card>

### Understanding Ayrshare Webhooks

Webhooks are categorized by the specific action and are *registered at the Primary Profile or User Profile level*. Any updates for the Primary or User Profiles are sent first to the registered Webhook for the User Profile. If User Profile does not have a registered Webhook, the update will be sent to the Primary Profile registered Webhook.

For example:

<ul class="custom-bullets">
  <li>
    If a User Profile has a registered Social Action Webhook and unlinks TikTok,
    the registered Social Action Webhook URL for the User Profile will be
    called. The Primary Profile webhook *will not* be called.
  </li>

  <li>
    If a User Profile unlinks TikTok and *does not* have a registered Social
    Action Webhook, but the Primary Profile does have a registered Webhook, the
    registered Social Action Webhook URL for the Primary Profile will be called.
  </li>
</ul>

### Register a Webhook

Register a Webhook by providing an endpoint URL and the type of action type to the POST [`/hook/webhook`](/docs/apis/webhooks/register) endpoint. When the action occurs an `HTTP POST` message will be sent to the provided URL.
E.g. register a URL to get notified of the status of scheduled post.

The Webhook endpoint URL should not use redirects and must be the final destination URL.

If you only register the Primary Profile webhook, the User Profiles will automatically inherit the Primary Profile webhook.
To have a unique webhook for each User Profile, you must register a webhook for each User Profile.

<Note>
  After your Webhook receives the `HTTP POST`, your server **must** respond with
  an HTTP status of `200` to mark the call as successful. If your server does
  not respond within 15 seconds, the attempt is recorded as failed and
  retried. Respond as soon as you receive the request and do your processing
  asynchronously — a timeout is not a rejection, so if your handler completes
  the work but answers late, the retry will make you process it twice.
</Note>

You can also register webhooks in the Developer Dashboard.

### Webhook Retries

If the HTTP response from your server is not in the `200-299` success range, or your server does not respond within 15 seconds, the system will automatically retry the Webhook call two more times. The first retry will occur after 5 seconds and the second retry will occur 30 seconds later. The retries will have the same `hookId` and the same payload.

### Delivery Semantics and Idempotency

Ayrshare delivers webhooks **at least once**. **Occasional duplicates are normal operation, not a defect — every consumer needs idempotency as a permanent property.**

Duplicates arrive in two different shapes, and each needs a different key:

| Duplicate                         | Why it happens                                                                     | What is identical                                                                         | Key that catches it              |
| --------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------- |
| **A retry of one delivery**       | Your endpoint answered outside `200-299`, timed out, or the connection dropped     | `hookId` **and** the whole payload, byte for byte                                         | `hookId`                         |
| **The same event notified again** | The social network sends us the event a second time, or it is re-observed upstream | The payload's own identifiers, such as `id` on `messages` — but `hookId` is **different** | a key you build from the payload |

`hookId` identifies **one delivery** of an event. It is identical on every retry of that delivery, so claiming on it makes retries safe — but a fresh notification of the same underlying event arrives with a **new** `hookId`, so `hookId` on its own will not recognise that case.

Recommended receiver pattern:

1. **Respond first.** Return `2xx` immediately, then process asynchronously. A timeout is not a rejection — if you finish the work but answer late, the event is sent again.
2. **Claim `hookId` atomically** the moment the request arrives — a unique constraint, an `INSERT ... ON CONFLICT DO NOTHING`, or a `SET NX` — **not** a read-then-write check. Two attempts can arrive concurrently, and a check-then-act guard lets both through.
3. **Claim a key of your own too**, built from the payload, so a second notification carrying a new `hookId` is still recognised. On `messages`, `id` combined with `subAction` works well.
4. **Then** do the work, holding both claims long enough to cover the retry window and any later re-notification.

<Note>
  The payload's `id` is not unique on its own for every event type — the same
  message id recurs across edits and reactions, and `messageRead` payloads carry
  no `id` — so combine it with `subAction` rather than using it bare.
</Note>

### Delivery Metadata Headers

Every delivery carries two headers identifying that specific transmission, so you can tell an original from a retry:

```bash theme={"system"}
X-Ayrshare-Delivery-Id      : <unique per delivery attempt>
X-Ayrshare-Delivery-Attempt : <0 on the first send, then 1, 2, ...>
```

`X-Ayrshare-Delivery-Attempt` is `0` on the first send and increments by one on each retry, so any value above `0` means we have already sent this delivery at least once. Treat it as an unbounded counter rather than a fixed set of values — the number of retries is an operational detail that can change. `X-Ayrshare-Delivery-Id` is unique to each attempt — quote it to support and it identifies the exact delivery record.

These identify the **transmission**; `hookId` identifies the **event**. Deduplicate on `hookId`, not on the delivery id — the delivery id is different on every attempt by design, so nothing would ever be recognised as a duplicate.

## Webhook Security

You may choose to add additional security by setting HMAC authentication as an HTTP request. This is often done to prevent replay attacks. Ayrshare uses [HMAC-SHA256](https://en.wikipedia.org/wiki/HMAC) to hash the body of the message and includes it and the UNIX timestamp in the header of the POST.

```bash theme={"system"}
X-Authorization-Timestamp : <Unix Timestamp In Seconds>
X-Authorization-Content-SHA256 : <HashedContent>
```

Based on a secret key set when [registering your webhook](/docs/apis/webhooks/register), you may validate the post by comparing the header `X-Authorization-Content-SHA256` with the HMAC-SHA256 of the POST body. The signing secret is profile-wide — one secret per User Profile, used across all of that profile's webhook actions, so setting it for one action changes it for all actions on that profile. Multi-profile accounts manage a separate secret per profile (target a profile with the `Profile-Key` header).

## Webhook Logs

In the Ayrshare Dashboard, you may view the [active webhooks](https://app.ayrshare.com/webhooks), see the details of the Webhook sent, your server response status, and resend the Webhook to the registered URL.
Switch to a particular User Profile to view that profile's Webhook logs.

### HTTP Response Codes

The first column indicates a successful HTTP response ✔️ (200, 300) from the Webhook or a failed response ✖️ (400, 500).

Switch to a particular user profile to view that profile's Webhook logs.

### Error Rate

The "Error Rate" of the most recent 1,000 posts can be viewed on both the Actions and Webhook Logs pages within the dashboard. Any webhook response from your server of 400-500 is considered an error.
