> ## Documentation Index
> Fetch the complete documentation index at: https://docs.failpath.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Instrument a Backend Flow with Failpath End to End

> Walk through adding Failpath step instrumentation to a real backend function, from initializing the CLI to viewing events on your dashboard.

This guide walks you through instrumenting a backend function with Failpath from scratch. You will initialize the CLI, inspect the generated flow definition, install the SDK, create a client, wrap your business steps, and publish your flow so events appear on your dashboard.

<Steps>
  <Step title="Initialize your project">
    Run the following command in the root of your backend repository, replacing `fp_project_xxx` with your actual project key:

    ```bash theme={"dark"}
    npx failpath init --project-key fp_project_xxx
    ```

    The `init` command does five things:

    * Writes `FAILPATH_PROJECT_KEY` to a `.env` file
    * Adds `.env` to your `.gitignore`
    * Creates `.failpath/AGENTS.md`
    * Pulls your project's current dashboard graph into `.failpath/flows.json`
    * Generates `.failpath/sdk.ts` for typed flow and step keys
  </Step>

  <Step title="Inspect the generated flow keys">
    Open `.failpath/flows.json`. You will see one or more flow objects. Two fields are important when writing SDK code:

    * **`flow.slug`** — pass this string as the first argument to `run()`. It identifies which flow a run belongs to.
    * **`node.sdkStepKey`** — pass this string as the first argument to `step()`. It maps a recorded event to a specific node on your dashboard graph.

    ```json theme={"dark"}
    {
      "flows": [
        {
          "slug": "checkout",
          "nodes": [
            { "sdkStepKey": "validate-cart", "label": "Validate Cart" },
            { "sdkStepKey": "charge-card", "label": "Charge Card" },
            { "sdkStepKey": "send-receipt-email", "label": "Send Receipt Email" }
          ]
        }
      ]
    }
    ```

    The CLI also generates `.failpath/sdk.ts` from this graph. Import `failpathFlows` from that file when you instrument code so the slugs and step keys autocomplete.
  </Step>

  <Step title="Install @failpath/sdk">
    Add the SDK to your project:

    <CodeGroup>
      ```bash npm theme={"dark"}
      npm install @failpath/sdk
      ```

      ```bash pnpm theme={"dark"}
      pnpm add @failpath/sdk
      ```

      ```bash bun theme={"dark"}
      bun add @failpath/sdk
      ```
    </CodeGroup>
  </Step>

  <Step title="Create the client">
    Create the Failpath client once at the module level and export it as a singleton. Importing the same instance across your codebase ensures that configuration — like `defaultMetadata` — is applied consistently everywhere.

    ```typescript theme={"dark"}
    // lib/failpath.ts
    import { createTypedFailpathClient } from "../.failpath/sdk";

    export const failpath = createTypedFailpathClient({
      projectKey: process.env.FAILPATH_PROJECT_KEY!,
      defaultMetadata: { environment: "production" },
    });
    ```

    The `projectKey` is read from the environment variable written by `npx failpath init`. In local development your `.env` file supplies it automatically.
  </Step>

  <Step title="Wrap your function steps">
    Import your client and `failpathFlows`, then use `run()` to start a run and wrap each business step with `step()`. Reuse the same `runId` for every step that belongs to the same request.

    ```typescript theme={"dark"}
    import { failpathFlows } from "../.failpath/sdk";
    import { failpath } from "./lib/failpath";

    export async function handleCheckout(requestId: string) {
      const run = failpath.run(failpathFlows.checkout.slug, {
        runId: requestId,
        metadata: { route: "/checkout" },
      });

      const cart = await run.step(failpathFlows.checkout.steps.validateCart, async () => {
        return validateCart();
      });

      const charge = await run.step(failpathFlows.checkout.steps.chargeCard, async () => {
        return chargeCard(cart);
      });

      await run.step(failpathFlows.checkout.steps.sendReceiptEmail, async () => {
        return sendReceiptEmail(charge.receiptAddress);
      });

      await failpath.flush();
    }
    ```

    Each `step()` call queues a `running` event before the wrapped operation executes, then queues `success` or `error` when it finishes. Sends run in the background by default, so call `flush()` before a short-lived request, job, or script exits unless your runtime keeps background work alive with `waitUntil`.

    <Tip>
      Wrap business steps like `validateCart`, `chargeCard`, and `sendReceiptEmail` — not tiny helpers, utility functions, or repository calls. Each wrapped step should represent a meaningful unit of work you want to track on your dashboard.
    </Tip>
  </Step>

  <Step title="Publish and view your dashboard">
    Push your local flow definition to Failpath:

    ```bash theme={"dark"}
    npx failpath publish
    ```

    Then trigger your function — call the endpoint, run the job, or fire the webhook — and open your Failpath dashboard. You will see the run appear with `success` or `error` status on each step node.
  </Step>
</Steps>
