> ## Documentation Index
> Fetch the complete documentation index at: https://lago-ftr-wallet-improvements.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Format & Signature

> Webhooks are HTTP notifications sent from Lago to your application.

The messages are sent as a `POST` to the URL defined in the settings of your
Lago workspace.

## Message format[](#message-format "Direct link to heading")

`POST __WEBHOOK_URL__`

```json
{
  "webhook_type": "__TYPE__",
  "object_type": "OBJECT_TYPE",
  "__OBJECT__": {}
}
```

## Signature[](#signature "Direct link to heading")

Allong with the payload the message contains both `X-Lago-Signature` and `X-Lago-Signature-Algorithm` HTTP header.

It is used to ensure the message is Coming from Lago and that the message has not been altered.

To verify the signature, you must decode the signature and compare the result with the body of the webhook.

You can choose between 2 differents signatures algorithm during your webhook endpoints creation, `hmac` or `jwt`.
Please note that `jwt` is our original signature and is used by default.

### JWT Signature

#### 1. Retrieve the public key[](#1-retrieve-the-public-key "Direct link to heading")

<CodeGroup>
  ```py Python
  from lago_python_client import Client

  client = Client(api_key='__YOUR_API_KEY__')
  webhooks_public_key = client.webhooks().public_key()
  ```

  ```js Node.js
  import Client from "lago-nodejs-client";

  let client = new Client("__YOUR_API_KEY__");
  let webhooksPublicKey = client.webhookPublicKey();
  ```

  ```ruby Ruby
  require 'net/http'

  api_key = "__YOUR_API_KEY__"
  uri = URI('https://api.getlago.com/api/v1/webhooks/public_key')

  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  response = http.send_request(
    'GET',
    uri.request_uri,
    '',
    { 'Authorization' => "Bearer #{api_key}" }
  )

  webhooks_public_key = response.body
  ```
</CodeGroup>

<br />

<Tip>
  You should persist the public key on your side to avoid querying it for each
  webhook.
</Tip>

#### 2. Decode and validate the signature[](#2-decode-and-validate-the-signature "Direct link to heading")

<CodeGroup>
  ```py Python
  import jwt

  decoded_signature = jwt.decode(
    request.headers.get('X-Lago-Signature'),
    webhooks_public_key,
    algorithms=['RS256'],
    issuer="https://api.getlago.com"
  )

  decoded_signature['data'] == request.body
  ```

  ```js Node.js
  import jwt from "jsonwebtoken";

  let token = request.header("X-Lago-Signature");

  jwt.verify(
    token,
    webhooksPublicKey,
    {
      algorithms: ["RS256"],
      issuer: "https://api.getlago.com",
    },
    function (err, payload) {
      payload === request.body;
    }
  );
  ```

  ```ruby Ruby
  require 'openssl'
  require 'jwt'

  decoded_signature = JWT.decode(
    request.headers['X-Lago-Signature'],
    OpenSSL::PKey::RSA.new(Base64.decode64(webhooks_public_key)),
    true,
    {
      algorithm: 'RS256',
      iss: "https://api.getlago.com",
      verify_iss: true,
    },
  ).first

  decoded_signature['data'] == request.body
  ```
</CodeGroup>

### HMAC Signature

#### Decode and validate the signature

<CodeGroup>
  ```py Python
  import hmac
  import base64

  calc_sig = hmac.new(LAGO_API_KEY, request.body.encode(), 'sha256').digest()
  base64_sig = base64.b64encode(calc_sig).decode()
  request.headers.get('X-Lago-Signature') == base64_sig
  ```

  ```js Node.js
  import crypto from 'crypto';

  const signature = request.header('X-Lago-Signature')
  const hmac = crypto.createHmac('sha256', 'YOUR_ORGANIZATION_API_KEY')

  hmac.update(request.body)

  const encodedBody = hmac.digest().toString('base64')

  signature == encodedBody
  ```

  ```ruby Ruby
  require 'openssl'

  signature =  request.headers['X-Lago-Signature']
  hmac = OpenSSL::HMAC.digest('sha-256', 'YOUR_ORGANIZATION_API_KEY', request.body)
  base64_hmac = Base64.strict_encode64(hmac)

  base64_hmac == signature
  ```
</CodeGroup>
