Skip to main content

Which one to use

Connecting from code is the recommended path: the process that already runs your agent connects itself, and there is no URL, no request template and no credential in the agent configuration. Use one of the paths below when that is not possible.

HTTP agents

The platform calls your endpoint once per conversation turn, from the LangWatch backend, and reads the reply text out of the response.

1. Expose an endpoint LangWatch can reach

A staging deployment is the recommended target: it exercises the real system without touching production data. Any URL the backend can reach works; an internal hostname or a firewalled service does not. The endpoint does not need to know anything about LangWatch. The request body and the response parsing are configured on the LangWatch side, in step 3.

2. Authenticate the scenario traffic

Your endpoint’s existing authentication passes through the HTTP agent’s header rows and its auth block (bearer, api_key or basic). Store the credential as a project secret and reference it as {{ secrets.NAME }}, so it stays encrypted at rest instead of readable in the agent’s configuration. When the normal authentication is built for human users (sessions, OAuth redirects), add a dedicated API key for scenario traffic instead: your server reads the expected key from an environment variable and checks it on each request, and the HTTP agent sends it from a secret. A dedicated key is also the one you revoke to close the testing path.
Also check: Testing agents behind authentication lists exactly which fields resolve secret references, and covers an OAuth2 client-credentials exchange.

3. Define the request and the response contract

The body template renders as a Liquid template on every turn, and outputPath is a JSONPath expression that picks the reply text out of the response.
Body template
The URL and the header values render the same variables. For the response, set outputPath to the JSONPath of the reply text. If the endpoint answers {"reply": "It ships on Tuesday."}, set outputPath to $.reply. The platform reads the reply text at that path; the response needs no other fields.

Keep a value between turns

Many agents create their own conversation on their side and cannot accept an id from outside. sessionPath covers that: a JSONPath to a value in the response that the platform keeps for the conversation and sends back as {{ session }} on the next turn, in the URL, the headers and the body. If the endpoint answers {"reply": "...", "conversation_id": "conv_8f2"}, set sessionPath to $.conversation_id and read it back from the body template:
Body template
The first turn of a conversation renders {{ session }} as an empty string. A response with no value at the path keeps the value from the previous turn. Any JSON value works: a string renders as text, an object or a list renders as raw JSON. The value is capped at 64 KB; a larger one fails the turn with agent_payload_too_large. An object or a list goes in the body without quotes. Give it a JSON value for the first turn with the default filter, or the body is not valid JSON before the endpoint has answered once:
Body template with an object session

4. Adopt the trace context

The platform sends a W3C traceparent header on every call, one trace per conversation turn. When your server adopts it, the spans your agent produces land in that same trace, and the judge reads them before its verdict: tool calls, database writes, retrievals. This step belongs to HTTP agents only. A connected agent adopts the context inside the SDK, with no middleware. With standard OpenTelemetry HTTP auto-instrumentation, adoption already happens and you keep the code unchanged. Without it, attach the extracted context in a middleware that runs before any tracing starts. Do not extract inside the handler body: a handler decorated with @langwatch.trace() opens its root span before the body runs, so an extraction there comes too late and the agent’s spans land in a separate trace.
For Flask, attach in before_request (keep the token on g) and detach in teardown_request.
The agent must report its traces to the same LangWatch project that runs the scenarios.

5. Register the agent and run

In the platform, create the agent on the Agents page: type HTTP, then the URL, authentication, body template and output path from the steps above.
The HTTP agent editor with the endpoint URL, request body template and output path
The Test tab sends one real request with the current configuration. Use it to confirm the endpoint answers and the output path extracts the reply text before you run any scenario:
The Test tab showing a 200 response from the agent endpoint and the extracted reply
With the CLI:
sessionPath is optional; leave it out when the endpoint keeps no value between turns.

Common failures

A local HTTP agent: langwatch agent dev

langwatch agent dev starts a tunnel in front of a local port, points a registered HTTP agent’s URL at the tunnel, and restores the previous URL when you stop it with Ctrl-C. While it runs, every scenario run against that agent calls straight into the process on your machine. A connected agent needs none of this: it reaches out from your machine, so a local process is a target with no tunnel and no URL rewrite. agent dev is for HTTP agents.
With no --agent flag, the command lists the project’s HTTP agents to pick from, and remembers the choice per project directory in ~/.langwatch/config.json. langwatch agent tunnel is an alias for the same command.

How the tunnel is protected

The tunnel URL is public, and your local agent process usually holds real model provider keys. langwatch agent dev mints a per-session secret, fronts your local port with a proxy that rejects any request missing the X-LangWatch-Dev-Secret header, and writes that header onto the agent’s configuration for the session. Only the platform’s scenario calls send the secret. On exit, the command removes both the proxy and the header row. --no-auth turns the proxy off, for a server that already authenticates every request. A bring-your-own tunnel (--tunnel-url) also runs without the proxy, so that tunnel endpoint must supply its own access control. The agent’s own configured authentication headers pass through the tunnel unchanged either way. While the tunnel is up, the agents list and the run target selector show a local tunnel badge on the repointed agent, so the team sees that the agent points at a developer machine.

Limits

The default transport is a Cloudflare quick tunnel on trycloudflare.com. The command shows Cloudflare’s terms notice on the first run.
  • Quick tunnels have no availability guarantee. Use them for the development loop, and run the test suites you rely on against a deployed URL.
  • Quick tunnels buffer server-sent events. This does not affect scenario calls, which are plain JSON requests; it only affects a streaming endpoint served through the same tunnel.
  • Quick tunnels cap at around 200 concurrent requests.
--tunnel-url swaps in your own tunnel when you hit these limits. A crash or a kill signal the process cannot catch skips the restore, and the agent keeps pointing at a dead tunnel. Calls to it then fail with an error saying the tunnel session probably ended, and the local tunnel badge stays visible. Run langwatch agent dev again to take the agent over with a fresh tunnel, or set the agent’s URL back by hand in the agent editor.

Code agents

A code agent is a small piece of Python stored in the agent configuration and executed by the platform. It reads the project’s secrets as an injected secrets namespace and the run’s values as params, and returns the reply under a declared output key. It fits a target that is glue and no more than glue: a token exchange in front of an API, or a call into a service that has no repository of its own. It does not run your product’s code, its dependencies or its secrets, so it is not the way to test a real agent. Use connecting from code for that.
The declared output key must match the key the Python returns, or the run fails with missing_output. Run it with --target code:<agent-id>.

Keep a value between turns

A code agent can keep one value per conversation. Return a session key beside the output, and the platform sends it back on the next turn of the same conversation. To receive it, declare an input (here session) and map it to the scenario source session in the agent’s scenario mappings; an input named session is mapped to it by default.
The mapped input is None on the first turn of a conversation, and afterwards exactly the value the code returned, with its JSON types: a dict stays a dict, a string stays a string. session needs no declared output. A turn that returns no session key keeps the previous value. The value is capped at 64 KB; a larger one fails the turn with agent_payload_too_large. Testing agents behind authentication has the worked OAuth2 client-credentials example, the secret rules and the runtime’s limits.

Next steps

Connect your agent

Decorate the function that runs your agent

Authenticated agents

Reference project secrets, and exchange OAuth2 credentials

Linking your traces

What the judge reads from your agent’s own spans

Command line interface

Every agent and run command, and its flags
Last modified on August 31, 2026