Custom nodes
A node is a class implementing the SDK's INode interface. Two members are required: a description that declares what the node is, and an execute that does the work.
The deep authoring detail — worked examples of a transform node, a branching control node and an action node with an error port — is the Integration Node SDK. This page is the map: the four types you implement against, what the runtime gives you, and how the package is built.
The four types
| Type | What it is |
|---|---|
INode | The node itself: a description, an execute, and up to three optional author-time hooks. |
INodeDescription | The declaration: slug, version, category, name and description, inputs, output ports, and configuration fields. |
INodeContext | What the runtime hands execute: an AbortSignal, a logger, and resolvers for secrets and credentials. |
INodeResult | What execute returns: the outputs, and — for a branching node — which branch fired. |
INodeDescription is where most of the design work happens, because it is what the editor renders. The category decides where the node sits in the palette, the output ports decide what an author can wire, and the configuration fields decide what they can fill in. See Node configuration for the field-type grammar the editor understands, and the node catalog for how the shipped nodes use it.
What the runtime hands you
INodeContext is deliberately small:
signal— anAbortSignal. Propagate it to every I/O call you make. A cancelled or terminated run, and a node that hit itstimeoutSeconds, both come through this signal; a node that ignores it keeps working after the run has stopped caring.logger—info,warnanderror. What you log here is what an author reads in the node's test panel and in a step's detail, so log the things that explain a failure: the URL you called, the number of rows you read, the field that was missing.secrets.get(key)— resolves a secret referenced by one of yoursecret-reffields.credentials.get(id)— resolves the credential instance chosen in one of yourcredentials-reffields into live access data, including a freshly minted token where the credential type authenticates with OAuth.
Resolve access data inside execute, every time. Do not cache it across executions: a token resolved on a first attempt may be expired by a retry, and the whole point of resolving late is that a retried step gets current material.
Return an INodeResult. For a node with branch ports, name the branch that fired; for an action with an error port, that port is how an expected failure leaves the node — see Failure handling.
Project layout
A node package is an ordinary TypeScript package. The shape that works:
my-nodes/
package.json
tsup.config.ts
src/
index.ts exports NODES, and optionally CREDENTIALS and TEMPLATES
nodes/
greet/GreetNode.ts
credentials/
MyApiCredential.ts
templates/
myTemplate.ts
One class per node, one file per class, and a single index.ts that exports the arrays the build reads. Keep the description and the execute in the same file — an author debugging a node's behaviour wants to read both at once.
The build
Two steps, in this order:
{
"scripts": {
"build": "tsup && rvnxx-nodes manifest"
}
}
tsup compiles the package. Then the SDK's CLI, rvnxx-nodes, runs its manifest subcommand: it imports your built entry point, reads the exported arrays, and writes the manifest the registry consumes.
manifest is the CLI's only subcommand. There is deliberately no publish — packages are not published from their own repo. See Registration.
Run the build before you hand anything over. A missing manifest is the most common reason a package cannot be registered, and the CLI fails loudly if the compiled entry point is not there or does not export what it expects.
Where to go next
- Integration Node SDK — install, the worked examples, and the full type reference.
- Package manifest — what else one package can ship, and how slugs and versions work.
- Dynamic behaviour — the three optional hooks.
- Registration — getting it onto a tenant.