FunnelFlux Pro API DocumentsFunnelFlux Pro API Documents
  • Changelog
  • Status
  • Dashboard
  • Documentation
  • Authentication
  • Domains
  • Assets
  • Reporting
Getting Started
    General InformationAPI Best PracticesAuthenticationGenerating Asset IDsCreating Funnels
Feature Information
    Logic ScriptsMiddleware NodesMiddleware TemplatesWebhook Payloads
Other
    Changelog
Feature Information

Middleware Templates

These templates are copy-ready starting points for synchronous Middleware Node destinations. They are not SDKs. Adapt the decision logic, keep the request/response shape stable, and return fast enough for live routing.

Middleware receivers should:

  • Treat every nested object and field as optional.
  • Return JSON before execute_timeout_ms with at least a 250ms internal safety buffer.
  • Use a stable response field such as decision so routes can evaluate body_json.decision.
  • Avoid logging the full payload because it can contain IPs, user agents, URL parameters, and action data.
  • Keep external API client timeouts low - remember this is hot-path routing so design accordingly.
  • Read secrets from environment variables instead of hardcoding them.

Recommended route conditions:

RouteCondition
Approvedbody_json.decision is approved
Rejectedbody_json.decision is rejected
Reviewbody_json.decision is review
DefaultNo conditions; used for fallback, no-match, or delivery failure.

AI Prompt

Copy this prompt into your AI tool, then paste the Middleware Nodes documentation after it if the tool needs more context.

Code
You are creating a synchronous FunnelFlux middleware destination. Read the FunnelFlux middleware payload documentation first. The function receives the FunnelFlux middleware JSON payload with schema_version, event_type, request_id, timestamp, execute_timeout_ms, visitor, funnel, traffic_source, hit, session, tracking_fields, url_buffer, action_data, and visitor_tags. Requirements: - Treat every nested object and field as optional. - Never throw because visitor/session/action/tracking data is missing. - Be aware of passed `execute_timeout_ms` as a cap on sensible processing time. - Keep an internal safety buffer of at least 250 ms. - If calling external APIs, set client timeouts to intelligently handle slow/unresponsive APIs, with a documented fallback decision. - Keep payload parsing, response formatting, and decision logic separate. - Return a stable response with decision, route, score, reason, request_id, and schema_version so FunnelFlux routes are easy to configure. - Use only deterministic logic unless external API calls have strict timeouts. - Do not log the full payload or secrets. - Do not expose API keys in the code. Read secrets from environment variables. - The route decision must be readable by FunnelFlux with body_json.decision or similar - Preserve FunnelFlux field names exactly. Implement the function for: <platform/language>. Business rule: <describe rule>. Allowed decisions: <list of decisions here>. Return only the function code. Do not wrap it in Markdown.

JavaScript

Works as a base for Node.js HTTP servers, serverless adapters, Cloudflare Workers, and Google Cloud Functions. Keep the generic helpers, then replace decide with your own routing logic.

