Message Envelope
A detailed field-by-field reference for the ANCP message envelope — the universal container that wraps every message exchanged in the BizFirstGO platform.
The Envelope at a Glance
The ANCP envelope is a flat JSON object. Every message — regardless of transport, message type, or payload content — shares this outer structure. The envelope contains all the metadata the router, security layer, and observability infrastructure need, while leaving the payload field free for arbitrary content.
{
"id": "3f7a9c1e-4b2d-4e8f-9a1c-0d5e7f8b3a2d",
"type": "Command",
"source": "node://tenant-acme/flow-42/data-transform",
"destination": "node://tenant-acme/flow-42/approval-gate",
"tenantId": "tenant-acme",
"timestamp": "2026-05-25T09:14:00.000Z",
"protocolVersion": "1.0",
"correlationId": "corr-7e8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
"payload": {
"formData": { "employeeId": "E-1042", "amount": 750.00 },
"context": { "requestedBy": "user-88", "workflowRunId": "run-9981" }
}
}
Header Fields (Required)
These fields must be present on every ANCP message. A missing or malformed required field causes the router to reject the message before any handler sees it.
| Field | Type | Description |
|---|---|---|
id required |
string (UUID v4) |
Globally unique message identifier. Producers must generate a fresh UUID for every message. Used for deduplication and audit trails. |
type required |
"Command" | "Event" | "Query" | "Response" |
Semantic message type. Determines routing behaviour, expected reply semantics, and how the message is logged. |
source required |
string |
Structured URI identifying the sender. Format: {scheme}://{tenantId}/{...path}. Example: node://tenant-1/flow-7/step-3. |
destination required |
string |
Structured URI identifying the intended recipient. The router resolves this to a transport endpoint or pub/sub topic. |
tenantId required |
string |
Tenant scope for this message. The router verifies that source, destination, and tenantId are all consistent. Cross-tenant messages are rejected. |
timestamp required |
string (ISO 8601) |
UTC timestamp when the message was created by the producer. Always in ISO 8601 format with millisecond precision. |
protocolVersion required |
string |
ANCP version this message conforms to. Consumers use this to choose the correct deserializer. Current value: "1.0". |
payload required |
object |
Message-specific content. Schema is defined per message type and documented in the relevant node or agent specification. May be an empty object {} for Events with no data. |
Header Fields (Optional)
| Field | Type | Description |
|---|---|---|
correlationId optional |
string (UUID v4) |
Links a Response back to its originating Query or Command. The producer of the original request sets this; the responder copies it into the reply. |
replyTo optional |
string |
Address (same URI format as source) where the recipient should send its Response. Used when the reply destination differs from the source. |
ttl optional |
number (milliseconds) |
Time-to-live. The router discards the message if more than ttl ms have elapsed since timestamp. Useful for time-sensitive Commands. |
priority optional |
number (0–9) |
Delivery priority hint for queuing transports. Higher values are processed first. Default is 5. |
traceId optional |
string |
Distributed trace ID (W3C Trace Context format). Set by the entry-point service and propagated unchanged through the call chain for end-to-end tracing. |
sessionId optional |
string |
User session identifier. Carried by messages originating from browser clients; used by EdgeStream to scope subscriptions to a session. |
The Payload Field
The payload field is a free-form JSON object. ANCP does not constrain its schema — that is defined by the node, agent, or service that produces the message. The convention is to structure payload with two sub-objects:
"payload": {
"data": {
// The actual business content of the message
},
"meta": {
// Optional message-level metadata beyond the header fields
// e.g., pagination, content-type hints, schema reference
}
}
The data/meta split inside payload is a recommended convention. Individual nodes may use a flat payload structure when appropriate. Routers and infrastructure only read the header fields above — the payload is opaque to them.
Relationship to EdgeStream IEnvelope
At the transport layer, ANCP messages travel inside EdgeStream's IEnvelope. The mapping is:
| ANCP Field | IEnvelope Location | Notes |
|---|---|---|
id | meta.id | Direct mapping |
type | meta.type | Stored as string |
source | meta.source | Direct mapping |
destination | meta.topic | Router resolves destination URI to pub/sub topic |
tenantId | body.data.metadata.tenantId | Also verified against SignalR JWT claim |
timestamp | meta.receivedAt | Transport records its own receivedAt; original timestamp in payload |
correlationId | meta.correlationId | Direct mapping |
payload | body | The ANCP payload becomes the envelope body |
Validation Rules
Schema Check
All required fields (id, type, source, destination, tenantId, timestamp, protocolVersion, payload) must be present and non-null.
Type Enum Check
The type field must be exactly one of: Command, Event, Query, Response. Case-sensitive.
Tenant Consistency
The tenantId in the envelope must match the tenant claim in the bearer token (SignalR) or the API key scope (HTTP). Mismatch = 403.
TTL Check
If ttl is present, the router computes now - timestamp. If the result exceeds ttl, the message is discarded and a dead-letter event is emitted.
Route
Message passes validation and is routed to the resolved destination handler.
When sending a Query, generate a UUID and set it as correlationId. Subscribe to your replyTo address and match incoming Responses by correlationId. This is the standard pattern for synchronous-style interactions over asynchronous ANCP.