On this page 12 sections
Write a custom node when the same integration logic - auth, pagination, error shaping - is duplicated across three or more workflows, or when a non-technical person needs to use the integration without seeing an HTTP request. Below that threshold, an HTTP Request node with a sub-workflow is cheaper to build and much cheaper to maintain.
Most teams reach for a custom node too early, because the HTTP node feels like a workaround. It usually is not.
Key takeaways
- Duplication across workflows is the trigger, not complexity within one.
- A custom node is code you now own, version and upgrade against n8n releases.
- A sub-workflow gives you most of the reuse for a fraction of the commitment.
- Auth handled once, in a credential type, is the strongest single argument for a node.
- If one workflow uses it, you do not need a node.
Who this applies to
You are integrating a system n8n has no built-in node for - an internal API, a niche vendor, a partner endpoint - and you are deciding how much to build.
Exhaust these three first
1. The HTTP Request node
It handles most integrations completely: methods, headers, query parameters, body shaping, and n8n’s generic credential types cover basic auth, header auth and OAuth2 without any code.
For a single workflow calling three endpoints, this is the correct answer and there is nothing crude about it. A custom node here is strictly more work for the same behaviour.
2. A sub-workflow
The step most people skip, and usually the right answer at medium scale.
Put the integration logic in its own workflow - auth, the call, pagination, error shaping - and invoke it from the others with the Execute Workflow node. You get one place to change the API version, one place to fix a bug, and reuse across as many callers as you like.
What you give up against a real node: no typed parameter UI, no dropdowns, a slightly clumsier calling convention. What you keep: no build tooling, no npm package, no compatibility surface against n8n upgrades.
If you are considering a custom node, build the sub-workflow first. It takes an hour, it solves the duplication, and if it turns out to be enough you have saved yourself a maintained package.
3. The Code node
For transformation rather than integration. If the awkwardness is reshaping a response rather than fetching it, eight lines of JavaScript solves it and needs no packaging.
The threshold
Write a node when two or more of these hold:
The same integration appears in three or more workflows. Duplication is the actual trigger. Two workflows is a sub-workflow; five is a maintenance problem.
Auth is non-trivial and repeated. Signed requests, token refresh, tenant-scoped credentials. A custom node lets you define a credential type once, and users pick it from a dropdown rather than pasting a token into a header field in every workflow. This is the strongest single argument.
Non-technical people need to use it. A node gives them labelled fields and dropdowns. An HTTP node gives them a URL and a JSON body, which is where operations teams break things.
Pagination or rate limiting is fiddly. Cursor pagination with backoff is genuinely awkward to express in a workflow and clean in code.
You will distribute it. Across teams, or to clients. A package installs; a sub-workflow gets copy-pasted and diverges.
What writing one actually involves
Not difficult, and not free either.
A node is a TypeScript package with a class describing the node’s properties - fields, dropdowns, display conditions - and an execute method that does the work. Credentials are a separate class. n8n’s node-creation tooling scaffolds the structure.
A straightforward REST integration with a handful of operations is one to three days including tests. Complex auth or many operations, more.
The part people underestimate is the ongoing side:
- It is code you own. Someone maintains it when the vendor’s API changes.
- It has a compatibility surface with n8n. Node API changes across major versions, and a node nobody updates eventually stops loading.
- Self-hosted installation adds a step. Community nodes install through the UI or into the container; either way it is a deployment concern, and n8n Cloud restricts which community nodes can run.
- It needs its own tests. A bug in a node used by eight workflows breaks eight workflows.
Call it a day to write and a few hours a year to keep, indefinitely.
What the code actually looks like
Worth seeing the shape, because the gap between “we should write a node” and “we have written a node” is smaller than people imagine and the maintenance is larger.
A node is two things. A description object declaring the node’s display name, its inputs and outputs, and the properties users configure - each with a type, a default, and optional displayOptions that show or hide it based on other selections. And an execute method that reads those properties, does the work, and returns items in n8n’s shape.
Credentials are a separate class declaring the fields to collect and how they attach to a request - a header, a query parameter, an OAuth2 flow. Declaring it once is what gives users a dropdown instead of a header they paste a token into.
The part that surprises people is the property definitions rather than the logic. A node with three operations and a dozen conditional fields has a description object several times longer than its execute method, and that object is where the usability lives. A node with a good execute method and a careless description is a node your operations team will misconfigure.
Two practical notes. n8n’s node-creation tooling scaffolds the structure, so you are editing rather than starting blank. And test the execute method as ordinary code - it is a function taking inputs and returning outputs, and treating it as untestable because it lives in a node package is a choice rather than a constraint.
A rough decision order
| Situation | Build |
|---|---|
| One workflow, few endpoints | HTTP Request node |
| Reshaping a response | Code node |
| Two or three workflows, same API | Sub-workflow |
| Three-plus workflows, fiddly auth | Custom node |
| Non-technical users must configure it | Custom node |
| Distributing across teams or clients | Custom node, packaged |
| Vendor already has a community node | Evaluate it before writing your own |
That last row is worth checking properly. The community registry covers a lot, and an existing node - even an imperfect one you fork - beats starting from nothing.
The version that is neither
Sometimes the honest answer is that the integration should not be in n8n at all.
If the “integration” is really a body of business logic - reconciling two systems with rules, deciding what to do with conflicts, maintaining state between runs - that is a service. Writing it as a custom node buries logic in a package that is harder to test than a normal codebase, and harder to reason about than a small API.
The signal: the node’s execute method is growing branches that have nothing to do with talking to the vendor. At that point you are using the node system as an application framework, and it is not one.
What we do, and the bias we correct for
Our default is HTTP node, then sub-workflow, then custom node, and we hold that order harder than clients usually want to.
The bias we are correcting is that a custom node feels like the professional answer and an HTTP node feels like a shortcut. In practice the HTTP node is a supported, first-class part of the tool and the custom node is a package somebody has to own after we leave. On a handover-first engagement that asymmetry matters: we would rather leave a client with a sub-workflow they can read than a TypeScript package they cannot modify.
Where we do write nodes without hesitation: when auth is repeated and non-trivial, and when the people configuring workflows afterwards are not engineers. Both are cases where the node removes a real recurring cost rather than adding polish.
The opinion that occasionally costs us scope: if you have one workflow, you do not need a node, and we will say so on the call rather than quoting for one.
When not to write one
One workflow. Repeated, because it is the most common case.
A vendor API that is about to change. Wait for the stable version rather than maintaining a node through their migration.
Nobody to maintain it. A node without an owner becomes the reason an n8n upgrade gets deferred.
To avoid learning the HTTP node. Genuinely a reason people do this. The HTTP node is worth an afternoon.
Frequently asked questions
How long does a custom node take to write?
One to three days for a straightforward REST integration with a few operations, including tests. Complex auth pushes it further.
Can we use custom nodes on n8n Cloud?
Cloud restricts which community nodes may run, and privately-built ones are generally a self-hosted concern. Check the current policy before planning around it - see self-hosted n8n.
Should we publish it publicly?
Only if it is genuinely reusable and you intend to maintain it. A published node with an open issue list and no maintainer is worse for your reputation than no node.
What breaks a custom node most often?
The vendor’s API changing, then n8n major-version upgrades. Both are manageable with a cadence and neither is manageable with nobody assigned.
Is a sub-workflow really enough?
For most reuse, yes. You lose the parameter UI and gain zero build tooling. Try it before committing to a package - that is the whole recommendation.
Next step
If you are unsure whether your integration warrants a node, the deciding question is how many workflows will call it and who configures them. The n8n and Make engagement builds workflows under version control, with custom nodes only where they earn their keep.
Related: n8n vs Make · n8n error handling · Self-hosted n8n · Custom AI development