Code
function objectValue(value) { return value && typeof value === "object" && !Array.isArray(value) ? value : {}; } function stringValue(value, fallback = "") { return typeof value === "string" ? value : fallback; } function numberValue(value, fallback = 0) { return typeof value === "number" && Number.isFinite(value) ? value : fallback; } function mapGet(map, key, fallback = "") { return stringValue(objectValue(map)[key], fallback); } function responseFor(payload, decision, reason, score = 0) { return { decision, route: decision, score, reason, request_id: stringValue(payload.request_id), schema_version: stringValue(payload.schema_version, "1.0"), }; } function decide(payload) { // Replace this function with your own deterministic routing logic. // Read optional values defensively: // const trackingValue = mapGet(payload.tracking_fields, "c1"); // const actionValue = mapGet(payload.action_data, "lead_score"); // const visitorTagIds = Array.isArray(payload.visitor_tags) // ? payload.visitor_tags // : []; return responseFor( payload, "review", "replace decide() with business logic", 0, ); } async function handleFunnelFluxMiddleware(request) { const startedAt = Date.now(); let payload = {}; try { payload = await request.json(); } catch (_) { payload = {}; } const timeoutMs = Math.max(1, numberValue(payload.execute_timeout_ms, 5000)); const deadlineMs = startedAt + Math.max(1, timeoutMs - 250); let response = decide(objectValue(payload)); if (Date.now() > deadlineMs) { response = responseFor( payload, "review", "middleware safety deadline reached", 0, ); } return new Response(JSON.stringify(response), { status: 200, headers: { "content-type": "application/json" }, }); }

Cloudflare Workers

Paste the JavaScript helper functions above this worker export.

Code
export default { async fetch(request, env, ctx) { if (request.method !== "POST") { return Response.json( { decision: "review", reason: "POST required" }, { status: 200 }, ); } return handleFunnelFluxMiddleware(request); }, };

Google Cloud Functions, Node.js

Use this with the JavaScript helper functions. Configure the function timeout above the FunnelFlux timeoutMs, but keep your own external calls below the FunnelFlux deadline.

Code
exports.funnelfluxMiddleware = async (req, res) => { const payload = req.body && typeof req.body === "object" ? req.body : {}; const result = decide(payload); res.status(200).json(result); };

TypeScript

Code
type StringMap = Record<string, string>; interface FunnelFluxMiddlewarePayload { schema_version?: string; event_type?: string; request_id?: string; timestamp?: number; execute_timeout_ms?: number; is_action?: boolean; action_number?: number; visitor?: { visitor_id?: string; ip?: string; user_agent?: string; country_code?: string; country_name?: string; region?: string; city?: string; timezone?: string; language?: string; device_type?: string; browser?: string; os?: string; connection_type?: string; }; funnel?: { funnel_id?: string; funnel_name?: string; node_id?: string; node_name?: string; }; traffic_source?: { traffic_source_id?: string; name?: string; }; hit?: Record<string, unknown>; session?: { visitor_id?: string; hit_id?: string; entrance_id?: string; entrance_timestamp?: number; hit_timestamp?: number; reference_hit_id?: string; most_recent_page_id?: string; referrer?: string; }; tracking_fields?: StringMap; url_buffer?: StringMap; action_data?: Record<string, unknown>; visitor_tags?: string[]; } interface MiddlewareDecision { decision: "approved" | "rejected" | "review"; route: string; score: number; reason: string; request_id: string; schema_version: string; } function asString(value: unknown, fallback = ""): string { return typeof value === "string" ? value : fallback; } function asMap(value: unknown): Record<string, unknown> { return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {}; } function getString(map: unknown, key: string, fallback = ""): string { return asString(asMap(map)[key], fallback); } function baseDecision( payload: FunnelFluxMiddlewarePayload, decision: MiddlewareDecision["decision"], score: number, reason: string, ): MiddlewareDecision { return { decision, route: decision, score, reason, request_id: asString(payload.request_id), schema_version: asString(payload.schema_version, "1.0"), }; } export function decide(payload: FunnelFluxMiddlewarePayload): MiddlewareDecision { // Replace this function with your own deterministic routing logic. // Read optional values defensively: // const trackingValue = getString(payload.tracking_fields, "c1"); // const actionValue = getString(payload.action_data, "lead_score"); // const visitorTagIds = Array.isArray(payload.visitor_tags) // ? payload.visitor_tags // : []; return baseDecision( payload, "review", 0, "replace decide() with business logic", ); }

Python

Works with Google Cloud Functions or Flask-style request objects.

Code
import time from typing import Any, Dict def obj(value: Any) -> Dict[str, Any]: return value if isinstance(value, dict) else {} def str_value(value: Any, fallback: str = "") -> str: return value if isinstance(value, str) else fallback def num_value(value: Any, fallback: int = 0) -> int: return value if isinstance(value, (int, float)) else fallback def map_get(value: Any, key: str, fallback: str = "") -> str: return str_value(obj(value).get(key), fallback) def response_for( payload: Dict[str, Any], decision: str, reason: str, score: int = 0, ) -> Dict[str, Any]: return { "decision": decision, "route": decision, "score": score, "reason": reason, "request_id": str_value(payload.get("request_id")), "schema_version": str_value(payload.get("schema_version"), "1.0"), } def decide(payload: Dict[str, Any]) -> Dict[str, Any]: # Replace this function with your own deterministic routing logic. # Read optional values defensively: # tracking_value = map_get(payload.get("tracking_fields"), "c1") # action_value = map_get(payload.get("action_data"), "lead_score") # visitor_tag_ids = payload.get("visitor_tags") # if not isinstance(visitor_tag_ids, list): # visitor_tag_ids = [] return response_for( payload, "review", "replace decide() with business logic", 0, ) def funnelflux_middleware(request): started_ms = int(time.time() * 1000) try: payload = request.get_json(silent=True) or {} except Exception: payload = {} timeout_ms = max(1, int(num_value(payload.get("execute_timeout_ms"), 5000))) deadline_ms = started_ms + max(1, timeout_ms - 250) result = decide(obj(payload)) if int(time.time() * 1000) > deadline_ms: result = response_for( payload, "review", "middleware safety deadline reached", 0, ) return (result, 200, {"content-type": "application/json"})

Go

Code
package middleware import ( "encoding/json" "net/http" "time" ) type Payload struct { SchemaVersion string `json:"schema_version"` EventType string `json:"event_type"` RequestID string `json:"request_id"` Timestamp int64 `json:"timestamp"` ExecuteTimeoutMS int64 `json:"execute_timeout_ms"` IsAction bool `json:"is_action"` ActionNumber int `json:"action_number"` Visitor map[string]any `json:"visitor"` Funnel map[string]any `json:"funnel"` TrafficSource map[string]any `json:"traffic_source"` Hit map[string]any `json:"hit"` Session map[string]any `json:"session"` TrackingFields map[string]string `json:"tracking_fields"` URLBuffer map[string]string `json:"url_buffer"` ActionData map[string]any `json:"action_data"` VisitorTags []string `json:"visitor_tags"` } type Decision struct { Decision string `json:"decision"` Route string `json:"route"` Score int `json:"score"` Reason string `json:"reason"` RequestID string `json:"request_id"` SchemaVersion string `json:"schema_version"` } func Handler(w http.ResponseWriter, r *http.Request) { started := time.Now() var payload Payload _ = json.NewDecoder(r.Body).Decode(&payload) timeout := time.Duration(payload.ExecuteTimeoutMS) * time.Millisecond if timeout <= 0 { timeout = 5 * time.Second } deadline := started.Add(timeout - 250*time.Millisecond) result := decide(payload) if time.Now().After(deadline) { result = baseDecision( payload, "review", 0, "middleware safety deadline reached", ) } w.Header().Set("content-type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(result) } func decide(payload Payload) Decision { // Replace this function with your own deterministic routing logic. // Read optional values defensively: // trackingValue := payload.TrackingFields["c1"] // actionValue := stringFromAny(payload.ActionData["lead_score"]) // visitorTagIDs := payload.VisitorTags return baseDecision( payload, "review", 0, "replace decide() with business logic", ) } func baseDecision( payload Payload, decision string, score int, reason string, ) Decision { schemaVersion := payload.SchemaVersion if schemaVersion == "" { schemaVersion = "1.0" } return Decision{ Decision: decision, Route: decision, Score: score, Reason: reason, RequestID: payload.RequestID, SchemaVersion: schemaVersion, } } func stringFromAny(value any) string { if s, ok := value.(string); ok { return s } return "" }

Operational Notes

  • Recommended FunnelFlux timeoutMs: 1000 to 5000 ms for simple functions.
  • Maximum FunnelFlux timeoutMs: 30000 ms.
  • Be aware of external API client timeouts - use some appropriate values.
  • For AI-generated functions, require the model to preserve field names exactly and keep body_json.decision route-compatible.
  • HMAC verification examples should be documented separately per platform. These templates do not hardcode secrets or assume every destination uses HMAC.
Last modified on June 16, 2026
Middleware NodesWebhook Payloads
On this page
  • AI Prompt
  • JavaScript
    • Cloudflare Workers
    • Google Cloud Functions, Node.js
  • TypeScript
  • Python
  • Go
  • Operational Notes
Javascript
Javascript
Javascript
TypeScript
Go