> For the complete documentation index, see [llms.txt](https://docs.lingoql.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lingoql.com/sub0/apis-abi/webhooks.md).

# Webhooks

In Sub0, a webhook is just an endpoint that receives inbound HTTP requests.

That means you secure and process it with the same ABI model as the rest of your backend.

### Basic webhook endpoint

```json
{
  "id": "payments-webhook-01",
  "resource": "payments-webhook",
  "run_in_background": true,
  "webhook": {
    "verifications": {
      "HeaderTokenVerifier": {
        "header_key": "$HEADER.X-Gitlab-Token",
        "secret_key": "$ENV.MY_GITLAB_TOKEN"
      }
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO transactions (txn_id, amount, status) VALUES ($1, $2, $3)",
        "parameters": ["txn_id", "amount", "status"] // assuming the webhook data contained these properties
      }
    }
  ]
}
```

### Secure webhook requests

Sub0 supports multiple verification strategies inside `webhook.verifications`.

Common options:

* `HeaderTokenVerifier`
* `HmacSignatureVerifier`
* `PublicKeySignatureVerifier`
* `JwtVerifier`
* `IpAllowListVerifier`

Use `composition_strategy_mode` to choose whether verification requires `ALL` (i.e all the strategies must be **passed**) or `ANY` (i.e if any of the strategy **passes**).

If you omit `composition_strategy_mode`, Sub0 uses `ALL`.

### Verification config rules

Use header references with the `$HEADER.` prefix.

Use secret and key references with the `$ENV.` prefix.

Example:

```json
"header_key": "$HEADER.X-Signature",
"secret_key": "$ENV.WEBHOOK_SECRET"
```

### Header token verifier

Use this when the provider sends a shared secret in a header.

Sub0 compares the header value against the environment variable value.

```json
{
  "id": "gitlab-webhook-01",
  "resource": "gitlab-webhook",
  "webhook": {
    "verifications": {
      "HeaderTokenVerifier": {
        "header_key": "$HEADER.X-Gitlab-Token",
        "secret_key": "$ENV.GITLAB_WEBHOOK_TOKEN"
      }
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO webhook_events (provider, event_name, payload) VALUES ($1, $2, $3)",
        "parameters": ["gitlab", "$HEADER.X-Gitlab-Event", "$PAYLOAD"]
      }
    }
  ]
}
```

### HMAC signature verifier

Use this when the provider signs the raw request body with a shared secret.

Supported algorithms:

* `sha256`
* `sha384`
* `sha512`

Sub0 accepts either a plain hex signature or a GitHub-style `sha256=<hex>` header value.

```json
{
  "id": "github-webhook-01",
  "resource": "github-webhook",
  "run_in_background": true,
  "webhook": {
    "verifications": {
      "HmacSignatureVerifier": {
        "sig_header": "$HEADER.X-Hub-Signature-256",
        "secret_key": "$ENV.GITHUB_WEBHOOK_SECRET",
        "algorithm": "sha256"
      }
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO webhook_events (provider, event_name, payload) VALUES ($1, $2, $3)",
        "parameters": ["github", "$HEADER.X-GitHub-Event", "$PAYLOAD"]
      }
    }
  ]
}
```

Example with `sha512`:

```json
{
  "webhook": {
    "verifications": {
      "HmacSignatureVerifier": {
        "sig_header": "$HEADER.X-Provider-Signature",
        "secret_key": "$ENV.PROVIDER_WEBHOOK_SECRET",
        "algorithm": "sha512"
      }
    }
  }
}
```

### Public key signature verifier

Use this when the provider signs the raw request body with a private key.

Sub0 expects:

* a base64-encoded signature in the configured header
* an RSA public key in PEM format in your environment variable

```json
{
  "id": "signed-provider-webhook-01",
  "resource": "signed-provider-webhook",
  "webhook": {
    "verifications": {
      "PublicKeySignatureVerifier": {
        "header_key": "$HEADER.X-Signature",
        "public_key_pem": "$ENV.PROVIDER_PUBLIC_KEY_PEM"
      }
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO webhook_events (provider, payload) VALUES ($1, $2)",
        "parameters": ["provider-with-rsa-signing", "$PAYLOAD"]
      }
    }
  ]
}
```

### JWT verifier

Use this when the provider sends a JWT in a header.

Set `algorithm` when the token uses a specific JWT signing algorithm.

If you omit it, Sub0 defaults to `HS256`.

```json
{
  "id": "jwt-webhook-01",
  "resource": "jwt-webhook",
  "webhook": {
    "verifications": {
      "JwtVerifier": {
        "header_key": "$HEADER.X-Webhook-JWT",
        "secret_key": "$ENV.WEBHOOK_JWT_SECRET",
        "algorithm": "HS256"
      }
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO webhook_events (provider, payload) VALUES ($1, $2)",
        "parameters": ["jwt-provider", "$PAYLOAD"]
      }
    }
  ]
}
```

If your provider sends the token in `Authorization`, configure that exact header:

```json
{
  "webhook": {
    "verifications": {
      "JwtVerifier": {
        "header_key": "$HEADER.Authorization",
        "secret_key": "$ENV.WEBHOOK_JWT_SECRET",
        "algorithm": "HS512"
      }
    }
  }
}
```

### IP allow-list verifier

Use this when the provider publishes fixed source IPs.

Set the environment variable to a comma-separated or pipe-separated list.

Example environment variable values:

```json
"ALLOWED_WEBHOOK_IPS": "192.0.2.10,198.51.100.24"
```

```json
"ALLOWED_WEBHOOK_IPS": "192.0.2.10|198.51.100.24"
```

Webhook config:

```json
{
  "id": "ip-locked-webhook-01",
  "resource": "ip-locked-webhook",
  "webhook": {
    "verifications": {
      "IpAllowListVerifier": {
        "allowed_ips": "$ENV.ALLOWED_WEBHOOK_IPS"
      }
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO webhook_events (provider, payload) VALUES ($1, $2)",
        "parameters": ["trusted-ip-provider", "$PAYLOAD"]
      }
    }
  ]
}
```

### Combine verifiers with ALL

Use `ALL` when every configured verifier must pass.

This is the strongest default for production webhooks.

```json
{
  "id": "payment-provider-webhook-01",
  "resource": "payment-provider-webhook",
  "run_in_background": true,
  "webhook": {
    "verifications": {
      "HmacSignatureVerifier": {
        "sig_header": "$HEADER.X-Signature",
        "secret_key": "$ENV.PAYMENT_WEBHOOK_SECRET",
        "algorithm": "sha256"
      },
      "IpAllowListVerifier": {
        "allowed_ips": "$ENV.PAYMENT_PROVIDER_IPS"
      },
      "composition_strategy_mode": "ALL"
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "UPDATE transactions SET status = $1, updated_at = $2::timestamptz WHERE provider_ref = $3",
        "parameters": ["status", "$DATETIME", "reference"]
      }
    }
  ]
}
```

### Combine verifiers with ANY

Use `ANY` when the provider may authenticate requests in more than one valid way.

Sub0 accepts the request as soon as one configured verifier succeeds.

```json
{
  "id": "multi-auth-webhook-01",
  "resource": "multi-auth-webhook",
  "webhook": {
    "verifications": {
      "HeaderTokenVerifier": {
        "header_key": "$HEADER.X-Webhook-Token",
        "secret_key": "$ENV.WEBHOOK_TOKEN"
      },
      "JwtVerifier": {
        "header_key": "$HEADER.X-Webhook-JWT",
        "secret_key": "$ENV.WEBHOOK_JWT_SECRET",
        "algorithm": "HS256"
      },
      "composition_strategy_mode": "ANY"
    }
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO webhook_events (provider, payload) VALUES ($1, $2)",
        "parameters": ["multi-auth-provider", "$PAYLOAD"]
      }
    }
  ]
}
```

### Map third-party payloads

Webhook payloads arrive through `$PAYLOAD`.

That makes it easy to map a provider payload into your own schema.

### Run in the background

This is the recommended pattern:

```json
"run_in_background": true
```

It helps you acknowledge the provider quickly while processing the real work asynchronously.

### What webhook handlers can do

Webhook endpoints can:

* write to the database
* call other APIs
* queue follow-up work
* broadcast updates over WebSockets

### Why it matters

Webhooks are usually messy because they mix security, parsing, retries, and async work.

Sub0 keeps all of that in one endpoint definition.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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.lingoql.com/sub0/apis-abi/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.
