# Webhooks

Mercury can deliver real-time contract event notifications to your HTTP endpoint via webhooks. When an event matches your webhook's filters, Mercury sends a POST request with\
the event data, signed with HMAC-SHA256 for authenticity.

#### Creating a webhook

```bash
curl https://mainnet.mercurydata.app/rest/webhooks/new \
  -H 'Authorization: Bearer <your-jwt>' \
  -H 'Content-Type: application/json' \
  -d '{
    "webhook_endpoint": "https://your-endpoint.com/webhook",
    "contract_id": "CDVNV4HBU5WBEQO5K7YZW7NULFLHOTYMUMYUGSPAU2QOADZRIJYTYMM6"
  }'
```

Response:

```bash
{
"id": 42,
"secret": "whsec_a1b2c3d4e5f6..."
}
```

Save the secret — it's shown only once. If desired you'll use it to verify incoming webhook signatures.

#### Topic filtering

By default, a webhook fires for all events from the specified contract. To filter by specific event topics, pass topic1 through topic4 as XDR base64-encoded values:

```bash
curl -X POST https://api.mercurydata.app/webhooks/new \
    -H "Authorization: Bearer <your-jwt>" \
    -H "Content-Type: application/json" \
    -d '{
      "webhook_endpoint": "https://your-endpoint.com/webhook",
      "contract_id": "CAA3TVCEWVRA426A4FX2FMFFVORECOIXPV52P7XRHPXCRHNAJ5Q5NYKE",
      "topic1": "AAAADwAAAAhwdXJjaGFzZQ==",
      "topic2": "AAAAEgAAAAAAAAAAYK5vRsE..."
    }'
```

Filtering rules:

* Each topic filter is optional. Omitted topics act as wildcards (match anything).
* All specified topics use AND logic — every filter must match for the event to trigger delivery.
* You can filter on any combination: just topic1, just topic3, both topic1 and topic4, etc.
* Topic values must match exactly (XDR base64 encoding of the ScVal).

#### Listing webhooks

```bash
curl https://api.mercurydata.app/webhooks/list \                                                                                                                                 
    -H "Authorization: Bearer <your-jwt>"    
```

Response:

{% code overflow="wrap" %}

```bash
[
    {
      "id": 42,
      "webhook_endpoint": "https://your-server.com/webhook",
      "contract_id": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
      "topic1": "AAAADwAAAAh0cmFuc2Zlcg==",
      "topic2": null,
      "topic3": null,
      "topic4": null
    }
  ]
```

{% endcode %}

#### Deleting a webhook

```bash
curl -X DELETE https://api.mercurydata.app/webhooks/{id} \                                                                                                                       
    -H "Authorization: Bearer <your-jwt>"
```

#### Webhook payload

When an event matches, Mercury sends a POST request to your endpoint:

```bash
  {
    "event": {
      "ext": "v0",
      "contract_id": "CDVNV4HBU5WBEQO5K7YZW7NULFLHOTYMUMYUGSPAU2QOADZRIJYTYMM6",
      "type_": "contract",
      "body": {
        "v0": {
          "topics": [
            { "symbol": "deposit" },
            { "address": "GAGAXGWEMQZ4NLHXDRSGXVMX2XENYHQVWSQC5R5U6YNXKBUP7FQ2SCVR" },
            { "i32": 28 }
          ],
          "data": {
            "i128": "100000"
          }
        }
      }
    },
    "tx_hash": "14ff90e64e533d87f5a11a961edd4d7d176e7953767c4532d01e4b0...."
  }
```

#### Headers&#x20;

* X-Mercury-Signature: HMAC-SHA256 hex signature of the request body
* X-Mercury-Timestamp: Unix timestamp (seconds) when the request was sent
* Content-Type: application/json

#### Verifying signatures

Optionally use your webhook secret to verify that requests are authentic:

*Node.js*

```javascript
const crypto = require('crypto');

  function verifyWebhook(body, signature, secret) {
    const expected = crypto
      .createHmac('sha256', secret)
      .update(body)
      .digest('hex');
    return crypto.timingSafeEqual(
      Buffer.from(signature, 'hex'),
      Buffer.from(expected, 'hex')
    );
  }

  // In your handler:
  app.post('/webhook', (req, res) => {
    const rawBody = JSON.stringify(req.body);
    const signature = req.headers['x-mercury-signature'];

    if (!verifyWebhook(rawBody, signature, 'whsec_your_secret_here')) {
      return res.status(401).send('Invalid signature');
    }

    // Process the event
    console.log('Received event:', req.body.event);
    res.status(200).send('OK');
  });
```

*Python*

```python
  import hmac
  import hashlib

  def verify_webhook(body: str, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode(), body.encode(), hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(signature, expected)
```
