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

# Webhooks

> Receive real-time event notifications via HTTP callbacks

Webhooks let Yativo push event data to your server the moment something happens, instead of you polling the API. When an event fires, Yativo sends an HTTP POST to your configured endpoint with a signed JSON payload.

***

## Create a Webhook

**POST** `/webhooks/create`

<ParamField body="webhook_name" type="string" required>
  A human-readable name for this webhook endpoint (e.g. "Production Deposits").
</ParamField>

<ParamField body="webhook_url" type="string" required>
  The HTTPS or HTTP URL Yativo will POST events to.
</ParamField>

<ParamField body="webhook_secret" type="string" required>
  A secret string you generate. Yativo uses it to sign every delivery so your server can verify the payload is genuine.
</ParamField>

<ParamField body="whitelist_ips" type="array">
  Optional allowlist of source IP addresses. When set, Yativo will only deliver events from these IPs. Leave empty to allow all.
</ParamField>

```bash cURL theme={null}
curl -X POST https://crypto-api.yativo.com/api/v1/webhooks/create \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_name": "Production webhook",
    "webhook_url": "https://your-server.com/webhooks/yativo",
    "webhook_secret": "my_super_secret_string"
  }'
```

```json Response theme={null}
{
  "status": "success",
  "status_code": "200",
  "message": "successful request",
  "data": {
    "_id": "664abc123def456789000001",
    "webhook_name": "Production webhook",
    "webhook_url": "https://your-server.com/webhooks/yativo",
    "user_id": "664abc123def456789000000",
    "status": "active",
    "createdAt": "2026-03-26T10:00:00.000Z"
  }
}
```

<Warning>
  Store `webhook_secret` securely (e.g. in an environment variable). Use it to verify the `X-Webhook-Signature` header on every incoming delivery.
</Warning>

***

## List Webhooks

**GET** `/webhooks`

```bash cURL theme={null}
curl -X GET https://crypto-api.yativo.com/api/v1/webhooks \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

***

## Update a Webhook

**POST** `/webhooks/edit`

Pass `webhook_id` plus any fields you want to change. Omitted fields keep their current value.

<ParamField body="webhook_id" type="string" required>
  The `_id` of the webhook to update.
</ParamField>

<ParamField body="webhook_name" type="string">Updated name.</ParamField>
<ParamField body="webhook_url" type="string">Updated delivery URL.</ParamField>
<ParamField body="webhook_secret" type="string">Rotated secret.</ParamField>
<ParamField body="whitelist_ips" type="array">Updated IP allowlist.</ParamField>
<ParamField body="status" type="string">Set to `"active"` or `"inactive"` to pause/resume delivery.</ParamField>

```bash cURL theme={null}
curl -X POST https://crypto-api.yativo.com/api/v1/webhooks/edit \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_id": "664abc123def456789000001",
    "status": "inactive"
  }'
```

***

## Delete a Webhook

**POST** `/webhooks/delete`

<ParamField body="webhook_id" type="string" required>
  The `_id` of the webhook to delete.
</ParamField>

```bash cURL theme={null}
curl -X POST https://crypto-api.yativo.com/api/v1/webhooks/delete \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_id": "664abc123def456789000001"
  }'
```

***

## View Event Logs

**GET** `/webhook/get-event-logs`

Returns a log of events processed through the webhook system.

```bash cURL theme={null}
curl -X GET https://crypto-api.yativo.com/api/v1/webhook/get-event-logs \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

***

## Signature Verification

Every webhook delivery includes an `X-Webhook-Signature` header. This is an HMAC-SHA256 signature of the raw request body, computed using your `webhook_secret`.

**Always verify the signature** before processing events. This confirms the request genuinely came from Yativo and was not tampered with.

<Tabs>
  <Tab title="TypeScript / Node.js">
    ```typescript theme={null}
    import crypto from 'crypto';
    import { Request, Response } from 'express';

    const WEBHOOK_SECRET = process.env.YATIVO_WEBHOOK_SECRET!;

    export function verifyWebhook(req: Request, res: Response, next: Function) {
      const signature = req.headers['x-webhook-signature'] as string;
      const rawBody = (req as any).rawBody as Buffer; // ensure body parser preserves raw bytes

      if (!signature || !rawBody) {
        return res.status(400).json({ error: 'Missing signature or body' });
      }

      const expected = crypto
        .createHmac('sha256', WEBHOOK_SECRET)
        .update(rawBody)
        .digest('hex');

      const isValid = crypto.timingSafeEqual(
        Buffer.from(signature, 'hex'),
        Buffer.from(expected, 'hex')
      );

      if (!isValid) {
        return res.status(401).json({ error: 'Invalid signature' });
      }

      next();
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import hmac
    import hashlib
    import os
    from flask import request, abort

    WEBHOOK_SECRET = os.environ['YATIVO_WEBHOOK_SECRET']

    def verify_webhook():
        signature = request.headers.get('X-Webhook-Signature', '')
        raw_body = request.get_data()

        expected = hmac.new(
            WEBHOOK_SECRET.encode('utf-8'),
            raw_body,
            hashlib.sha256
        ).hexdigest()

        if not hmac.compare_digest(signature, expected):
            abort(401, 'Invalid signature')
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    <?php
    function verifyWebhook(string $rawBody, string $secret): bool {
        $signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
        $expected = hash_hmac('sha256', $rawBody, $secret);
        return hash_equals($expected, $signature);
    }

    $rawBody = file_get_contents('php://input');
    $secret = getenv('YATIVO_WEBHOOK_SECRET');

    if (!verifyWebhook($rawBody, $secret)) {
        http_response_code(401);
        exit('Invalid signature');
    }

    $event = json_decode($rawBody, true);
    // process $event...
    ```
  </Tab>
</Tabs>

***

## Webhook Payload Structure

All webhook payloads share the same envelope:

```json theme={null}
{
  "id": "evt_01abc123",
  "type": "deposit.confirmed",
  "created_at": "2026-03-26T10:05:00Z",
  "data": {
    // event-specific fields
  }
}
```

***

## Best Practices

<Accordion title="Respond with 2xx quickly">
  Your endpoint should return a `200` status within a few seconds. Do your heavy processing in the background. If Yativo does not receive a `2xx` response, it will retry the event with exponential backoff.
</Accordion>

<Accordion title="Handle duplicates idempotently">
  Webhooks can be delivered more than once (e.g., after retries). Use the `id` field to deduplicate events in your database before processing.
</Accordion>

<Accordion title="Use HTTPS">
  Always use HTTPS endpoints in production so payloads cannot be intercepted in transit. The API accepts HTTP URLs but they should only be used for local development and testing.
</Accordion>

<Accordion title="Monitor the event log">
  Check `GET /webhook/get-event-logs` regularly to spot delivery failures before they become business problems.
</Accordion>
