---
title: "ویب ہُک دستخطوں کی تصدیق کریں"
description: "ThunderPhone کی بھیجی گئی ہر ویب ہُک اور ٹول درخواست پر دستخط ہوتے ہیں۔ یہاں موجود طریقۂ کار کے ذریعے دستخط کی ایک بار تصدیق کریں، پھر اپنے چلائے جانے والے ہر اینڈ پوائنٹ پر یہی جانچ دوبارہ استعمال کریں۔"
---

ہم آپ کے سرور کو بھیجنے والی ہر درخواست — ویب ہک ڈیلیوریز اور
ٹول اینڈپوائنٹ انوووکیشنز — میں
`X-ThunderPhone-Signature` ہیڈر میں ایک HMAC-SHA256 دستخط شامل ہوتا ہے۔ تصدیق ایک بار درست کریں اور
اسی ہیلپر کو ہر ہینڈلر میں استعمال کریں۔

## الگورتھم

1. درخواست کی **خام** باڈی پڑھیں — بالکل وہی بائٹس جو ہم نے آپ کو POST کیے ہیں۔
2. `hmac_sha256(secret, body).hexdigest()` کا حساب کریں۔
3. `X-ThunderPhone-Signature` کے مقابلے میں **مستقل وقت** میں موازنہ کریں۔
   (سادہ اسٹرنگ موازنہ وقت سے متعلق معلومات ظاہر کرتا ہے۔)

ہم بالکل انہی بائٹس پر دستخط کرتے ہیں جو ہم منتقل کرتے ہیں، اس لیے خام باڈی کی
تصدیق ہمیشہ کام کرتی ہے۔ یہ بائٹس پے لوڈ کی **کینونیکل JSON سیریلائزیشن**
بھی ہوتی ہیں — کلیدیں حروفِ تہجی کے مطابق ترتیب دی گئی، مختصر جداکار
(بغیر اسپیس کے `,` اور `:`)، UTF-8۔ جب آپ کا فریم ورک صرف پارس شدہ JSON فراہم کرے تو
یہ آپ کو دوسرا، مکمل طور پر مساوی طریقہ دیتا ہے:
کینونیکل انداز میں دوبارہ سیریلائز کریں اور اس کا HMAC بنائیں۔

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

خام باڈی کو ترجیح دیں — اس میں ایک مرحلہ کم ہے اور کچھ زبانوں میں JSON نمبروں کے
راؤنڈ ٹرپ کی خصوصیات سے محفوظ رہتی ہے۔

## کون سا secret؟

