Skip to main content
An HTTP channel turns a managed deep agent into an HTTP endpoint that any external service can call. Use it for a provider that Managed Deep Agents does not support directly, such as an order system, a support tool, or your own application. You supply two callbacks: one that authenticates the request, and one that converts it into a message, naming the caller and the conversation it belongs to. Managed Deep Agents owns the trusted handoff, the agent run, and the reply. For the provider-managed alternative, see Slack.
Managed Deep Agents is in public beta on LangSmith Cloud and Bring Your Own Cloud (BYOC). Use version 0.10 of the Managed Deep Agents CLI and SDK for BYOC.
HTTP channels require managed-deepagents>=0.8.0.
verify, parse, and the optional post sit between the external service and the agent run: Three-row flow diagram. In the external service row, an inbound event arrives as a provider webhook, and a reply is delivered to the user by the provider. In your code row, verify checks the signature, parse extracts content and a thread ID and returns a message, and post sends the reply to the target. In the Managed Deep Agents row, the agent run executes on the thread and hands its response to post. Three-row flow diagram. In the external service row, an inbound event arrives as a provider webhook, and a reply is delivered to the user by the provider. In your code row, verify checks the signature, parse extracts content and a thread ID and returns a message, and post sends the reply to the target. In the Managed Deep Agents row, the agent run executes on the thread and hands its response to post.

Project structure

An HTTP channel declaration lives under channels/, like any other channel:
The file name becomes the channel name and the endpoint path. A project can declare more than one HTTP channel, and names must be unique. For the full project layout, see Project structure.

Add an HTTP channel

1

Declare the channel

channels/orders.py
provider names the external service, and it is separate from the channel name. Managed Deep Agents sends it to Agent Auth with the caller’s ID, resolving the principal whose credentials the run may use. The provider therefore namespaces caller IDs.Share one provider across channels that serve the same service. Two Shopify channels, channels/orders and channels/refunds, both declaring provider: "shopify", resolve the same Shopify user to one principal. A channel declaring provider: "slack" resolves to a different principal, even when the caller ID is identical.Declaring the channel registers the provider, so any non-empty name works.
2

Verify the request

Managed Deep Agents calls verify first. Return True to continue to parse, or False to reject the request with 401. verify may be async def.verify receives an HttpChannelRequest with a Starlette request and the original raw_body bytes. Check a signature against the bytes, not against a re-serialized body:
lib/orders.py
The endpoint has no platform authentication. Channel event routes authenticate inside the adapter, not through Managed Deep Agents ingress. That makes verify the only thing standing between the public internet and an agent run. Always check a signature or a shared secret, and never accept a request unconditionally.
Use a deployment secret for the signing key. A verify callback that raises rejects the request with 500.Resolve channel credentials from the environment, not with connections.get, which requires a run. For credentials the agent’s tools use during a run, see Manage connections.
3

Parse the request into a message

parse converts a verified request into a message that starts a run, or ignores the event. It receives the same HttpChannelRequest as verify, so request headers are available here too, and it may be asynchronous. Return one of two shapes:
  • {"type": "message", "message": {...}} starts a run.
  • {"type": "ignore"} skips the event.
Both accept an optional response, a Starlette Response.The message carries four required fields:
  • user_id: The caller’s ID in the external service, as a string, resolved from the verified event. Agent Auth maps it to the principal whose credentials the run may use, so derive it from verified data rather than from an unauthenticated field. Convert a numeric provider ID with str().
  • thread_id: The conversation to run in, as a UUID. Managed Deep Agents lowercases it and maps it to a durable agent thread, so the same value continues the same conversation.
  • target: A JSON value that identifies the reply destination in the provider. This is separate from the agent thread UUID. Include it even when the channel only starts runs.
  • content: The message text, or a list of LangChain content blocks.
