---
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 ਨੰਬਰਾਂ ਨੂੰ
ਮੁੜ-ਰੂਪਾਂਤਰਿਤ ਕਰਨ ਦੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੋਂ ਪ੍ਰਭਾਵਿਤ ਨਹੀਂ ਹੁੰਦੀ।

## ਕਿਹੜਾ ਸੀਕ੍ਰੇਟ?

| ਸਰੋਤ | ਸੀਕ੍ਰੇਟ |
|--------|--------|
| [ਵੈੱਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ](/pa/webhooks/endpoints) (`/v1/developer/webhook-endpoints`) | ਬਣਾਉਣ ਵੇਲੇ ਇੱਕ ਵਾਰ ਵਾਪਸ ਕੀਤਾ ਗਿਆ ਪ੍ਰਤੀ-ਐਂਡਪੁਆਇੰਟ `secret` (48 ਹੈਕਸ ਅੱਖਰ) |
| [ਪੁਰਾਣਾ ਸਿੰਗਲ-URL ਵੈੱਬਹੁੱਕ](/api-reference/organizations#legacy-single-url-webhook) | `GET /v1/webhook` ਉੱਤੇ ਵਾਪਸ ਕੀਤਾ ਗਿਆ ਪ੍ਰਤੀ-ਸੰਗਠਨ `secret` |
| [ਟੂਲ-ਐਂਡਪੁਆਇੰਟ ਇਨਵੋਕੇਸ਼ਨ](/pa/tools/overview) (ਤੁਹਾਡੇ `endpoint.url` ਨੂੰ ਸਿੱਧੀ ਕਾਲ) | **ਸੰਗਠਨ-ਪੱਧਰ ਦਾ ਵੈੱਬਹੁੱਕ ਸੀਕ੍ਰੇਟ** (ਪੁਰਾਣੇ ਸਿੰਗਲ-URL ਵੈੱਬਹੁੱਕ ਵਾਲਾ ਹੀ) — ਪ੍ਰਤੀ-ਐਂਡਪੁਆਇੰਟ ਸੀਕ੍ਰੇਟ ਨਹੀਂ |

ਸੀਕ੍ਰੇਟ ਨੂੰ ਆਪਣੇ ਸੀਕ੍ਰੇਟ ਮੈਨੇਜਰ ਜਾਂ env var ਵਿੱਚ ਸਟੋਰ ਕਰੋ — ਇਸ ਨੂੰ ਕਦੇ ਵੀ ਕਮਿਟ ਨਾ ਕਰੋ।

## ਹਵਾਲਾ ਇੰਪਲੀਮੈਂਟੇਸ਼ਨ

ਸਾਰੇ ਚਾਰ ਕੱਚੀ ਬੇਨਤੀ ਬਾਡੀ ਦੀ ਤਸਦੀਕ ਕਰਦੇ ਹਨ:

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

## ਟੂਲ ਕਾਲਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨਾ

ਜਦੋਂ ਏਜੰਟ ਤੁਹਾਡੇ ਕਿਸੇ
[ਫੰਕਸ਼ਨ ਟੂਲ](/pa/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` ਵਜੋਂ ਭੇਜੇ ਜਾਂਦੇ ਹਨ) ਇੱਕ ਆਮ
ਸਾਈਨ ਕੀਤਾ ਵੈੱਬਹੁੱਕ ਹੈ — ਉੱਪਰ ਦਿੱਤੀ ਮਿਆਰੀ ਵਿਧੀ ਲਾਗੂ ਹੁੰਦੀ ਹੈ। ਦੋਵੇਂ ਬੇਨਤੀ ਰੂਪਾਂ ਲਈ
[ਫੰਕਸ਼ਨ ਟੂਲ](/pa/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="/pa/webhooks/overview">
    ਡਿਲਿਵਰੀ ਅਰਥ-ਵਿਵਸਥਾ, ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ਾਂ, ਸਰੋਤ IP।
  </Card>
  <Card title="ਵੈੱਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ" icon="plug" href="/pa/webhooks/endpoints">
    ਕਈ URL ਪ੍ਰਬੰਧਿਤ ਕਰੋ, ਸੀਕਰਟ ਰੋਟੇਟ ਕਰੋ।
  </Card>
  <Card title="ਫੰਕਸ਼ਨ ਟੂਲਜ਼" icon="screwdriver-wrench" href="/pa/tools/overview">
    ਦੋ ਟੂਲ-ਇਨਵੋਕੇਸ਼ਨ ਪਾਥ ਅਤੇ ਉਹਨਾਂ ਦੇ ਰਿਕਵੇਸਟ ਆਕਾਰ।
  </Card>
  <Card title="ਟੂਲ ਇੰਟੀਗ੍ਰੇਸ਼ਨ" icon="wrench" href="/pa/guides/build-tool-integration">
    ਸ਼ੁਰੂ ਤੋਂ ਅੰਤ ਤੱਕ ਪੂਰੀ ਟੂਲ-ਸਹਾਇਤਿਤ ਇੰਟੀਗ੍ਰੇਸ਼ਨ ਬਣਾਓ।
  </Card>
</CardGroup>
