---
title: "વેબહૂક સહી ચકાસો"
description: "ThunderPhone મોકલે છે તે દરેક વેબહૂક અને ટૂલ વિનંતી પર સહી હોય છે. અહીં આપેલી રીતથી સહી એકવાર ચકાસો, પછી તમે ચલાવતા દરેક એન્ડપોઇન્ટ પર એ જ ચકાસણી ફરીથી વાપરો."
---

અમે તમારા સર્વરને મોકલતી દરેક વિનંતી — webhook ડિલિવરી અને
ટૂલ-એન્ડપોઇન્ટ ઇન્વોકેશન — `X-ThunderPhone-Signature` હેડરમાં HMAC-SHA256
સિગ્નેચર ધરાવે છે. વેરિફિકેશન એકવાર યોગ્ય રીતે સેટ કરો અને
દરેક હેન્ડલરમાં એ જ હેલ્પર વાપરો.

## અલ્ગોરિધમ

1. **raw** રિક્વેસ્ટ બોડી વાંચો — અમે તમને POST કરેલા ચોક્કસ બાઇટ્સ.
2. `hmac_sha256(secret, body).hexdigest()` ગણો.
3. `X-ThunderPhone-Signature` સાથે **constant time** માં સરખામણી કરો.
   (સામાન્ય સ્ટ્રિંગ સરખામણી ટાઇમિંગ માહિતી લીક કરે છે.)

અમે ટ્રાન્સમિટ કરીએ છીએ તે જ ચોક્કસ બાઇટ્સ પર સિગ્નેચર કરીએ છીએ, તેથી raw બોડી
વેરિફાય કરવું હંમેશા કાર્ય કરે છે. તે બાઇટ્સ પેલોડનું **canonical JSON serialization**
પણ છે — કીઓ મૂળાક્ષર મુજબ સૉર્ટ કરેલી, કોમ્પેક્ટ સેપરેટર્સ
(ખાલી જગ્યાઓ વિનાના `,` અને `:`), UTF-8. જ્યારે તમારું ફ્રેમવર્ક માત્ર પાર્સ કરેલું JSON
આપતું હોય ત્યારે આ તમને બીજું, સંપૂર્ણપણે સમકક્ષ રીત આપે છે:
કેનોનિકલ રીતે ફરી સિરિયલાઇઝ કરો અને તેના પર HMAC કરો.

```python
# Equivalent to hashing the raw body:
import json
canonical = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")
```

raw બોડી પસંદ કરો — તેમાં એક પગલું ઓછું છે અને કેટલીક ભાષાઓમાં JSON નંબરના
રાઉન્ડ-ટ્રિપિંગની વિચિત્રતાઓથી સુરક્ષિત છે.

## કયું સિક્રેટ?