thread_id must be a UUID. Managed Deep Agents rejects any other value with 400, so map an external conversation ID to a UUID before returning it, for example with uuid.uuid5.
Add parse to the lib/orders file that contains verify. Combine the imports from both examples at the top of the file, keeping only one import of HttpChannelRequest:
lib/orders.py
Return a response to control what the provider receives. Managed Deep Agents sends it after the run is accepted for a message, and immediately for an ignored event. Use it to answer a provider’s verification challenge without starting a run:This example uses Starlette’s JSONResponse. Add Starlette to your project dependencies before using it:
4

Send replies with post

Add an async post callback to deliver the agent’s final response to the external service. Omit it for a channel that only starts runs.post receives one input with the verified target and a message. Automatic replies use type: "content" with a content field. Explicit native messages use type: "native" with a native field. Return the posted message id and an optional url.These examples support content messages and reject native messages. The target is the order ID returned by parse.
channels/orders.py
Managed Deep Agents posts the reply after the run finishes, separately from the response the provider already received. Two cases produce no reply: a run that pauses on an interrupt, and a run whose agent already delivered a final message itself. A failed post is logged as a delivery failure.Add an optional async on_error(error, target) callback to handle run or delivery failures. It replaces the default error reply. It receives the verified reply target.

Call the endpoint

A deployed HTTP channel accepts requests at the channel name, not the provider name:
A declaration in channels/orders.py is reachable at /channels/orders/events. Your parse callback decodes the request body. It can read JSON, form data, text, or bytes. The examples above expect JSON. Managed Deep Agents answers with one of these, unless parse returned its own response: A 202 means the run was accepted, not that it finished. The agent’s answer arrives later through post, if configured.

Read the event in the agent

Tools and middleware read the delivery from runtime.thread.origin.channel. For the parser examples, its provider is "orders". Return raw_event from parse to expose JSON provider data at runtime.thread.origin.channel.raw_event. If omitted, raw_event is None. The reply target stays private. Code cannot read it from the channel. Managed Deep Agents builds the channel’s event from the parsed message. It does not decode the body again or preserve the provider event automatically.

Deploy the agent

An HTTP channel needs no provider authorization, so deployment is the standard command. Managed Deep Agents mounts the endpoint from the declaration.
Put the signing key and any reply credentials in the project .env so mda deploy forwards them as deployment secrets. Then register https://<deployment-url>/channels/<name>/events with the external service as its webhook target.

Share a channel adapter as a package

A reusable channel adapter is a package that creates an HTTP channel. Publish the provider-specific code separately so other projects can install it without copying callbacks or changing the Managed Deep Agents SDK. The package owns request verification, event parsing, and optional reply delivery. Managed Deep Agents still owns the agent run and trusted handoff.

Package the adapter

To package the orders adapter from this guide, move verify, parse, post, and their helpers into the package. Keep secrets in environment variables, not in the published source. Export a factory that returns the HTTP channel declaration: Declare a normal Python dependency on managed-deepagents in pyproject.toml. Put the callbacks in mda_orders/orders.py and export the factory:
mda_orders/__init__.py
Declare the supported managed-deepagents versions in your package metadata. Test against those versions before publishing.

Use the package in a project

mda-orders is an example package name, not a published integration. To install it as an application dependency and declare a channel under channels/:
channels/orders.py
The channel file name, channels/orders.py, determines the endpoint: /channels/orders/events. Keep the provider value, "orders", stable across releases because it namespaces caller identities.

Before you publish

Test valid and invalid signatures, ignored events, conversation-to-thread mapping, and reply delivery. Document required environment variables, supported event types, and any provider limitations. Publishing an adapter does not add managed provider provisioning. Document how to create the provider app, configure credentials, and register the webhook. For deployment secrets and webhook registration, see Deploy the agent.

See also

  • Channels overview: understand how channels connect messaging services to an agent.
  • Slack: use the provider-managed Slack channel instead.
  • Identity: authenticate callers and scope channel runs to the resolved user.
  • Deploy an agent: configure and deploy a managed deep agent.