Description
n8n-nodes-brydge
An n8n community node for BRYDGE: ask before an agent acts, then check against the destination system’s own records whether the work actually happened.
It is a thin wrapper over brydge-core — the same verification client the BRYDGE LangChain and MCP packages use — bundled in, so the node has no runtime dependencies of its own.
What it does
One node, five operations:
| Operation | What it does |
| — | — |
| Supervise | Ask BRYDGE whether the agent may take an action, before it does. ALLOWED returns an authorization id to cite at the destination; ESCALATED means a person decides. |
| Report Outcome | Tell BRYDGE what the agent believes happened. BRYDGE keeps it beside the finding; it never decides it. |
| Verify | Read the destination’s own records now and say whether the work happened as permitted. |
| Get Finding | What BRYDGE has found so far, without reading the records again. |
| Get Headroom | How much the agent may do without a person in any 24 hours, and why. |
The flow a workflow builds: Supervise → (if ALLOWED, your own nodes carry the action out, writing the authorization into the record they create) → Report Outcome → Verify.
ESCALATED is returned, not thrown, so a workflow can branch on it with an IF node and ask the same question again — with the same Idempotency Key — once the person has answered. Only a fault (a bad key, an unclaimed workspace, a rate limit) is an error, which an item carries when the node runs with Continue On Fail.
A worked example: refunding one order
The whole loop, with the values you would actually type. Four nodes.
Everything below is what the node and BRYDGE do today. The ids BRYDGE returns
are its own — yours will differ.
1. Supervise — ask before refunding
| Field | Value |
| — | — |
| Node name | Supervise — rename it, so the later steps can point at it |
| Operation | Supervise |
| Actor | agent:refund-bot |
| Action | refund |
| Target | ch_3QxMne2eZvKYlo2C0Rk8pTqA — the Stripe charge being refunded |
| Facts (JSON) | {"amount": 4200} |
| Idempotency Key | refund-order-1001 |
Target is the charge id, not the order number. The Stripe Refunds preset
finds the work by asking Stripe GET /v1/refunds?charge=, and compares
the refund’s own charge field against it. An order number put here would
match nothing. The order number belongs in the Idempotency Key, which is
BRYDGE’s own name for this one intended action and is never compared against
anything at Stripe.
First run — nobody has issued a mandate for this yet, so a person decides:
{
"id": "clz8k2p0a0001q8f3h5x7m2vd",
"decision": "ESCALATED",
"mandateId": null,
"because": "no mandate lets agent:refund-bot refund yet, so a person decides. A person has been asked. Ask again with the same idempotency key once they have answered: if they allow it, the answer is ALLOWED.",
"checked": [],
"unobserved": [],
"replayed": false,
"settled": null,
"next": "A person has been asked. Ask again with the same idempotency key once they have answered: if they allow it, the answer is ALLOWED."
}
ESCALATED is returned, not thrown. Branch on it with an IF node testing
{{ $json.decision }}, and do not refund anything on that path.
A person approves. In BRYDGE, open Escalations, open this one, type who
you are, and press Let it happen. (The other button is Don’t.)
Run the same node again, with the same Idempotency Key. Same key, same
question — that is how the person’s answer reaches the workflow:
{
"id": "clz8k2p0a0001q8f3h5x7m2vd",
"decision": "ALLOWED",
"mandateId": null,
"because": "authorised by maya, who allowed this one on 2026-10-29. Carry it out once",
"replayed": true,
"settled": "ALLOWED",
"next": null
}
id is the authorization. Every node after this one cites it. The name in
because is whoever typed it on the escalation, and mandateId stays null — a
person allowed this one action, which is not the same as delegating the class.
Had they pressed Don’t, this would still read ESCALATED, with
settled: "REFUSED" and a because naming who refused it. Asking again under
the same key will never turn that into ALLOWED.
2. Your own node — refund, and write the authorization into the refund
BRYDGE does not move money. Your workflow does, and the record it creates has
to carry the authorization or BRYDGE cannot find it later.
For the Stripe Refunds preset, create the refund with
metadata[brydge_authorization] set to the id from step 1. An HTTP Request
node does it — Stripe’s API is form-encoded:
| Field | Value |
| — | — |
| Method | POST |
| URL | https://api.stripe.com/v1/refunds |
| Authentication | your Stripe credential |
| Body Content Type | Form Urlencoded |
| Body Parameter | charge = ch_3QxMne2eZvKYlo2C0Rk8pTqA |
| Body Parameter | amount = 4200 |
| Body Parameter | metadata[brydge_authorization] = {{ $('Supervise').item.json.id }} |
That one parameter is all the preset needs. Any node that can set metadata on
the refund will do; the HTTP Request node is simply the one that certainly can.
Point at the Supervise node by name, not at $json. From here on $json
is Stripe’s refund, and a Stripe refund has an id of its own (re_…). Using
{{ $json.id }} in the steps below would send Stripe’s id to BRYDGE as the
authorization, and BRYDGE would answer about an action it has never heard of.
Two optional fields, and one of them can hurt you:
metadata[brydge_actor]— the preset reads the actor from here. Set it, andamount— the preset reads Stripe’samount, which is minor units on both
it must equal the Actor from step 1 exactly (agent:refund-bot). Set it
wrong and the finding is MISMATCH (ACTOR). Leave it out and nothing is
compared: a field the destination did not supply is not a field that
disagrees.
sides, and BRYDGE compares it against the amount fact from step 1. A
partial refund of a different figure is MISMATCH (AMOUNT).
There is nothing to write for the action verb: Stripe refunds carry none, so
the preset reads it as absent rather than guessing.
3. Report Outcome — say what the agent believes happened
| Field | Value |
| — | — |
| Operation | Report Outcome |
| Authorization | {{ $('Supervise').item.json.id }} |
| Outcome | Succeeded |
| Said | optional, e.g. refund issued for order-1001 |
{ "authorization": "clz8k2p0a0001q8f3h5x7m2vd", "reported": "SUCCEEDED" }
BRYDGE keeps this beside the finding. It never decides it — the next step is
what decides, and it can contradict this.
4. Verify — read Stripe’s own records
| Field | Value |
| — | — |
| Operation | Verify |
| Authorization | {{ $('Supervise').item.json.id }} |
This needs a registered destination. Verify reads the customer’s own Stripe
account over a credential they stored in BRYDGE, at a destination registered
for refund and attested. Register one on the Connect page before relying
on this step.
With one, a matched refund reads:
{
"authorization": "clz8k2p0a0001q8f3h5x7m2vd",
"state": "VERIFIED",
"reason": null,
"externalRef": "re_3QxMne2eZvKYlo2C1Hs9kLpB",
"claimed": "SUCCEEDED",
"agentAgreed": true
}
Without one, Verify still answers — it does not fail — and says it could not
look:
{
"authorization": "clz8k2p0a0001q8f3h5x7m2vd",
"state": "UNKNOWN",
"reason": "NO_DESTINATION",
"because": "BRYDGE has no way to check this action — no destination is registered for it.",
"externalRef": null,
"claimed": "SUCCEEDED",
"agentAgreed": null,
"checkedAt": null
}
UNKNOWN is not FAILED. BRYDGE has not established that the refund did not
happen; it has established that it cannot tell, so agentAgreed is null
rather than false — the agent is not called a liar because BRYDGE had nowhere
to look. Branch on {{ $json.state }} if the workflow needs to care, and treat
anything that is not VERIFIED as unestablished rather than as a failure.
Install
In n8n: Settings → Community Nodes → Install, and enter n8n-nodes-brydge.
Then add a BRYDGE API credential: your API key (it starts with brydgesk; issue one in BRYDGE on the Connect page) and, if you self-host BRYDGE, its URL.
Development
In the monorepo, build the shared core once so this package can bundle it:
npm --prefix ../core install && npm --prefix ../core run buildnpm install # links brydge-core from ../core
npm run build # bundles brydge-core in and copies the icon into dist
npm test # the node driven through a mock n8n context
Published standalone, this package depends on brydge-core from npm instead of ../core; the bundle is the same either way.
Publishing and submitting
A community node is an npm package n8n installs by name.
1. Publish to npm (nothing is published until you do):
npm publish
prepublishOnly runs the typecheck and tests, and prepack builds dist, so a broken build never ships. The package declares n8n-community-node-package in its keywords and n8n-workflow as a peer dependency, with no runtime dependencies of its own.
2. Install it in any n8n via Settings → Community Nodes → n8n-nodes-brydge.
3. Submit for verification (optional, to appear in n8n’s built-in search). Follow n8n’s current process at
npx eslint-plugin-n8n-nodes-base # or: npx @n8n/scan-community-package n8n-nodes-brydge
The node already follows the conventions the review checks: a file: icon, a credential with a password-typed key, operations with action and description, a subtitle, and no runtime dependencies.
License
MIT


