> ## 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.

# Ротація секретного ключа підпису

> Безпечно виконуйте ротацію секретного ключа підпису webhook із 24-годинним пільговим вікном подвійного підпису

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>;
};

export const HeaderAPI = ({noProfileKey, profileKeyRequired}) => <>
    <ParamField header="Authorization" type="string" required>
      <a href="/docs/apis/overview#authorization">API Key</a> of the Primary Profile.
      <br />
      <br />
      Format: <code>Authorization: Bearer API_KEY</code>
    </ParamField>
    {!noProfileKey && (profileKeyRequired ? <ParamField header="Profile-Key" type="string" required>
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField> : <ParamField header="Profile-Key" type="string">
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField>)}
  </>;

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

## Огляд

Ayrshare підписує кожну доставку webhook за допомогою [HMAC-SHA256](https://en.wikipedia.org/wiki/HMAC) корисного навантаження, використовуючи Ваш **секретний ключ підпису**, тож Ваш приймач може підтвердити, що доставку справді надіслано з Ayrshare. Див. [Безпека Webhook](/docs/apis/webhooks/overview#webhook-security), щоб дізнатися, як працює верифікація.

Ротація Вашого секретного ключа підпису дозволяє замінити його за регулярним графіком або негайно, якщо Ви підозрюєте, що його було скомпрометовано. Щоб зробити ротацію безпечною, Ayrshare відкриває **24-годинне пільгове вікно** після кожної ротації, протягом якого доставки підписуються **обома** — попереднім і новим — секретами. Це дозволяє оновити Ваш приймач у власному темпі без втрати чи відхилення жодної доставки — той самий шаблон використовують Stripe і GitHub.

<Note>
  Секретний ключ підпису діє **на весь профіль**: існує один секрет на User Profile (UID),
  і він підписує **кожну** дію webhook, зареєстровану цим профілем. Немає окремого
  секретного ключа для окремої дії — встановлення або ротація секрету змінює його для всіх
  дій цього профілю одночасно.
</Note>

## Ротація з панелі керування

Ви можете встановити або виконати ротацію секретного ключа підпису на [сторінці Webhooks](https://app.ayrshare.com/webhooks) у Developer Dashboard. Панель **Signing Secret** з'являється над списком Ваших webhook після того, як у профілі буде принаймні один зареєстрований webhook.

<Steps>
  <Step title="Відкрийте панель Signing Secret">
    Перейдіть на [сторінку Webhooks](https://app.ayrshare.com/webhooks). Якщо секрет ще не налаштовано, панель показує **No signing secret configured** з кнопкою **Set Signing Secret**. Якщо один уже налаштований, вона показує **Signing secret configured** з кнопкою **Rotate**.
  </Step>

  <Step title="Встановіть або виконайте ротацію">
    Натисніть **Set Signing Secret** (перший раз) або **Rotate** (наявний секрет). Відкриється модальне вікно з попередньо заповненим і показаним надійним, випадково згенерованим секретом. Ви можете **Copy** його, **Regenerate** новий або перемкнутися на **paste my own**, щоб надати власне значення.
  </Step>

  <Step title="Підтвердьте">
    Скопіюйте секрет у безпечне місце — його показано лише один раз, і його ніколи неможливо буде отримати з UI знову — потім підтвердьте, щоб надіслати. З'явиться повідомлення про успіх, і панель оновиться.
  </Step>

  <Step title="Оновіть Ваш приймач">
    Під час ротації (не першого встановлення) панель показує індикатор активного пільгового вікна, а кнопка **Rotate** вимкнена до закриття вікна. У Вас є 24 години, щоб розгорнути новий секрет на Вашому приймачі.
  </Step>
</Steps>

## Ротація через API

Виконайте ротацію (або встановіть) секретного ключа підпису одним викликом. Це створює новий секрет, перепризначає посилання на секрет профілю на нього і — коли існував наявний секрет — записує заміщений секрет як попередній секрет із терміном дії 24 години.

### Header Parameters

<HeaderAPI />

### Body Parameters

<ParamField body="secret" type="string" required>
  Значення нового секретного ключа підпису. Приймається будь-який непорожній рядок. Рекомендуємо довге, високоентропійне випадкове значення (наприклад, 32 випадкових байти в кодуванні base64url).
</ParamField>

<RequestExample>
  ```bash cURL theme={"system"}
  curl --request POST \
    --url 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"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200: Response theme={"system"}
  {
    "status": "success",
    "action": "webhook",
    "refId": "3dc079614bdc3f281d9" // User Profile Ref Id
  }
  ```
</ResponseExample>

Відкритий текст `secret` **ніколи** не повертається у відповіді та ніколи не логується. Відповідь містить клієнтський `refId` (хеш UID), ніколи не сам UID. Заголовок `Profile-Key` необов'язковий і обмежує ротацію одним User Profile для акаунтів із кількома профілями.

Відсутній або порожній `secret` повертає зіставлену помилку (`code: 101`, "Missing/incorrect parameter") зі статусом HTTP `400`, і жодних змін до Вашого поточного секрету не вноситься. Перше встановлення через API (за відсутності наявного секрету) створює секрет без запису попереднього секрету та без пільгового вікна.

## Безпечна процедура ротації

Завдяки 24-годинному пільговому вікну немає обов'язкового порядку дій — Ваш приймач продовжує працювати протягом усього процесу. Рекомендована послідовність:

<Steps>
  <Step title="Виконайте ротацію секрету">
    Виконайте ротацію з панелі керування або через API. Ayrshare одразу починає підписувати доставки обома — попереднім і новим — секретами.
  </Step>

  <Step title="Оновіть Ваш приймач">
    Протягом 24 годин розгорніть новий секрет на Вашому приймачі webhook, щоб він верифікував за новим значенням.
  </Step>

  <Step title="Дайте вікну закритися">
    Через 24 години Ayrshare автоматично очищує попередній секрет і підписує лише новим секретом. Ніяких додаткових дій з Вашого боку не потрібно.
  </Step>
</Steps>

<Tip>
  Якщо Ви виконуєте ротацію знову, поки пільгове вікно ще відкрите, щойно заміщений
  секрет стає новим попереднім секретом, і починається нове 24-годинне вікно. Одночасно
  зберігається лише один попередній секрет.
</Tip>

## Верифікація підписів під час пільгового вікна

Поза пільговим вікном підписані доставки містять стандартні заголовки (див. [Безпека Webhook](/docs/apis/webhooks/overview#webhook-security)):

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

Протягом 24-годинного вікна після ротації новий заголовок `X-Authorization-Content-SHA256-V2` містить **обидва** підписи, поточний першим, розділені комою:

```bash theme={"system"}
X-Authorization-Timestamp : <Unix Timestamp In Seconds>
X-Authorization-Content-SHA256 : <current-sig>
X-Authorization-Content-SHA256-V2 : v1=<current-sig>,v1=<previous-sig>
```

<Note>
  `X-Authorization-Content-SHA256` не змінюється: він завжди містить єдиний
  HMAC поточного секрету для зворотної сумісності. Подвійні підписи з'являються лише в
  новому заголовку `X-Authorization-Content-SHA256-V2`.
</Note>

Кожне значення в `X-Authorization-Content-SHA256-V2` має префікс тега схеми. `v1=` позначає підпис HMAC-SHA256, обчислений точно так, як `X-Authorization-Content-SHA256`. Заголовок `-V2` завжди присутній, коли доставку підписано — він містить принаймні `v1=<current-sig>` — тож Ви можете покладатися на нього як на стабільний контракт приймача.

Щоб верифікувати доставку під час (або поза) ротацією:

<Steps>
  <Step title="Обчисліть HMAC">
    Обчисліть HMAC-SHA256 **сирого тіла запиту**, використовуючи локально налаштований секретний ключ підпису.
  </Step>

  <Step title="Порівняйте з кожним переліченим підписом">
    Прочитайте `X-Authorization-Content-SHA256-V2`, розділіть його за комами, зніміть префікс `v1=` з кожного значення та прийміть доставку як автентичну, якщо Ваш обчислений HMAC збігається з **будь-яким** переліченим підписом `v1=`.
  </Step>
</Steps>

Прийняття, якщо збігається **будь-який** перелічений підпис, — це те, що робить ротацію без простоїв: приймач, усе ще налаштований зі старим секретом, збігається з `v1=<previous-sig>`, тоді як приймач, оновлений до нового секрету, збігається з `v1=<current-sig>` — обидва проходять успішно протягом вікна.

### Приклад верифікації на приймачі

```javascript Node.js theme={"system"}
import crypto from "crypto";

// secret is the signing secret currently configured on your receiver.
function isAuthenticWebhook(rawBody, headers, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody) // the raw, unparsed request body
    .digest("hex");

  const headerValue = headers["x-authorization-content-sha256-v2"] || "";

  // Accept if ANY v1= signature in the header matches our computed HMAC.
  return headerValue
    .split(",")
    .map((part) => part.trim())
    .filter((part) => part.startsWith("v1="))
    .map((part) => part.slice("v1=".length))
    .some((sig) => {
      const sigBuf = Buffer.from(sig);
      const expectedBuf = Buffer.from(expected);
      // timingSafeEqual throws on length mismatch — treat as not authentic.
      return (
        sigBuf.length === expectedBuf.length &&
        crypto.timingSafeEqual(sigBuf, expectedBuf)
      );
    });
}
```

<Warning>
  Завжди обчислюйте HMAC над **сирими** байтами тіла запиту, точно як отримано —
  не над повторно серіалізованим JSON-об'єктом. Повторна серіалізація може змінити
  пробіли або порядок ключів і зламати верифікацію. Використовуйте порівняння з
  постійним часом (наприклад, `crypto.timingSafeEqual`), щоб уникнути timing-атак.
</Warning>

Якщо для доставки відсутній запис поточного секрету, доставка виконується **без підпису** (без заголовків підпису), а не з помилкою. Якщо відсутній лише запис попереднього секрету, попередній підпис пропускається, а поточний підпис усе одно надсилається в обох заголовках.
