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

# Quickstart: Add Agent Feedback to Your API or Service

> Instrument your Node/Express app to collect AI agent outcome signals in five steps — with Python, Go, and Rust examples included.

The fastest path to your first outcome signal is a single middleware call. This guide walks you through creating a product in the dashboard, installing the SDK, and verifying that feedback contracts are flowing — all without changing any of your existing route handlers.

<Steps>
  ### Create a product and get your API key

  Open the Agent Feedback dashboard and create a new workspace if you don't have one. Then create a **product** for the service you want to instrument.

  After creation, the dashboard generates a product key. V2 keys always follow this shape:

  ```
  af_live_<32 lowercase hex key id>_<secret>
  ```

  Copy the key and store it as an environment variable — you'll reference it as `AGENT_FEEDBACK_KEY` throughout this guide.

  <Warning>
    Your product key is a secret. Never commit it to source control or log it. If you expose a key, rotate it immediately from the product's Settings page.
  </Warning>

  ### Install the SDK

  Add the Node SDK to your project. It requires Node.js 20 or later.

  ```bash theme={null}
  npm install @agent-feedback/node
  ```

  The package ships adapters for Express, Fastify, and MCP as separate entry points, so you only import what you use.

  ### Add the middleware

  Register `agentFeedback` as a global middleware **before** your route handlers. It wraps `response.json()` and `response.send()` internally, so none of your handlers need to change.

  ```ts theme={null}
  import express from "express";
  import { agentFeedback } from "@agent-feedback/node/express";

  const app = express();

  const middleware = agentFeedback({
    apiKey: process.env.AGENT_FEEDBACK_KEY,
    include: ["/search", "/docs/*"],
    customerRef: req => req.user?.accountId, // optional opaque ID
  });

  app.use(middleware);

  // Your existing routes stay exactly as they are
  app.get("/search", (req, res) => {
    res.json({ results: [] });
  });
  ```

  The `include` array accepts glob patterns (`*` matches within a path segment, `**` matches across segments). Omit it to instrument every eligible route. The `customerRef` callback extracts an opaque account identifier from the request — it is never stored with the outcome, only used to group interactions on your dashboard.

  <Tip>
    Health checks, metrics endpoints, asset paths, and the Agent Feedback endpoints themselves are excluded automatically. You don't need to list them in `exclude`.
  </Tip>

  The middleware also exposes a `shutdown()` method. Call it during graceful shutdown to flush any queued telemetry events before the process exits:

  ```ts theme={null}
  process.on("SIGTERM", async () => {
    await middleware.shutdown();
    server.close();
  });
  ```

  ### Verify with the integration doctor

  The `agent-feedback-doctor` CLI makes a test request to your running server and confirms that the feedback contract is present and correctly formed.

  ```bash theme={null}
  npx agent-feedback-doctor http://localhost:3000/search
  ```

  A passing check prints each verified property of the `_agentFeedback` envelope. If the doctor reports a missing field or a signing error, check that `AGENT_FEEDBACK_KEY` is set and that the route is covered by your `include` patterns.

  ### Check the dashboard for incoming interactions

  Make a few real requests to your instrumented routes (or let your CI test suite run against the server). Within seconds, the interactions appear in the **Interactions** tab of your product dashboard.

  Once a feedback-aware agent runtime submits an outcome using the capability token embedded in a response, you'll see the review in the **Reviews** tab alongside the outcome and note.
</Steps>

## Equivalent setup in Python, Go, and Rust

The steps above apply to every language. Only the installation command and import path change.

<CodeGroup>
  ```python Python (ASGI) theme={null}
  import os
  from agent_feedback import AgentFeedbackASGI

  # Wrap your ASGI app — works with FastAPI, Starlette, Quart, and Django ASGI
  app = AgentFeedbackASGI(
      app,
      api_key=os.environ["AGENT_FEEDBACK_KEY"],
      include=("/search", "/docs/*"),
      customer_ref=lambda scope: scope.get("account_id"),
  )
  ```

  ```python Python (WSGI) theme={null}
  import os
  from agent_feedback import AgentFeedbackWSGI

  # Wrap your WSGI app — works with Flask, Django WSGI, Bottle, and Pyramid
  app.wsgi_app = AgentFeedbackWSGI(
      app.wsgi_app,
      api_key=os.environ["AGENT_FEEDBACK_KEY"],
      include=("/search",),
  )
  ```

  ```go Go theme={null}
  import (
      "context"
      "log"
      "net/http"
      "os"

      agentfeedback "github.com/open-software-network/agent-feedback-go"
  )

  feedback, err := agentfeedback.New(agentfeedback.Options{
      APIKey:  os.Getenv("AGENT_FEEDBACK_KEY"),
      Include: []string{"/search", "/docs/**"},
      CustomerRef: func(r *http.Request) string {
          return r.Header.Get("X-Account-ID")
      },
  })
  if err != nil {
      log.Fatal(err)
  }
  defer feedback.Shutdown(context.Background())

  http.ListenAndServe(":8080", feedback.Middleware(router))
  ```

  ```rust Rust (Axum) theme={null}
  use agent_feedback::{AgentFeedbackLayer, Options};

  let feedback = AgentFeedbackLayer::new(
      Options::new(std::env::var("AGENT_FEEDBACK_KEY")?)
          .include(["/search", "/docs/**"])
          .customer_ref(|request| {
              request
                  .headers()
                  .get("x-account-id")?
                  .to_str()
                  .ok()
                  .map(str::to_owned)
          }),
  )?;

  let app = Router::new()
      .route("/search", get(search))
      .layer(feedback.clone());
  ```
</CodeGroup>

<Note>
  The Python WSGI adapter uses the `Agent-Feedback` response header contract rather than mutating the response body, so it never buffers or modifies your application's output stream.
</Note>

## Next steps

Once interactions are flowing, read [How It Works](/how-it-works) for a precise breakdown of the signing algorithm, envelope format, telemetry queue, and outcome submission flow.
