AI agents: use the documentation index at llms.txt to locate machine-readable pages. This section is indexed by https://docs.particular.net/nservicebus/llms.txt. The markdown version of this page is served as plain text. An MCP server at /mcp serves the same content via the search_docs and read_doc tools; it is read-only and needs no credentials. Markdown versions of documentation pages are available by appending .md to the page URL. Directory URLs use index.md. They are served as text/plain because some retrieval backends reject text/markdown.

Envelope Handlers

Component:
NServiceBus

When receiving messages from external systems or transports that wrap payloads in a custom envelope format, NServiceBus needs to extract the actual message body and headers before processing the message. The IEnvelopeHandler interface allows implementing this extraction step.

How it works

When a message arrives from the transport, NServiceBus passes it through all registered envelope handlers in registration order.

  • If a handler returns a non-null headers dictionary, that handler's extracted headers and body are used as the incoming message for the pipeline. No further handlers are tried.
  • If a handler returns null or throws an exception, the next handler is tried. Exceptions are logged as warnings; they do not fail the message.
  • If no handler successfully unwraps the message, NServiceBus treats it as a standard NServiceBus-formatted message.

Unwrapping replaces only the transport-level envelope format. The unwrapped message then continues through the pipeline like any incoming message: NServiceBus deserializes its body using the configured deserializer. The deserializer is selected from the NServiceBus.ContentType header in the message headers (the headers returned by the handler, or the transport headers when no handler matched). If that header is missing or does not match a registered deserializer, NServiceBus uses the default serializer. For more information on deserializer selection, see message serialization.

The returned headers also enable message type mapping. Include the NServiceBus.EnclosedMessageTypes header or use a serializer that can infer the message type from the body.

Each unwrapping attempt is tracked via the nservicebus.envelope.unwrapped OpenTelemetry metric.

Implementing IEnvelopeHandler

Implement the IEnvelopeHandler interface and its single method UnwrapEnvelope:

public class MyEnvelopeHandler : IEnvelopeHandler
{
    public Dictionary<string, string>? UnwrapEnvelope(
        string nativeMessageId,
        IDictionary<string, string> incomingHeaders,
        ReadOnlySpan<byte> incomingBody,
        ContextBag extensions,
        IBufferWriter<byte> bodyWriter)
    {
        // Return null if this handler cannot process the message format.
        if (!CanHandle(incomingHeaders, incomingBody))
        {
            return null;
        }

        // Parse the envelope to extract the inner body and headers.
        var (extractedHeaders, extractedBody) = ParseEnvelope(incomingBody);

        // Write the unwrapped body into the provided writer.
        bodyWriter.Write(extractedBody);

        // Return the extracted headers to be passed to the pipeline.
        return extractedHeaders;
    }
}

UnwrapEnvelope parameters

ParameterDescription
nativeMessageIdThe native message ID assigned by the transport. Treat as read-only - for diagnostics only.
incomingHeadersHeaders as provided by the transport.
incomingBodyThe raw body bytes received from the transport.
extensionsExtension values provided by the transport via ContextBag.
bodyWriterWrite the unwrapped message body here using IBufferWriter<byte>.

Return value: Return a Dictionary<string, string> of NServiceBus headers if the message was successfully unwrapped, or null if this handler does not apply to the message.

Registering an envelope handler

Envelope handlers are registered from within a feature via FeatureConfigurationContext:

public class MyFeature : Feature
{
    protected override void Setup(FeatureConfigurationContext context)
    {
        context.AddEnvelopeHandler<MyEnvelopeHandler>();
    }
}

Handlers are instantiated once at endpoint startup and kept alive for the lifetime of the endpoint. Constructor dependencies are resolved from the endpoint's dependency injection container.

Use cases

  • Third-party system integration - messages from systems that wrap payloads in proprietary formats.
  • CloudEvents - receiving messages conforming to the CloudEvents specification.
  • Legacy format migration - supporting older message envelope formats alongside current ones during a migration period.
  • Custom transport framing - transports that add custom metadata or framing outside of NServiceBus headers.

Related

For a production-ready implementation that handles the CloudEvents specification, see the NServiceBus.Envelope.CloudEvents package.

Related Articles