Integrations
n8n workflow automation
Connect self-hosted n8n to this app using outbound webhooks and inbound REST calls with API keys.
## Overview
You can use **self-hosted n8n** as workflow middleware: the app **sends** signed webhooks to n8n, and n8n **calls** the app over HTTPS with a tenant **API key**. No first-party marketplace or n8n-specific API route is required.
**License note:** n8n is **source-available** under the **Sustainable Use License** (fair-code), not an OSI-approved open source license. If your policy requires OSI-only tooling, evaluate alternatives such as Node-RED or Windmill separately.
## Outbound: app to n8n
1. In n8n, create a workflow with a **Webhook** node (POST). Activate the workflow and copy the **Production URL**.
2. In this app, go to **Settings → Webhooks**, create an endpoint with that URL, and select the events you need.
3. When the app dispatches an event, n8n receives an HTTP POST.
### Payload and headers
The **raw body** is a JSON string (UTF-8) with this shape (keys in this order matches the server):
```json
{
"event": "member.joined",
"data": {},
"timestamp": "2026-03-21T12:00:00.000Z"
}
```
Headers:
| Header | Meaning |
|--------|---------|
| `Content-Type` | `application/json` |
| `X-Webhook-Event` | Same string as `event` in the body |
| `X-Webhook-Signature` | HMAC-SHA256 of the **exact raw body string**, output as **lowercase hex** (no prefix) |
The signing key is the full endpoint **secret** shown once when you create the webhook (format `whsec_` followed by hex). The server uses that string as the HMAC key.
### Verifying the signature in n8n
1. Store the secret in n8n as an **environment variable** named `SAAS_WEBHOOK_SECRET` (same value as the full `whsec_...` string from the app).
2. After the Webhook node, add a **Code** node (JavaScript) so you control the string used for HMAC. Rebuild the payload in the same key order as above, then compare:
```javascript
const crypto = require("crypto");
const secret = $env.SAAS_WEBHOOK_SECRET;
const item = $input.first().json;
const body = item.body;
const payload = JSON.stringify({
event: body.event,
data: body.data,
timestamp: body.timestamp,
});
const expected = crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
const received = item.headers["x-webhook-signature"];
if (expected !== received) {
throw new Error("Invalid webhook signature");
}
return [{ json: { ...body, verified: true } }];
```
Adjust `item.body` / `item.headers` to match your n8n version’s Webhook output shape (sometimes headers are lowercased).
### Event types
The app may emit these webhook event names (subscribe to the ones you need in Settings):
- `member.invited`
- `member.joined`
- `member.removed`
- `subscription.created`
- `subscription.updated`
- `subscription.canceled`
- `usage.threshold`
## Inbound: n8n to app
Use the **HTTP Request** node (or equivalent) against your deployment base URL and paths from **OpenAPI** / **API Docs** (`/api-docs` in the app).
- **Authentication:** `Authorization: Bearer <your API key>` (`rl_live_...` from **Settings → API keys**).
- **CSRF:** Applies to browser sessions calling `/api/*`. **Server-to-server** calls from n8n with a Bearer API key do not use the browser cookie flow.
Create keys with the scopes your workflows need (`read`, `write`, `mcp`, etc.). See the **API Keys** doc in this help center.
## Local n8n with Docker (optional)
For a disposable local editor, run n8n from the repository root:
`docker compose -f docker-compose.n8n.yml up`
The file pins `n8nio/n8n:2.13.0` and documents fair-code licensing. Use **HTTP** webhook URLs that n8n can reach (e.g. tunnel or same Docker network as the app). This is for development only, not production hardening guidance.
An importable sample workflow (Webhook → verify HMAC → respond) lives in **`examples/n8n/`** in the repository.
## Further reading
- [API Keys](/docs/api-keys) in this help center
- In-app **API Docs** at `/api-docs` (OpenAPI try-it-out). Repository reference: `docs/api-documentation.md` for SDK and export scripts.