> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook - signature verification

> RSA signature verification method and code examples for Webhook requests.

Each Webhook request header includes `X-SIGNATURE`, which is signed using the Waffo private key. Merchants must use the Waffo public key to verify the signature.

## Get the Waffo public key

Log in to **Merchant Portal** → **Integration** menu to view and copy the Waffo public key.

> [!NOTE]
>
> **Dev** or **Admin** role permissions are required to access this page.

## Recommended approach: use the SDK

The SDK’s `handleWebhook()` method automatically performs signature verification, event parsing, routing, and response body construction:

```typescript Node.js theme={null}
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  const body = req.body.toString();
  const signature = req.headers['x-signature'] as string;

  const result = await waffo.webhook().handleWebhook(body, signature);

  res.setHeader('Content-Type', 'application/json');
  res.status(200).send(result.responseBody);
});
```

```go Go theme={null}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    signature := r.Header.Get("X-SIGNATURE")

    result := handler.HandleWebhook(string(body), signature)

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(200)
    w.Write([]byte(result.ResponseBody))
}
```

## Manual verification

If you need to handle this manually (without the SDK), the verification steps are:

**1. Get the signature**

Get `X-SIGNATURE` from the request header.

**2. Get the raw body**

Get the raw request body string (do not JSON parse and then stringify).

**3. Verify the signature**

Use the Waffo public key + SHA256WithRSA to verify the signature.

**4. Process the event**

Process the event after the signature verification passes.

### Manual examples

```typescript Node.js theme={null}
import { createVerify } from 'crypto';

function verifyWaffoSignature(body: string, signature: string): boolean {
  const verify = createVerify('SHA256');
  verify.update(body);
  return verify.verify(process.env.WAFFO_PUBLIC_KEY!, signature, 'base64');
}

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString();
  const signature = req.headers['x-signature'] as string;

  if (!verifyWaffoSignature(body, signature)) {
    const failedBody = JSON.stringify({ message: 'failed' });
    return res.status(200).send(failedBody);
  }

  // Process the event...

  const successBody = JSON.stringify({ message: 'success' });
  res.status(200).send(successBody);
});
```

```go Go theme={null}
import (
    "crypto"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "encoding/json"
    "io"
    "net/http"
    "os"
)

// Verify the Waffo signature
func verifyWaffoSignature(body string, signature string) bool {
    pubKeyPEM := []byte(os.Getenv("WAFFO_PUBLIC_KEY"))
    block, _ := pem.Decode(pubKeyPEM)
    pubKey, _ := x509.ParsePKIXPublicKey(block.Bytes)

    sig, _ := base64.StdEncoding.DecodeString(signature)
    hash := sha256.Sum256([]byte(body))
    err := rsa.VerifyPKCS1v15(pubKey.(*rsa.PublicKey), crypto.SHA256, hash[:], sig)
    return err == nil
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    signature := r.Header.Get("X-SIGNATURE")

    if !verifyWaffoSignature(string(body), signature) {
        failedBody, _ := json.Marshal(map[string]string{"message": "failed"})
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(200)
        w.Write(failedBody)
        return
    }

    // Process the event...

    successBody, _ := json.Marshal(map[string]string{"message": "success"})
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(200)
    w.Write(successBody)
}
```

## Notes

> [!WARNING]
>
> * **You must verify the signature before processing the event**. Do not respond first and then verify.
> * Verify the signature using the raw request body; do not JSON parse and then stringify.
> * The SDK provides a complete webhook handling pipeline. Using the SDK is recommended over manual handling.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.