# 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://testnet.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://testnet.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://testnet.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

* 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)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mercurydata.app/mercury-classic/webhooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
