How to Send DCP over CloudEvents
If your infrastructure already speaks CloudEvents — Kafka with CE headers, EventBridge, Knative, any CNCF-style event mesh — you don’t need the bare DCP envelope on the wire. This binding makes the CloudEvents route first-class and normative: a CloudEvent whose data is a complete, unchanged DcpMessage. CloudEvents labels, routes, and dedupes; DCP says what actually happened to the project. The bare envelope (see the envelope reference) remains the standalone form for transports that carry raw JSON.
By InterIP Networks · Last updated 2026-07-06.
The mapping
Section titled “The mapping”| CE attribute | Value | Rule |
|---|---|---|
specversion | "1.0" | MUST |
id | CE-layer delivery id, unique per source | MUST — not derived from message_id (see dedup rule below) |
source | Producer-controlled URI-reference for the emitting context | MUST — origin lives in the carrying layer; the bare DCP envelope deliberately has no source |
type | org.devcopro.v1.<entity_type>.<verb> | MUST — reverse-DNS, derived from the body, never from the message_type copy. Example: org.devcopro.v1.task.completed |
datacontenttype | application/json | MUST |
dataschema | https://schemas.devcopro.org/v1/dcp-message.schema.json | SHOULD — a schema URI, never the dcp_version literal |
subject | body.entity_id | MAY — CE-layer filtering without parsing data |
time | body.occurred_at | MAY — only if occurred_at is present; never fall back to issued_at (composition time ≠ occurrence time). Absent occurred_at → omit time |
data | The complete DcpMessage, unchanged | MUST |
correlation_id stays inside data: CloudEvents extension attribute names are limited to lowercase [a-z0-9] (max 20 chars), so it cannot even be expressed at the CE layer — and it is a semantic link, not a transport one.
Two ids, two jobs
Section titled “Two ids, two jobs”CE id and DCP message_id answer different questions. Dedup deliveries on the pair (source, id) — that is CloudEvents’ contract. message_id is a producer-asserted audit label: not verifiable, not delivery-scoped, never a dedup key. The same message_id arriving under different (source, id) pairs (relays, fan-out) is legal and expected.
Don’t let the broker touch data
Section titled “Don’t let the broker touch data”DcpMessage and Event are additionalProperties: false — an interceptor that injects tracing fields into payloads makes the message invalid. Intermediaries must not rewrite data; consumers must extract data and validate it as a DcpMessage, treating failure as malformed. If your pipeline can’t guarantee payload immutability, data_base64 (the JSON bytes, base64-encoded) is the escape hatch — byte-exact, at the cost of CE-layer inspectability.
A validated example
Section titled “A validated example”{ "specversion": "1.0", "id": "d7a80f4e-8f3a-4a5e-9b6e-2f1c33a90210", "source": "https://ci.example.com/pipelines/42", "type": "org.devcopro.v1.task.completed", "subject": "task_schema_validation", "time": "2026-06-29T12:29:30Z", "datacontenttype": "application/json", "dataschema": "https://schemas.devcopro.org/v1/dcp-message.schema.json", "data": { "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" } } } }}The data of this event passes the reference validator unchanged — that is the binding’s additivity guarantee, and it is checked before every publication of the binding.
Routing on type without fighting open verbs
Section titled “Routing on type without fighting open verbs”DCP verbs are an open set, and CE type is a routing key — a combination that can pollute a type space if producers mint instance-specific verbs. Prefer the controlled vocabulary; when a custom verb is genuinely needed, name a kind of change (review_escalated, not review_escalated_to_bob), and route on the entity prefix (org.devcopro.v1.task.) when the verb tail is open-ended — the CE-layer mirror of DCP’s tolerate-unknown-verbs rule.
Conflicts and trust
Section titled “Conflicts and trust”CE attributes label and route; they carry no DCP authority. If type disagrees with the body inside data, the event is malformed — the body wins, the same rule the spec applies to message_type. And an authenticated channel does not make the payload trusted: every DCP field stays untrusted data, per the consume-safely guide.
Full normative text: docs/bindings/cloudevents.md in the DCP repository. For how DCP and CloudEvents relate conceptually, see DCP vs. CloudEvents; for the agent-to-agent rail, see DCP as an A2A extension.