DcpMessage Envelope Reference
A DcpMessage is the outer JSON envelope that carries exactly one Event. It holds only semantic fields — version, identity, timing, correlation — and no transport, trust, identity, routing, signing, delivery, or retry semantics; those belong to the transport layer.
Schema: https://schemas.devcopro.org/v1/dcp-message.schema.json
The envelope core is strict: additionalProperties: false. There is no ad-hoc field growth — new capability is added only through the namespaced extensions object, never by adding top-level fields.
Required fields
Section titled “Required fields”dcp_version
Section titled “dcp_version”Type: string, pattern ^1\.[0-9]{1,4}$ — MUST be present.
The protocol version as major.minor (e.g. "1.0", "1.10"). Consumers MUST parse dcp_version as two separate integers, never as a floating-point number — a naive float parse would treat 1.10 as less than 1.9, which is wrong. Within a major version, only additive, optional changes are permitted, so an older validator continues to accept newer-but-compatible messages.
message_id
Section titled “message_id”Type: string, pattern ^msg_[A-Za-z0-9._-]+$ (≤128 chars) — MUST be present.
A stable identity for this message, for audit and correlation only. Uniqueness is producer-asserted; DCP has no mechanism to verify it is actually unique. message_id is explicitly not a transport, delivery, or idempotency identifier — deduplication and delivery guarantees are the transport’s job, not DCP’s. See Transport-Neutral for why the two layers are kept apart.
message_type
Section titled “message_type”Type: string, pattern <entity>.<verb> (≤64 chars) — MUST be present.
A denormalized convenience copy of <body.entity_type>.<body.verb>, provided so a human or operator can read a message’s shape at a glance (e.g. in logs or a dashboard) without descending into body. It exists for readability only — see the load-bearing rule below.
issued_at
Section titled “issued_at”Type: string, ISO-8601 UTC timestamp — MUST be present.
When this message was composed, not when it was delivered. This is distinct from the Event’s own occurred_at, which records when the underlying state change actually happened. A producer may compose a message well after the event occurred, or a transport may deliver it well after issued_at; DCP makes no claim about either gap.
Type: Event object — MUST be present.
Exactly one Event. The envelope carries no batching and no multi-event payloads: one DcpMessage, one state change.
Optional fields
Section titled “Optional fields”correlation_id
Section titled “correlation_id”Type: string, pattern ^msg_[A-Za-z0-9._-]+$ (≤128 chars) — MAY be present.
A semantic link to a prior message_id — for example, a task.completed message correlating back to the task.created message for the same task. DCP gives no guarantee that the referenced message exists, was authentic, or came from the same source; a consumer MUST NOT make security-relevant decisions based on correlation_id alone. It is meaning, not a transport thread id.
extensions
Section titled “extensions”Type: object — MAY be present.
A free-form, namespaced map for implementor-specific fields, following the same rules as Event and entity extensions: keys MUST match ^x-<vendor>$, and a conformant consumer MUST be able to ignore any extension it doesn’t recognize. This is the only sanctioned way the envelope grows.
The one rule JSON Schema cannot express
Section titled “The one rule JSON Schema cannot express”message_type is a convenience copy, not authority. The normative source of a message’s type is always the body: <body.entity_type>.<body.verb>.
- A validator MUST check that
message_typeequals<body.entity_type>.<body.verb>. - On conflict, the body wins, and the message MUST be treated as malformed.
- A transport MUST NOT route, authorize, or trust anything on the value of
message_type.
Pure JSON Schema cannot express this cross-field equality constraint — the dcp-message.schema.json document validates the shape and pattern of message_type independently of body, but the equality check is a separate, mandatory step every conformant implementation must add on top of schema validation (SPEC §4.1). A message that is schema-valid but has a mismatched message_type is still malformed. See the conformance suite for reject cases that exercise exactly this.
Timestamps
Section titled “Timestamps”All DCP timestamps, including issued_at, are ISO-8601 in UTC with a trailing Z (e.g. 2026-06-29T10:00:00Z). They are self-reported by the producer — DCP defines no freshness guarantee. A consumer with freshness requirements (staleness windows, ordering assumptions) MUST enforce them using transport-layer delivery timestamps, not issued_at or any other DCP field. This mirrors the same non-trust posture that governs every other envelope field: DCP describes, it does not attest.
Worked example
Section titled “Worked example”A task.completed message, correlated back to the message that created the task:
{ "dcp_version": "1.0", "message_id": "msg_01HZ0TASKDONE0001", "message_type": "task.completed", "issued_at": "2026-06-29T12:30:00Z", "correlation_id": "msg_01HZ0TASKCREATED1", "body": { "event_id": "evt_01HZ0TASKDONE0001", "entity_type": "task", "verb": "completed", "entity_id": "task_schema_validation", "occurred_at": "2026-06-29T12:29:30Z", "attributed_to": "agent-builder", "delta": { "status": { "from": "in_progress", "to": "completed" } } }}Note message_type: "task.completed" matches body.entity_type + "." + body.verb exactly, and issued_at (12:30:00Z) trails body.occurred_at (12:29:30Z) by 30 seconds — a normal gap between when a change happened and when the message describing it was composed.
For the full field reference of body, see the Event Schema Reference. For how DCP messages stay valid across different transports (webhooks, message queues, direct API calls), see Transport-Neutral. For the closed set of things that make a message invalid, see the error and conformance reference. New producers should start with the quickstart to see a full request/response cycle.
By InterIP Networks · Last updated 2026-07-01.