| સ્રોત | સિક્રેટ |
|--------|--------|
| [Webhook એન્ડપોઇન્ટ](/gu/webhooks/endpoints) (`/v1/developer/webhook-endpoints`) | બનાવતી વખતે એકવાર પરત મળતું પ્રતિ-એન્ડપોઇન્ટ `secret` (48 હેક્સ અક્ષરો) |
| [લેગસી સિંગલ-URL webhook](/api-reference/organizations#legacy-single-url-webhook) | `GET /v1/webhook` પર પરત મળતું પ્રતિ-org `secret` |
| [ટૂલ-એન્ડપોઇન્ટ ઇન્વોકેશન](/gu/tools/overview) (તમારા `endpoint.url` પર સીધો કૉલ) | **org-સ્તરનું webhook સિક્રેટ** (લેગસી સિંગલ-URL webhook જેવું જ) — પ્રતિ-એન્ડપોઇન્ટ સિક્રેટ નહીં |

સિક્રેટને તમારા સિક્રેટ મેનેજર અથવા env var માં સ્ટોર કરો — તેને ક્યારેય કમિટ કરશો નહીં.

## સંદર્ભ અમલીકરણો

ચારેય raw રિક્વેસ્ટ બોડીને વેરિફાય કરે છે:

<CodeGroup>
```python Python
import hashlib
import hmac


def verify(body: bytes, signature: str, secret: str) -> bool:
    """Constant-time HMAC-SHA256 verification."""
    expected = hmac.new(
        secret.encode("utf-8"),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature or "")
```

```javascript Node.js
import crypto from "node:crypto";

export function verify(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  if (!signature || expected.length !== signature.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}
```

```go Go
package webhook

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

func Verify(body []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}
```

```ruby Ruby
require "openssl"

def verify(body, signature, secret)
  expected = OpenSSL::HMAC.hexdigest("SHA256", secret, body)
  Rack::Utils.secure_compare(expected, signature.to_s)
end
```
</CodeGroup>

## ફ્રેમવર્ક-વિશિષ્ટ જોડાણ

<CodeGroup>
```python FastAPI
from fastapi import FastAPI, HTTPException, Request

app = FastAPI()

@app.post("/thunderphone-webhook")
async def hook(request: Request):
    body = await request.body()           # raw bytes, NOT request.json()
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify(body, sig, SECRET):
        raise HTTPException(status_code=401)

    import json
    event = json.loads(body)
    # … dispatch on event["type"] …
    return {"ok": True}
```

```javascript Express
import express from "express";

const app = express();

app.post(
  "/thunderphone-webhook",
  // IMPORTANT: parse as raw; do NOT use express.json() here.
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.header("X-ThunderPhone-Signature") || "";
    if (!verify(req.body, sig, process.env.WEBHOOK_SECRET)) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));
    // … dispatch on event.type …
    res.sendStatus(204);
  },
);
```

```python Django
import json

from django.http import JsonResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST


@csrf_exempt
@require_POST
def hook(request):
    body = request.body  # raw bytes
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify(body, sig, SECRET):
        return HttpResponseForbidden("invalid signature")
    event = json.loads(body)
    # … dispatch on event["type"] …
    return JsonResponse({"ok": True})
```
</CodeGroup>

## ટૂલ કૉલ્સની ચકાસણી

જ્યારે એજન્ટ તમારી
[ફંક્શન ટૂલ્સ](/gu/tools/overview)માંથી કોઈ એકને સીધું ચલાવે છે
(ટૂલમાં `endpoint` હોય છે), ત્યારે વિનંતિ તમારા કૉન્ફિગર કરેલા
`endpoint.headers` સાથે બે ThunderPhone હેડર્સ ધરાવે છે:

- `X-ThunderPhone-Call-ID` — લાઇવ કૉલનું આંકડાકીય આઈડી.
- `X-ThunderPhone-Signature` — ચોક્કસ વિનંતિ-બોડી બાઇટ્સ પર તમારા
  **સંસ્થા-સ્તરીય વેબહૂક સિક્રેટ** વડે કી કરાયેલ HMAC-SHA256.

એ જ `verify()` હેલ્પર કોઈ ફેરફાર વિના કાર્ય કરે છે, પરંતુ બે ખાસ બાબતો છે:

1. **`GET` / `DELETE` ટૂલ્સમાં કોઈ બોડી હોતી નથી.** આર્ગ્યુમેન્ટ્સ ક્વેરી
   પેરામીટર્સ તરીકે જાય છે, અને સહીની ગણતરી **ખાલી બાઇટ
   સ્ટ્રિંગ** પર થાય છે — એટલે `verify(b"", sig, secret)` (Python) અથવા
   `verify(Buffer.alloc(0), sig, secret)` (Node). ક્વેરી સ્ટ્રિંગને હૅશ
   **ન કરો**.
2. **લેગસી વેબહૂક કૉન્ફિગર ન હોય તેવી સંસ્થાઓ પાસે સંસ્થા સિક્રેટ હોતું નથી.**
   તે સ્થિતિમાં ટૂલ કૉલ્સમાં માત્ર `X-ThunderPhone-Call-ID` હોય છે અને કોઈ
   સહી હેડર હોતું નથી. સાઇનિંગ સિક્રેટ મેળવવા માટે લેગસી વેબહૂક
   (`PUT /v1/webhook`) કૉન્ફિગર કરો, અથવા `endpoint.headers` મારફતે તમારા
   પોતાના હેડરથી ટૂલ કૉલ્સનું પ્રમાણીકરણ કરો.

```python
@app.post("/tools/search-appointments")
async def tool(request: Request):
    body = await request.body()  # b"" for GET/DELETE tools
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    call_id = request.headers.get("X-ThunderPhone-Call-ID", "")
    if not verify(body, sig, ORG_WEBHOOK_SECRET):
        raise HTTPException(status_code=401)
    args = json.loads(body)
    ...
```

વેબહૂક-**મોડ** ટૂલ ડિસ્પૅચ (`endpoint` વિનાની ટૂલ્સ, જે તમારી સંસ્થાના
વેબહૂક પર `telephony.tool` / `web.tool` તરીકે પહોંચાડવામાં આવે છે) સામાન્ય
સહી કરાયેલ વેબહૂક છે — ઉપરોક્ત માનક પદ્ધતિ લાગુ પડે છે. બંને વિનંતિ
આકારો માટે [ફંક્શન ટૂલ્સ](/gu/tools/overview) જુઓ.

## સામાન્ય ખામીઓ

<AccordionGroup>
  <Accordion title="ડિફૉલ્ટ ફોર્મેટિંગ સાથે ફરીથી સિરિયલાઇઝ કરવું">
    બોડી પાર્સ કરીને તેને તમારી JSON લાઇબ્રેરીના ડિફૉલ્ટ્સ સાથે
    ફરીથી ડમ્પ કરવાથી (`,` / `:` પછી ખાલી જગ્યાઓ, દાખલ કરેલા ક્રમમાં કીઓ)
    અલગ બાઇટ્સ બને છે અને HMAC તૂટી જાય છે. રૉ બોડી ચકાસો — અથવા જો
    તમારે ફરીથી સિરિયલાઇઝ કરવું જ હોય, તો અમારા કેનોનિકલ સ્વરૂપને ચોક્કસ રીતે મેળવો:
    સૉર્ટ કરેલી કીઓ, કોમ્પેક્ટ સેપરેટર્સ, UTF-8.
  </Accordion>

  <Accordion title="ફ્રેમવર્ક આપમેળે JSON પાર્સ કરે છે">
    Expressનું `express.json()` મિડલવેર બોડી સ્ટ્રીમ વાપરી લે છે
    અને તમે રૉ બાઇટ્સ ગુમાવો છો. ખાસ કરીને વેબહૂક રૂટ પર `express.raw()` વાપરો,
    અથવા પ્રી-મિડલવેરમાં રૉ બોડી બફર કરો.
    NestJS / Koa માટે પણ આ જ વાત લાગુ પડે છે — તેમના "રૉ બોડી" દસ્તાવેજો તપાસો.
  </Accordion>

  <Accordion title="ટાઇમિંગ-અસુરક્ષિત સરખામણી">
    JSમાં `expected === signature` અથવા Pythonમાં `expected == signature`
    ટાઇમિંગ-વેરિએબલ સરખામણીઓ છે. અનુક્રમે `crypto.timingSafeEqual`
    અથવા `hmac.compare_digest` વાપરો. કામગીરીમાં તફાવત
    નગણ્ય છે.
  </Accordion>

  <Accordion title="ટૂલ એન્ડપૉઇન્ટ્સ માટે ખોટું સિક્રેટ">
    સીધા ટૂલ-એન્ડપૉઇન્ટ કૉલ્સ પર **સંસ્થા-સ્તરનું વેબહૂક
    સિક્રેટ** (`GET /v1/webhook`) વડે સહી કરવામાં આવે છે — 
    `/v1/developer/webhook-endpoints` ના કોઈપણ પ્રતિ-એન્ડપૉઇન્ટ સિક્રેટથી નહીં. એ જ `verify()`
    ફંક્શન ફરી વાપરો, પરંતુ ટૂલ રૂટ્સ પર તેને સંસ્થાનું સિક્રેટ આપો તેની ખાતરી કરો.
  </Accordion>

  <Accordion title="GET/DELETE ટૂલ્સ પર ક્વેરી સ્ટ્રિંગને હૅશ કરવી">
    બોડી વિનાની ટૂલ મેથડ્સ માટે સહી ખાલી બાઇટ
    સ્ટ્રિંગને આવરી લે છે, જેથી એક સર્વવ્યાપક રીત જળવાઈ રહે: રૉ રિક્વેસ્ટ બોડીનું HMAC કરો,
    તે ગમે તે હોય. URL અથવા ક્વેરી સ્ટ્રિંગને હૅશ કરવાથી ક્યારેય મેળ નહીં પડે.
  </Accordion>

  <Accordion title="અસંગતતા પર 401 પરત ન કરવું">
    ચકાસણી નિષ્ફળ જાય ત્યારે 200 પરત કરવાથી હેન્ડલર રીપ્લે
    લક્ષ્ય બને છે. ચકાસણી નિષ્ફળ જાય તો હંમેશા નૉન-2xx પ્રતિસાદ આપો.
  </Accordion>
</AccordionGroup>

---

## આગળનાં પગલાં

<CardGroup cols={2}>
  <Card title="વેબહૂક્સનો પરિચય" icon="bolt" href="/gu/webhooks/overview">
    ડિલિવરી સિમેન્ટિક્સ, પુનઃપ્રયાસો, સ્રોત IP સરનામાંઓ.
  </Card>
  <Card title="વેબહૂક એન્ડપૉઇન્ટ્સ" icon="plug" href="/gu/webhooks/endpoints">
    બહુવિધ URL મેનેજ કરો, સિક્રેટ્સ રોટેટ કરો.
  </Card>
  <Card title="ફંક્શન ટૂલ્સ" icon="screwdriver-wrench" href="/gu/tools/overview">
    બે ટૂલ-ઇન્વોકેશન પાથ્સ અને તેમના રિક્વેસ્ટ આકારો.
  </Card>
  <Card title="ટૂલ ઇન્ટિગ્રેશન્સ" icon="wrench" href="/gu/guides/build-tool-integration">
    સંપૂર્ણ ટૂલ-આધારિત ઇન્ટિગ્રેશન છેડેથી છેડે બનાવો.
  </Card>
</CardGroup>
