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

# Build Your Webhook Endpoint

> Minimal, fast, and secure callback handler patterns.

## Requirements

* **HTTPS** endpoint with a stable URL.
* Accept `POST` with `Content-Type: application/json`.
* Return **2xx** quickly (store → ack → process async).

Use [Verify signature](/pages/webhooks/verify-signature) if you want to validate `validation_hash`.

## Example handlers

### Node.js (Express)

```ts theme={null}
import crypto from "node:crypto";
import express from "express";

const app = express();
app.use(express.json({ limit: "256kb" }));

app.post("/webhooks/payments", async (req, res) => {
  const body = req.body;

  // 1) Basic validation
  if (!body?.status || !body?.results?.payment_uuid) {
    return res.status(400).send("Invalid payload");
  }

  // 2) Verify integrity (optional but recommended)
  // compute validation_hash as shown in Verify Signature page

  // 3) Idempotent persist
  await upsertByPaymentUuid(body.results.payment_uuid, body);

  // 4) Respond fast
  return res.status(202).send("Accepted");
});

app.listen(3000);
```

### Python (FastAPI)

```py theme={null}
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()

@app.post("/webhooks/payments")
async def webhook(req: Request):
  body = await req.json()
  if "status" not in body or "results" not in body:
    raise HTTPException(400, "Invalid payload")

  # verify hash (optional), then persist idempotently
  save_or_update(body["results"]["payment_uuid"], body)

  return {"ok": True}
```

### PHP (Laravel)

```php theme={null}
Route::post('/webhooks/payments', function (Illuminate\Http\Request $request) {
    $data = $request->json()->all();
    if (!isset($data['status'], $data['results']['payment_uuid'])) {
        return response('Invalid payload', 400);
    }

    // verify hash (optional), then idempotent save
    upsert_by_payment_uuid($data['results']['payment_uuid'], $data);

    return response()->json(['ok' => true], 202);
});
```
