Mark as Flow (Manual Flows)

Overview

Hud automatically instruments your application's entry points — HTTP frameworks, message queues, GraphQL, and more — with no code changes. Manual flows let you extend this to entry points that automatic instrumentation doesn't cover.

When you mark a block of code as a flow, the Hud SDK collects flow metrics and forensics for it, and Hud treats it as a first-class entry point: it appears in the UI and MCP, and gets issue detection, error rates, duration graphs, and related issues — just like an auto-detected flow.

There are three kinds of manual flows:

Flow typeUse it forWhere it appears in HudAPI
HTTPUnsupported HTTP endpointsEndpoints pagesetHttpFlow
QueueUnsupported queuesQueues pagewrapQueue (upcoming version)
CustomEverything else (e.g. scripts, scheduled tasks, etc.)Custom Flows pagestartFlow / endFlow, wrapFlow

All of these APIs are exported from hud-sdk/api and work together with setContext and setFailure.



Requirements

SDKMinimum versionSupported flow types
Node.js1.8.12HTTP, Custom (Queue upcoming)
Pythoncoming soon
🚧

Important Notes

  • All manual flow APIs must be called after register() — calling them during startup before Hud is initialized has no effect.
  • Flow names must be stable, non-empty strings. Avoid high-cardinality names (e.g. embedding user IDs or timestamps) — put variable data in setContext instead. The number of distinct custom flow names is capped.
  • Manual flows cannot be nested: calling startFlow / wrapFlow inside an already-active flow (or inside an HTTP request) is skipped.

Mark HTTP flows

Use setHttpFlow(route, method) to define the route and method of the current HTTP request. Call it from inside the request handler. This is useful when the route can't be inferred automatically.

The flow appears on the Endpoints page, named by the route and method you provide.

import { setHttpFlow } from 'hud-sdk/api';

// Inside your request handler, while processing the request:
const server = http.createServer((req, res) => {
  setHttpFlow('/hello-world', 'GET');
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello, World!\n');
});

server.listen(3000);
const { setHttpFlow } = require('hud-sdk/api');

const server = http.createServer((req, res) => {
  setHttpFlow('/hello-world', 'GET');
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello, World!\n');
});

server.listen(3000);
👉

setHttpFlow must be called during an active HTTP request that Hud is already tracking. Calling it outside of an HTTP context is skipped.


Mark Custom flows

Use custom flows for code that isn't an HTTP endpoint or a queue — scripts, main functions, scheduled tasks, cron jobs, background loops, and so on.

Custom flows appear on the Custom Flows page, which includes a table of all custom flows plus a dedicated page per flow with detailed graphs, functions, forensics, and related issues.

There are two ways to define a custom flow.

Option A: wrapFlow

wrapFlow(name, handler) returns a wrapped version of your function. Every time the wrapped function is called, that invocation is tracked as a flow. It works for both sync and async functions, and the return value (and arguments) are preserved.

import { wrapFlow } from 'hud-sdk/api';

const generateNightlyReport = wrapFlow('nightly-report', async (date: string) => {
  const data = await collectData(date);
  return buildReport(data);
});

// Each call is tracked as the "nightly-report" flow:
await generateNightlyReport('2026-06-17');
const { wrapFlow } = require('hud-sdk/api');

const generateNightlyReport = wrapFlow('nightly-report', async (date) => {
  const data = await collectData(date);
  return buildReport(data);
});

// Each call is tracked as the "nightly-report" flow:
await generateNightlyReport('2026-06-17');

Option B: startFlow / endFlow

When you can't wrap a single function, bracket the code manually with startFlow(name) and endFlow(failureReason?). Pass an optional failure reason to endFlow to mark the flow as failed.

import { startFlow, endFlow, setFailure } from 'hud-sdk/api';

async function runCronJob() {
  startFlow('cleanup-expired-sessions');
  try {
    await deleteExpiredSessions();
  } catch (err) {
    setFailure('CleanupFailed');
    throw err;
  } finally {
    endFlow();
  }
}
const { startFlow, endFlow, setFailure } = require('hud-sdk/api');

async function runCronJob() {
  startFlow('cleanup-expired-sessions');
  try {
    await deleteExpiredSessions();
  } catch (err) {
    setFailure('CleanupFailed');
    throw err;
  } finally {
    endFlow();
  }
}
👉

Always pair every startFlow with an endFlow (including on the error path). An endFlow without a preceding startFlow is skipped.


Mark Queue flows

🔜

Upcoming version. wrapQueue is planned for an upcoming Node SDK release and is documented here for reference.

Use wrapQueue(queueName, payload, handler) to instrument a custom or in-house queue consumer. The flow appears on the Queues page as a first-class queue (not as a custom flow), and no KafkaJS / SQS dependency is required.

If you pass the message's insertionTime, Hud also records the end-to-end duration — how long the message waited in the queue before it was processed. The payload is captured into forensics (not the metric).

import { wrapQueue } from 'hud-sdk/api';

await wrapQueue('orders', message.payload, async () => {
  await processOrder(message.payload);
});

// With end-to-end timing:
await wrapQueue(
  'orders',
  message.payload,
  { insertionTime: message.enqueuedAt }, // a Date
  async () => {
    await processOrder(message.payload);
  },
);
const { wrapQueue } = require('hud-sdk/api');

await wrapQueue('orders', message.payload, async () => {
  await processOrder(message.payload);
});

// With end-to-end timing:
await wrapQueue(
  'orders',
  message.payload,
  { insertionTime: message.enqueuedAt }, // a Date
  async () => {
    await processOrder(message.payload);
  },
);

Combining with context and failures

Manual flows work with the same helpers as auto-detected flows:

  • setContext — attach metadata (e.g. orderId, tenant) to the current flow's forensics.
  • setFailure — explicitly mark the current flow as failed even when no exception was thrown.
import { wrapFlow, setContext, setFailure } from 'hud-sdk/api';

const processBatch = wrapFlow('process-batch', async (batchId: string) => {
  setContext({ batchId });

  const result = await runBatch(batchId);
  if (result.rejected) {
    setFailure('BatchRejected', { reason: result.reason });
  }
  return result;
});

API reference

// HTTP flows
setHttpFlow(route: string, method: string): void

// Custom flows
startFlow(flowName: string): void
endFlow(failureReason?: string): void
wrapFlow<Args extends unknown[], T>(
  flowName: string,
  handler: (...args: Args) => T,
): (...args: Args) => T

// Queue flows (upcoming)
wrapQueue<T>(queueName: string, payload: unknown, handler: () => T): T
wrapQueue<T>(
  queueName: string,
  payload: unknown,
  attributes: { insertionTime?: Date },
  handler: () => T,
): T