---
updatedAt: 2026-07-06T08:42:45.000Z
agentTools:
  projectIndex: https://docs.hud.io/llms.txt
---

# Mark as Flow (Manual Flows)

Manually mark code as a flow to track entry points that aren't covered by Hud's automatic instrumentation.

## 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 type | Use it for                                            | Where it appears in Hud | API                                                                  |
| --------- | ----------------------------------------------------- | ----------------------- | -------------------------------------------------------------------- |
| HTTP      | Unsupported HTTP endpoints                            | **Endpoints** page      | `setHttpFlow`                                                        |
| Queue     | Unsupported queues                                    | **Queues** page         | `startFlow` / `endFlow` or `wrapFlow`, both with `{ type: "queue" }` |
| Custom    | Everything else (e.g. scripts, scheduled tasks, etc.) | **Custom Flows** page   | `startFlow` / `endFlow`, `wrapFlow`                                  |

All of these APIs are exported from `hud-sdk/api` and work together with [`setContext`](https://docs.hud.io/docs/setting-custom-context-for-requests-nodejs) and [`setFailure`](https://docs.hud.io/docs/mark-a-flow-as-failed-nodejs).

<br />

***

## Requirements

| SDK     | Minimum version                            | Supported flow types                                        |
| ------- | ------------------------------------------ | ----------------------------------------------------------- |
| Node.js | `1.8.12` (HTTP & Custom), `1.8.14` (Queue) | HTTP, Custom, Queue                                         |
| Python  | `0.4.10`                                   | Custom, Queue ([see Python guide](https://docs.hud.io/docs/mark-as-flow-python)) |

<Callout icon="🚧" theme="warn">
  ### 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.
</Callout>

***

## 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.

```typescript Typescript
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);
```
```javascript JavaScript
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);
```

<Callout icon="👉" theme="default">
  ###

  `setHttpFlow` must be called **during an active HTTP request** that Hud is already tracking. Calling it outside of an HTTP context is skipped.
</Callout>

***

## 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.&#x20;

If the function throws an exception, it is considered a flow failure and will be counted toward the flow error rate.

```typescript Typescript
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');
```
```javascript JavaScript
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.

```typescript Typescript
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();
  }
}
```
```javascript JavaScript
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();
  }
}
```

<Callout icon="👉" theme="default">
  ###

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

***

## Mark Queue flows

To instrument a **custom or in-house queue** consumer, pass `{ type: "queue" }` in the options of `startFlow` or `wrapFlow`.

Set `framework` to a short string identifying your queue (e.g. `"kafka"`, `"rabbitmq"`). It defaults to `"custom"`.

For queue flows using `startFlow` you can also pass two extra options:

* `enqueueTime` — a `Date` of when the message was enqueued/produced. Hud uses it to record the **end-to-end duration** (how long the message waited in the queue before processing finished). A value that isn't a `Date`, is in the future, or is more than a year in the past is ignored.
* `messageCount` — the number of messages handled in this invocation. Must be a positive integer; any other value is ignored.

### Option A: `startFlow` / `endFlow` (per-message metadata)

```typescript Typescript
import { startFlow, endFlow } from 'hud-sdk/api';

// In your consumer loop, for each message:
startFlow('my-kafka-topic', {
  type: 'queue',
  framework: 'kafka',
  enqueueTime: message.timestamp, // a Date of when the message was produced
  messageCount: 1,
});
try {
  await processMessage(message);
} finally {
  endFlow();
}
```
```javascript JavaScript
const { startFlow, endFlow } = require('hud-sdk/api');

// In your consumer loop, for each message:
startFlow('my-kafka-topic', {
  type: 'queue',
  framework: 'kafka',
  enqueueTime: message.timestamp, // a Date of when the message was produced
  messageCount: 1,
});
try {
  await processMessage(message);
} finally {
  endFlow();
}
```

### Option B: `wrapFlow`

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

const handleMessage = wrapFlow(
  'my-kafka-topic',
  async (message) => {
    await processMessage(message);
  },
  { type: 'queue', framework: 'kafka' },
);

// Each call is tracked as the "my-kafka-topic" queue flow:
await handleMessage(message);
```
```javascript JavaScript
const { wrapFlow } = require('hud-sdk/api');

const handleMessage = wrapFlow(
  'my-kafka-topic',
  async (message) => {
    await processMessage(message);
  },
  { type: 'queue', framework: 'kafka' },
);

// Each call is tracked as the "my-kafka-topic" queue flow:
await handleMessage(message);
```

***

## Combining with context and failures

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

* [`setContext`](https://docs.hud.io/docs/setting-custom-context-for-requests-nodejs) — attach metadata (e.g. `orderId`, `tenant`) to the current flow's forensics.
* [`setFailure`](https://docs.hud.io/docs/mark-a-flow-as-failed-nodejs) — explicitly mark the current flow as failed even when no exception was thrown.

```typescript Typescript
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

```typescript
// Options shared by startFlow and wrapFlow (all fields optional)
interface FlowOptions {
  type?: 'custom' | 'queue'; // defaults to 'custom'
  framework?: string;        // defaults to 'custom'
  enqueueTime?: Date;        // queue flows only; sets the end-to-end duration
  messageCount?: number;     // queue flows only; positive integer
}

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

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

<br />