| ماخذ | Secret |
|--------|--------|
| [ویب ہک اینڈپوائنٹ](/ur/webhooks/endpoints) (`/v1/developer/webhook-endpoints`) | ہر اینڈپوائنٹ کا `secret` (48 hex حروف) جو تخلیق کے وقت ایک بار واپس کیا جاتا ہے |
| [پرانا سنگل-URL ویب ہک](/api-reference/organizations#legacy-single-url-webhook) | ہر تنظیم کا `secret` جو `GET /v1/webhook` پر واپس کیا جاتا ہے |
| [ٹول اینڈپوائنٹ انوووکیشن](/ur/tools/overview) (آپ کے `endpoint.url` پر براہِ راست کال) | **تنظیمی سطح کا ویب ہک secret** (وہی جو پرانے سنگل-URL ویب ہک کے لیے ہے) — ہر اینڈپوائنٹ کا secret نہیں |

secret کو اپنے secret manager یا env var میں محفوظ کریں — اسے کبھی commit نہ کریں۔

## حوالہ جاتی نفاذ

چاروں خام درخواست باڈی کی تصدیق کرتے ہیں:

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

## ٹول کالز کی تصدیق

جب ایجنٹ آپ کے
[فنکشن ٹولز](/ur/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` کے طور پر پہنچائے جاتے ہیں) ایک عام
دستخط شدہ ویب ہک ہے — اوپر دیا گیا معیاری طریقہ لاگو ہوتا ہے۔ درخواست کی
دونوں ساختوں کے لیے [فنکشن ٹولز](/ur/tools/overview) دیکھیں۔

## عام خامیاں

<AccordionGroup>
  <Accordion title="ڈیفالٹ فارمیٹنگ کے ساتھ دوبارہ سیریلائز کرنا">
    باڈی کو پارس کر کے اپنی JSON لائبریری کے ڈیفالٹس کے ساتھ دوبارہ ڈمپ کرنے سے
    (`,` / `:` کے بعد اسپیسز، اندراج کی ترتیب کے مطابق کلیدیں) مختلف
    بائٹس بنتے ہیں اور HMAC ناکام ہو جاتا ہے۔ خام باڈی کی تصدیق کریں — یا اگر
    آپ کو دوبارہ سیریلائز کرنا ضروری ہو تو ہماری معیاری شکل سے بالکل مطابقت رکھیں:
    ترتیب شدہ کلیدیں، مختصر جداکار، UTF-8۔
  </Accordion>

  <Accordion title="فریم ورک JSON کو خودکار طور پر پارس کرتا ہے">
    Express کا `express.json()` middleware باڈی اسٹریم استعمال کر لیتا ہے
    اور آپ خام بائٹس کھو دیتے ہیں۔ خاص طور پر webhook روٹ پر `express.raw()` استعمال کریں،
    یا pre-middleware میں خام باڈی کو بفر کریں۔
    NestJS / Koa کے لیے بھی یہی صورتحال ہے — ان کی "raw body" دستاویزات دیکھیں۔
  </Accordion>

  <Accordion title="ٹائمنگ کے لحاظ سے غیر محفوظ موازنہ">
    JS میں `expected === signature` یا Python میں `expected == signature`
    ٹائمنگ کے لحاظ سے متغیر موازنے ہیں۔ بالترتیب `crypto.timingSafeEqual`
    یا `hmac.compare_digest` استعمال کریں۔ کارکردگی میں فرق نہ ہونے کے برابر ہے۔
  </Accordion>

  <Accordion title="ٹول اینڈ پوائنٹس کے لیے غلط secret">
    براہ راست ٹول اینڈ پوائنٹ کالز پر **تنظیم کی سطح کا webhook
    secret** (`GET /v1/webhook`) کے ساتھ دستخط ہوتے ہیں — نہ کہ
    `/v1/developer/webhook-endpoints` کے کسی فی اینڈ پوائنٹ secret کے ساتھ۔ وہی `verify()`
    فنکشن دوبارہ استعمال کریں، لیکن یقینی بنائیں کہ آپ ٹول روٹس پر اسے تنظیم کا secret فراہم کرتے ہیں۔
  </Accordion>

  <Accordion title="GET/DELETE ٹولز میں query string کو hash کرنا">
    باڈی کے بغیر ٹول میتھڈز کے لیے دستخط خالی بائٹ
    اسٹرنگ پر مشتمل ہوتا ہے، جس سے ایک ہی عالمگیر طریقہ برقرار رہتا ہے: خام درخواست باڈی پر HMAC لگائیں،
    خواہ وہ کچھ بھی ہو۔ URL یا query string کو hash کرنے سے کبھی مطابقت نہیں ہوگی۔
  </Accordion>

  <Accordion title="مطابقت نہ ہونے پر 401 واپس نہ کرنا">
    ناکام تصدیق پر 200 واپس کرنا ہینڈلر کو replay
    کا ہدف بناتا ہے۔ تصدیق ناکام ہونے پر ہمیشہ 2xx کے علاوہ جواب دیں۔
  </Accordion>
</AccordionGroup>

---

## اگلے مراحل

<CardGroup cols={2}>
  <Card title="Webhook کا جائزہ" icon="bolt" href="/ur/webhooks/overview">
    ترسیل کے اصول، دوبارہ کوششیں، سورس IPs۔
  </Card>
  <Card title="Webhook اینڈ پوائنٹس" icon="plug" href="/ur/webhooks/endpoints">
    متعدد URLs کا انتظام کریں، secrets تبدیل کریں۔
  </Card>
  <Card title="فنکشن ٹولز" icon="screwdriver-wrench" href="/ur/tools/overview">
    ٹول استعمال کرنے کے دو راستے اور ان کی درخواست کی ساختیں۔
  </Card>
  <Card title="ٹول انٹیگریشنز" icon="wrench" href="/ur/guides/build-tool-integration">
    ابتدا سے انتہا تک مکمل ٹول پر مبنی انٹیگریشن بنائیں۔
  </Card>
</CardGroup>
