Connection SDK (Node.js)
Connect local services, internal APIs, and proprietary systems to Rapidfolio procedures without opening firewall ports.
Overview
The Connection SDK lets you expose functions from any service running inside your own infrastructure — internal APIs, proprietary databases, core banking systems — as callable tools within Rapidfolio procedures. No firewall rules, no public IP, no VPN required.
The SDK runs inside your service and maintains an outbound long-poll connection to Rapidfolio. When a procedure step needs to call one of your registered functions, Rapidfolio queues the invocation. The SDK picks it up, executes your handler locally, and posts the result back. The procedure then resumes with the result.
How It Works
Your service (SDK) Rapidfolio
│ │
│── GET /v1/sdk/poll ─────────────>│ (long-poll, waits for work)
│ │
│ [Procedure runs, calls your function]
│ │
│<── invocation payload ──────────│
│ │
│ [handler executes locally] │
│ │
│── POST /v1/sdk/result ──────────>│
│ │
│ [Procedure resumes] │
│ │
│── GET /v1/sdk/poll ─────────────>│ (immediately re-polls)
Key properties of this design:
- All connections are outbound from your service — nothing needs to be open inbound.
- Handlers run inside your network, with full access to your databases and internal services.
- The SDK reconnects and re-polls automatically on transient network errors.
- One SDK process can register multiple functions under a single connection.
Creating a Connection
- In the Rapidfolio dashboard, go to Connections → New Connection.
- Select Private Connection.
- Give the connection a name (e.g.
Core Banking API). - Rapidfolio generates two tokens — one for sandbox, one for live. Copy them now — they are shown once and cannot be retrieved again.
- Store the tokens in your service's environment variables (see Token Management).
Installation
npm install @rapidfolio/connection-sdk-node
Quick Start
import { Connection } from '@rapidfolio/connection-sdk-node'
import { z } from 'zod'
const conn = new Connection()
// Token is read automatically from the RAPID_TOKEN environment variable.
conn.register('getCustomer', {
description: 'Fetch a customer by their ID',
input: z.object({ customerId: z.string() }),
handler: async ({ customerId }) => {
return db.customers.findById(customerId)
}
})
await conn.start()
console.log('Connected to Rapidfolio')
Registering Functions
Each registered function needs a description (shown to the AI agent), an input Zod schema, and a handler. register is chainable:
conn
.register('getCustomer', {
description: 'Fetch a customer by their ID from the core banking system',
input: z.object({
customerId: z.string().describe('The customer ID to look up')
}),
handler: async ({ customerId }, ctx) => {
console.log(`[${ctx.environment}] Looking up customer ${customerId}`)
return db.customers.findById(customerId)
}
})
.register('createTransfer', {
description: 'Initiate an internal bank transfer between two accounts',
input: z.object({
fromAccount: z.string().describe('Source account number'),
toAccount: z.string().describe('Destination account number'),
amount: z.number().positive().describe('Amount to transfer'),
currency: z.string().default('GBP').describe('ISO 4217 currency code')
}),
requiresReview: true,
handler: async ({ fromAccount, toAccount, amount, currency }) => {
return bankingService.transfer({ fromAccount, toAccount, amount, currency })
}
})
.register('freezeAccount', {
description: 'Freeze a customer account to prevent further transactions',
input: z.object({
accountId: z.string(),
reason: z.string().describe('Reason for the freeze, recorded in audit log')
}),
requiresReview: true,
handler: async ({ accountId, reason }, ctx) => {
console.log(`[${ctx.runId}] Freezing account ${accountId}: ${reason}`)
return bankingService.freeze(accountId, reason)
}
})
await conn.start()
Human Review Gate
Set requiresReview: true to pause the procedure run for human approval before your handler is called:
- Approved — your handler is called with the proposed arguments. The procedure resumes with the result.
- Rejected — your handler is not called. The procedure receives a rejection signal and can handle it gracefully.
This is useful for high-impact operations such as fund transfers, account freezes, or any action that should require a second pair of eyes before executing.
Handler Context
Every handler receives an InvocationContext as its second argument:
| Property | Type | Description |
|---|---|---|
ctx.id | string | Unique ID for this invocation |
ctx.environment | 'sandbox' | 'live' | The environment this run is executing in |
ctx.runId | string | null | The ID of the current procedure run |
ctx.procedureId | string | null | The ID of the procedure being executed |
ctx.procedureVersion | string | null | The version name of the procedure |
Use ctx.environment to apply different behaviour in sandbox vs. live:
handler: async ({ customerId }, ctx) => {
const db = ctx.environment === 'sandbox' ? sandboxDb : productionDb
return db.customers.findById(customerId)
}
Token Management
Rapidfolio issues two tokens per connection:
| Token prefix | Environment | Usage |
|---|---|---|
run_sandbox_xxx | Sandbox | Development and testing |
run_live_xxx | Live | Production traffic |
Set the appropriate token via the RAPID_TOKEN environment variable, or pass it explicitly:
RAPID_TOKEN=run_sandbox_xxxxxxxxxxxx node server.js
const conn = new Connection({ token: 'run_sandbox_xxxxxxxxxxxx' })
Regenerating Tokens
If a token is compromised or lost, regenerate it in the dashboard:
- Go to Connections → your connection → Regenerate Token.
- The old token is immediately invalidated — your running SDK will receive a
401on the next poll and stop. - Update
RAPID_TOKENin your environment and restart your service.
401 Behaviour
When the SDK receives a 401 it stops polling immediately and logs a clear error. It does not retry with an invalid token.
Connection Status API
Check whether your SDK is currently connected and see which functions are registered:
GET /api/v1/connections/:connectionId/private-connection/status?environment=sandbox
Response:
{
"status": "connected",
"lastPollAt": "2024-03-15T10:30:00.000Z",
"registeredFunctions": [
"getCustomer",
"createTransfer",
"freezeAccount"
]
}
status is "connected" if the SDK has polled within the last 60 seconds, otherwise "disconnected".
Local Testing
Use conn.connectLocal() to test handlers in-process without any network calls or token. Useful for unit tests:
import { Connection } from '@rapidfolio/connection-sdk-node'
import { z } from 'zod'
const conn = new Connection()
conn.register('getCustomer', {
description: 'Fetch a customer by ID',
input: z.object({ customerId: z.string() }),
handler: async ({ customerId }) => ({
id: customerId,
name: 'Test User',
status: 'active'
})
})
const local = conn.connectLocal({ environment: 'sandbox' })
const result = await local.invoke('getCustomer', { customerId: 'cust_test' })
// result => { id: 'cust_test', name: 'Test User', status: 'active' }
Error Handling
Throw an error inside a handler to report a failure back to the procedure:
handler: async ({ customerId }) => {
const customer = await db.customers.findById(customerId)
if (!customer) throw new Error(`Customer ${customerId} not found`)
return customer
}
The error message is surfaced in the procedure run's step output.
Security
- Tokens are long-lived bearer tokens. Treat them like passwords.
- Always store tokens in environment variables — never hard-code them.
- Use the sandbox token during development; switch to live only in production.
- Rotate tokens immediately if you suspect exposure.
- The SDK communicates over HTTPS only.