---
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 எண்களை
மீண்டும் மாற்றும் நுணுக்கங்களிலிருந்தும் பாதுகாப்பானது.

## எந்த ரகசியம்?

| மூலம் | ரகசியம் |
|--------|--------|
| [வெப்ஹுக் எண்ட்பாயிண்ட்](/ta/webhooks/endpoints) (`/v1/developer/webhook-endpoints`) | உருவாக்கும்போது ஒருமுறை வழங்கப்படும் எண்ட்பாயிண்ட்-ஒன்றுக்கான `secret` (48 ஹெக்ஸ் எழுத்துகள்) |
| [பழைய ஒற்றை-URL வெப்ஹுக்](/api-reference/organizations#legacy-single-url-webhook) | `GET /v1/webhook`-இல் வழங்கப்படும் நிறுவனம்-ஒன்றுக்கான `secret` |
| [கருவி-எண்ட்பாயிண்ட் அழைப்பு](/ta/tools/overview) (உங்கள் `endpoint.url`-க்கு நேரடி அழைப்பு) | **நிறுவன-நிலை வெப்ஹுக் ரகசியம்** (பழைய ஒற்றை-URL வெப்ஹுக்கில் உள்ளதே) — எண்ட்பாயிண்ட்-ஒன்றுக்கான ரகசியம் அல்ல |

ரகசியத்தை உங்கள் ரகசிய மேலாளர் அல்லது env மாறியில் சேமியுங்கள் — அதை ஒருபோதும் கமிட் செய்யாதீர்கள்.

## குறிப்பு செயலாக்கங்கள்

நான்கும் மூல கோரிக்கை உடலைச் சரிபார்க்கின்றன:

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

## டூல் அழைப்புகளைச் சரிபார்த்தல்

ஏஜென்ட் உங்கள் [செயல்பாட்டு டூல்களில்](/ta/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` ஆக
அனுப்பப்படுபவை) ஒரு சாதாரண கையொப்பமிடப்பட்ட வெப்ஹூக் ஆகும் — மேலே உள்ள
நிலையான முறையே பொருந்தும். இரண்டு கோரிக்கை வடிவங்களுக்கும்
[செயல்பாட்டு டூல்களை](/ta/tools/overview) பார்க்கவும்.

## பொதுவான சிக்கல்கள்

<AccordionGroup>
  <Accordion title="இயல்புநிலை வடிவமைப்புடன் மீண்டும் வரிசைப்படுத்துதல்">
    body-ஐப் பாகுபடுத்தி, உங்கள் JSON நூலகத்தின் இயல்புநிலை அமைப்புகளுடன்
    (`,` / `:`-க்குப் பின் இடைவெளிகள், சேர்க்கை வரிசையிலான key-கள்) மீண்டும் வெளியிடுவது
    வேறுபட்ட byte-களை உருவாக்கி HMAC-ஐ செயலிழக்கச் செய்கிறது. மூல body-ஐச் சரிபார்க்கவும் — அல்லது
    நீங்கள் மீண்டும் வரிசைப்படுத்த வேண்டியிருந்தால், எங்கள் நியம வடிவத்துடன் துல்லியமாகப் பொருத்தவும்:
    வரிசைப்படுத்தப்பட்ட key-கள், சுருக்கமான பிரிப்பான்கள், UTF-8.
  </Accordion>

  <Accordion title="Framework தானாக JSON-ஐப் பாகுபடுத்துகிறது">
    Express-இன் `express.json()` middleware body stream-ஐப் பயன்படுத்திவிடும்;
    இதனால் மூல byte-களை இழப்பீர்கள். webhook route-க்கு மட்டும் `express.raw()`-ஐப் பயன்படுத்தவும்,
    அல்லது pre-middleware-இல் மூல body-ஐ buffer செய்யவும்.
    NestJS / Koa-விற்கும் இதுவே பொருந்தும் — அவற்றின் "raw body" ஆவணங்களைப் பார்க்கவும்.
  </Accordion>

  <Accordion title="நேர-பாதுகாப்பற்ற ஒப்பீடு">
    JS-இல் `expected === signature` அல்லது Python-இல் `expected == signature`
    நேரத்தைப் பொறுத்து மாறும் ஒப்பீடுகள். முறையே `crypto.timingSafeEqual`
    அல்லது `hmac.compare_digest`-ஐப் பயன்படுத்தவும். செயல்திறன் வேறுபாடு
    இல்லை.
  </Accordion>

  <Accordion title="Tool endpoint-களுக்கான தவறான ரகசியம்">
    நேரடி tool-endpoint அழைப்புகள் **org-நிலை webhook
    ரகசியம்** (`GET /v1/webhook`) மூலம் கையொப்பமிடப்படுகின்றன — 
    `/v1/developer/webhook-endpoints`-இலுள்ள endpoint-தனிப்பட்ட ரகசியம் மூலம் அல்ல. அதே `verify()`
    செயல்பாட்டை மீண்டும் பயன்படுத்தவும்; ஆனால் tool route-களில் அதற்கு org ரகசியத்தையே வழங்குவதை உறுதிசெய்யவும்.
  </Accordion>

  <Accordion title="GET/DELETE tool-களில் query string-ஐ hash செய்தல்">
    body இல்லாத tool முறைகளில், கையொப்பம் வெற்று byte
    string-ஐ உள்ளடக்கும்; இதனால் ஒரே பொதுவான நடைமுறை கிடைக்கிறது: மூல கோரிக்கை body எதுவாக இருந்தாலும்,
    அதையே HMAC செய்யவும். URL அல்லது query string-ஐ hash செய்தால் ஒருபோதும் பொருந்தாது.
  </Accordion>

  <Accordion title="பொருந்தாதபோது 401-ஐ வழங்காமை">
    சரிபார்ப்பு தோல்வியடைந்தபோது 200-ஐ வழங்குவது handler-ஐ replay
    இலக்காக மாற்றுகிறது. சரிபார்ப்பு தோல்வியடைந்தால் எப்போதும் 2xx அல்லாத பதிலை வழங்கவும்.
  </Accordion>
</AccordionGroup>

---

## அடுத்த படிகள்

<CardGroup cols={2}>
  <Card title="Webhook-கள் மேலோட்டம்" icon="bolt" href="/ta/webhooks/overview">
    விநியோக அர்த்தவியல், மீண்டும் முயற்சிகள், மூல IP-கள்.
  </Card>
  <Card title="Webhook endpoint-கள்" icon="plug" href="/ta/webhooks/endpoints">
    பல URL-களை நிர்வகிக்கவும், ரகசியங்களை மாற்றவும்.
  </Card>
  <Card title="Function Tools" icon="screwdriver-wrench" href="/ta/tools/overview">
    இரண்டு tool-invocation பாதைகள் மற்றும் அவற்றின் கோரிக்கை வடிவங்கள்.
  </Card>
  <Card title="Tool ஒருங்கிணைப்புகள்" icon="wrench" href="/ta/guides/build-tool-integration">
    தொடக்கம் முதல் முடிவு வரை முழுமையான tool-ஆதரவு ஒருங்கிணைப்பை உருவாக்கவும்.
  </Card>
</CardGroup>
