Dolfin Custom Functions & Developer API #
The Dolfin Custom Functions Engine enables developers to write, compile, and execute isolated TypeScript functions directly inside the Dolfin platform. Functions can be triggered by user interactions (lead row actions), automated stage transitions (on entering or exiting a pipeline stage), or scheduled background jobs.
All custom functions are authored in TypeScript and automatically transpiled to secure ES2020 JavaScript upon saving in the Custom Functions IDE (Ctrl+S).
Execution Model & Architecture #
Each custom function invocation executes inside a dedicated, memory-capped V8 Isolate. This architecture provides strict process isolation, preventing rogue scripts from blocking the main API thread or leaking tenant data.
┌─────────────────────────────────────────────────────────────┐
│ Trigger Event │
│ • Lead Table Row Action │
│ • Pipeline Stage Transition (On Enter / On Exit) │
│ • Background Automation Dispatcher │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Isolated V8 Sandbox │
│ │
│ async function run(context: Context, dolfin: DolfinApi) │
│ │
│ ├── Context: Injected Lead, Contact & User metadata │
│ ├── dolfin.crm.module(id): Scoped CRM database bridge │
│ └── dolfin.http: Outbound HTTP with SSRF filter │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Execution Logs & Database Persistence │
│ • Status (Success / Failed / Timeout) │
│ • Captured Console Output (stdout / stderr) │
│ • Execution Duration (ms) & Context Payload Audit │
└─────────────────────────────────────────────────────────────┘
Global Runtime Signature #
Every custom function exports a top-level asynchronous run entry point accepting two ambient arguments:
async function run(context: Context, dolfin: DolfinApi): Promise<void> {
// 1. Destructure entity data from runtime context
const { lead } = context;
console.log(`Executing function for lead "${lead.name}" (ID: ${lead.id})`);
// 2. Obtain module-scoped CRM API bridge
const crm = await dolfin.crm.module(lead.moduleId);
// 3. Perform CRM operations or outbound HTTP integrations
await crm.leads.notes.add(
lead.id,
"Custom function executed successfully."
);
}
Ambient Parameters #
| Parameter | Type | Description |
|---|---|---|
context | Context | Contains immutable runtime data for the triggering entity (e.g. lead.id, lead.name, lead.meta). |
dolfin | DolfinApi | Primary SDK client providing dolfin.crm and dolfin.http APIs. |
console | Console | Provides console.log, console.warn, and console.error. All logged output is streamed into the Execution Logs drawer. |
API Namespaces #
Dolfin exposes two primary API groups:
1. dolfinApi.crm
#
Access module-scoped CRM entities, update custom metadata attributes (lead.meta), transition stages, reassign leads, add internal timeline notes, and query activity logs.
const crm = await dolfin.crm.module(lead.moduleId);
await crm.leads.update(lead.id, { meta: { status: "verified" } });
Explore dolfinApi.crm Documentation →
2. dolfinApi.http
#
Make authenticated outbound HTTP requests (GET, POST, PUT, DELETE) to external services (Slack, Discord, Zapier, Webhooks, ERPs) with built-in SSRF protections and automatic JSON serialization.
const res = await dolfin.http.post("https://api.external.com/v1/sync", { leadId: lead.id });
Explore dolfinApi.http Documentation →
3. Custom Listing Pages & Actions #
Build reactive, code-first data listing views with cascading filters, system-managed pagination, primary key bindings, and typed single-row and bulk actions powered by filters.ts, fetcher.ts, and actions/*.ts.
// Fetcher (fetcher.ts)
export async function run(context: CustomListContext, dolfin: DolfinSdk): Promise<CustomListResponse> {
const crm = await dolfin.crm.module(context.moduleId);
const result = await crm.leads.list({ page: context.pagination.page, limit: context.pagination.limit });
return { items: result.data || [], total: result.total || 0 };
}
// Single-Row Action (actions/qualify.ts)
export async function run(context: CustomListRowActionContext, dolfin: DolfinSdk): Promise<CustomListActionResponse> {
const crm = await dolfin.crm.module(context.moduleId);
await crm.leads.update(Number(context.row.id), { meta: { status: "qualified" } });
return { success: true, message: "Lead qualified!", refresh: true };
}
Explore Custom Listing Pages Documentation → | Custom Actions & Row Operations →
Engine Constraints & Safety Budgets #
To ensure high performance and prevent infinite loops, the engine enforces strict limits:
| Metric | Limit | Failure Behavior |
|---|---|---|
| Execution Timeout | 10,000 ms (10 seconds) | Process terminated; recorded with status timeout. |
| Isolate Memory Cap | 64 MB | Script terminates if memory threshold is exceeded. |
| CRM Call Budget | 50 operations per invocation | Throws Exceeded maximum CRM calls per execution (50). |
| HTTP Call Budget | 5 requests per invocation | Throws Exceeded maximum HTTP calls per execution (5). |
| HTTP Request Timeout | 8,000 ms per request | Aborts request via AbortController signal. |
| Max HTTP Response Size | 1 MB (1,048,576 bytes) | Throws Response size exceeds maximum limit. |
| SSRF Guardrail | Prohibits private CIDRs & localhost | Throws SSRF Blocked: URL resolves to forbidden range. |
The CRM Call Budget is shared across all module instances created within a single script invocation. Avoid executing unbounded database reads or writes in loops.
Viewing Execution History & Logs #
Every execution generates a persistent audit log accessible from the Custom Functions management screen:
- Duration: Exact runtime execution time in milliseconds.
- Status:
Success,Failed, orTimeout. - Console Output: Real-time captures of all
console.logstatements. - Error Trace: Full stack trace and error message if an uncaught exception occurred.
- Context Snapshot: Sanitized JSON payload representing the entity state at time of invocation.