> ## Documentation Index
> Fetch the complete documentation index at: https://docs.finalsay-hitl.com/llms.txt
> Use this file to discover all available pages before exploring further.

# N8n

# n8n

Connect **n8n** to **FinalSay** using the community package **`n8n-nodes-finalsay`** (in-repo: [`finalsay-n8n-nodes/`](../../finalsay-n8n-nodes)), or using the **HTTP Request** node with the [Agent API](../api-reference/agent).

## Community nodes (`n8n-nodes-finalsay`)

The package adds:

* **FinalSay Create Approval** — `POST /v1/approvals`. **Toggles at the bottom** (defaults on): use **execution id** for `request_id` and `correlation_id` (manual fields hidden until toggles are off), and **send callback** with URL default `{{ $execution.resumeUrl }}` (**Wait** → Create → resume). Optional timeout override and callback headers (use headers for non-secret metadata; see [Securing the Wait resume URL](#securing-the-wait-resume-url)).
* **FinalSay Ask & Wait** — Same create fields (without a separate callback URL): posts `POST /v1/approvals` with `callback.url` set to the execution resume URL, then **pauses** until FinalSay delivers the outcome to that URL. Use **one item per run**. Resume-webhook **Authentication** options mirror n8n’s **Wait** node (None / Header / Basic / JWT). Output merges the create response with **`final_result`** (callback JSON). The node is marked **`usableAsTool`**, so n8n also registers derived types for LangChain **AI Agent** wiring (see below).
* **FinalSay Get Approval Result** — `GET /v1/approvals/:requestId/result` (e.g. if you clear the callback URL and poll manually).

Install from the monorepo (`npm run build` in `finalsay-n8n-nodes`, then `npm link` or copy into n8n’s custom nodes path), or from npm when published. See [`finalsay-n8n-nodes/README.md`](../../finalsay-n8n-nodes/README.md).

### AI Agent tools and HitlTool

n8n auto-derives two extra node types from **FinalSay Ask & Wait** (same mechanism as built-in send-and-wait integrations such as Gmail):

| Registered type (package `n8n-nodes-finalsay`) | Role                                                                                                                                                                                                                         |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `finalSayAskAndWaitTool`                       | Sub-node with a **Tool** output: connect to the LangChain **AI Agent**’s **`ai_tool`** input so the model can create a FinalSay approval and wait on the resume webhook.                                                     |
| `finalSayAskAndWaitHitlTool`                   | **Human-in-the-loop gate** between the agent and **another** tool: **Agent → FinalSay HitlTool → downstream tool**. Reviewers act in FinalSay; the Hitl UI adds a **Message** field (merged into approval context when set). |

**Self-hosted:** set environment variable **`N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true`** (n8n ≥ 1.79) so community packages are allowed as AI tools. Restart n8n after installing or upgrading the package.

Shape reference: [`finalsay-n8n-nodes/examples/ai-agent-finalsay-tool.example.json`](../../finalsay-n8n-nodes/examples/ai-agent-finalsay-tool.example.json).

To create approvals **only** via MCP and handle **Wait** / resume URLs yourself in the workflow, use the FinalSay MCP adapter tools **`create_approval`** or **`request_human_approval`** with optional `callback` — see [MCP](./mcp).

You can still use **HTTP Request** for full control or unusual payloads.

### Human gate before another MCP server

To run a **human approval step** in n8n **before** calling a **different** MCP server (not the FinalSay adapter), wire **FinalSay Ask & Wait** → (optional **IF** / **Switch** on `final_result`) → n8n’s built-in **MCP Client Tool**. The community package supplies the approval/wait nodes; it does not embed the MCP client—that stays a separate node on the canvas. Reference: [`finalsay-n8n-nodes/examples/human-gate-before-mcp.example.json`](../../finalsay-n8n-nodes/examples/human-gate-before-mcp.example.json) and *Human review before a different MCP tool* in [`finalsay-n8n-nodes/README.md`](../../finalsay-n8n-nodes/README.md).

## Prerequisites

* [Credentials](../guide/credentials) from the **web dashboard** or **mobile app**: **Agent ID**, **workspace API key**, and your deployment **base URL**.
* Familiarity with n8n **HTTP Request** nodes and, for callbacks, **Webhook** nodes.

## Headers for every request

Add these to your HTTP Request node (or expression equivalent):

| Header         | Value                       |
| -------------- | --------------------------- |
| `X-Agent-Id`   | Your Agent ID               |
| `X-Api-Key`    | Your workspace API key      |
| `Content-Type` | `application/json` for POST |

## Create an approval

1. Add an **HTTP Request** node.
2. Method **POST**, URL: `{BASE_URL}/v1/approvals`
3. Body: **JSON** matching [Agent API: POST /v1/approvals](../api-reference/agent#post-v1-approvals) (`question`, `context_markdown`, `options`, and optionally `timeout`, `callback`, or `request_id`). Omit `timeout` to use the agent’s dashboard defaults.

Save the response field **`request_id`** for polling (the API returns one whether you sent your own or the server generated it).

## Get the result

### Option A — Callback (recommended for long waits)

1. In the **create** payload, set `callback.url` to a URL n8n exposes (e.g. **Webhook** node in “Test” or production URL).
2. Run your workflow; when a human resolves the approval, the service POSTs the final payload to your webhook.
3. Parse the JSON in a following node.

### Option B — Polling

1. After creation, poll **GET** `{BASE_URL}/v1/approvals/{requestId}/result` where `{requestId}` is the **`request_id` from the create response** (or the same value if you set `request_id` in the body yourself).
2. If the body is `{ "status": "pending" }`, wait and repeat (use **Wait** node or a loop with delay).
3. When resolved, the response includes the full result object—see [Agent API](../api-reference/agent#get-v1-approvals-requestid-result).

```mermaid theme={null}
flowchart LR
  create[POST_create_approval] --> branch{Callback?}
  branch -->|yes| webhook[Webhook_receives_POST]
  branch -->|no| poll[GET_result_until_not_pending]
```

## Securing the Wait resume URL

When you use **Wait** → **On Webhook Call**, n8n gives you `{{ $execution.resumeUrl }}`. Anyone who obtains that URL could resume the execution unless you protect the Wait node.

**Recommended (Wait-compatible): agent-level callback auth**

1. In the **FinalSay web dashboard**, open the agent → **Settings** → **Callback webhook authentication**.
2. Choose a **header name** (default `X-FinalSay-Callback`) and click **Generate secret** (or **Rotate secret**). Copy the value once; it is not shown again.
3. In n8n, on the **Wait** node, set **Authentication** to **Header Auth** (or equivalent) and enter the **same** header name and value as in the dashboard.
4. In **FinalSay Create Approval**, set **Callback URL** to `{{ $execution.resumeUrl }}` as usual. You can leave **Callback headers** empty unless you need extra non-secret headers.

FinalSay merges the dashboard secret into the outbound callback **on the server** when the approval resolves. The secret does **not** belong in workflow JSON, `callback.headers` on create, or MCP tool arguments—so it is not leaked in exports.

If the same header name appears in both **Callback headers** on the node and agent callback auth, the **agent** value wins.

### Troubleshooting `403` on the resume URL

* **Header never sent** — Outbound callback auth exists only after you **Generate secret** in the agent **Settings** tab. If callback auth is **not** configured, FinalSay does not add the header; n8n **Header Auth** on **Wait** / **FinalSay Ask & Wait** still expects it → **403**.
* **Name or value mismatch** — Use the **same header name** as in the dashboard (default `X-FinalSay-Callback`) and paste the **generated secret** into the n8n **Header Auth** credential’s value. After **Rotate secret**, update the credential; the previous secret is invalid.
* **IP allowlist** — On **FinalSay Ask & Wait**, **Options → IP(s) Allowlist** must either be empty or include the addresses FinalSay uses to call your URL; otherwise n8n returns **403** (*IP is not allowed*).
* **Ignore bots** — With **Ignore bots** enabled, unusual `User-Agent` strings may be rejected.

## Tips

* You can **omit `request_id`**; the API assigns a UUID. Add your own stable `request_id` when you need idempotent retries or to tie the approval to a workflow run id.
* Respect **rate limits** on create; if you receive `429`, slow down.
* Idempotency: repeating the same **client-supplied** `request_id` with the same payload is safe; conflicting payloads return `409`.

## Related

* [Custom HTTP & scripts](./custom-http)
* [Automation limits & billing](../guide/automation-limits)
* [Zapier](./zapier)
* [MCP](./mcp)
* [Agent API](../api-reference/agent)
