Events
Status: internal today. DiligenceID emits platform events, and they drive usage metering. They are not yet deliverable to you as webhooks from this pipeline — webhook delivery currently runs from a separate mechanism on the internal surface. This page describes the event model so that integrations written now match what webhook delivery will carry.
If you are looking for how to receive notifications today, see webhooks.
What an event is
A statement that something happened, and which resource it happened to.
{
"id": "evt_9c1e4b7a",
"type": "credential.issued",
"subject": "credential/cred_123",
"time": "2026-08-30T04:11:02Z",
"dataVersion": "1.0",
"environment": "Sandbox",
"correlationId": "corr_5f2c",
"data": { "entityType": "credential", "entityId": "cred_123", "issuerId": "iss_1" }
}
What an event is not
Not the resource. data carries identifiers, not contents. There are no claims, no credential payloads, no
proofs, no disclosures and no subject identifiers in any event DiligenceID emits, and there never will be.
If you need the resource, read it back through the API using the identifier. That way the read is authorised at the moment it happens, against the key making it — rather than having been decided when the event was written.
The catalogue
| Event | When | Data version |
|---|---|---|
credential.issued |
A credential has been signed and issued to a holder | 1.0 |
credential.revoked |
A credential has been revoked. Permanent | 1.0 |
verification.completed |
A verification reached a terminal result | 1.0 |
Every event carries entityType and entityId. credential.issued also carries issuerId.
More event types exist internally and are not externally visible. That distinction is deliberate: some events exist to drive DiligenceID's own internals, and forwarding them because they happen to be on the bus would leak operational detail and commit us to a contract nobody designed for you.
Delivery semantics
These are the rules to design your consumer around. They are not caveats; they are the contract.
At least once. You may receive the same event more than once. A network failure after your endpoint processed a delivery but before it acknowledged is indistinguishable, from our side, from a failure before it — so the event is sent again.
Your consumer must be idempotent. Key on id, which is stable across every delivery of the same event.
Record it, and make a repeat a no-op. This is the single most important thing to get right.
No ordering guarantee. credential.revoked can arrive before credential.issued for the same credential.
Use time to order, and treat your local state as a projection you correct rather than a sequence you replay.
Not transactional with your own work. An event tells you something happened at DiligenceID. It does not know or care whether your handler succeeded.
Eventual, not immediate. An event is published shortly after the operation that caused it, not during it. It reflects something that has already happened.
Versioning
dataVersion describes the shape of data for that event type.
Additive changes keep the version. New fields can appear in data at any time. Ignore fields you do not
recognise — a consumer that rejects unknown fields breaks on the first additive change.
Breaking changes get a new version. Removing a field, renaming one, or changing its meaning.
Do not treat an unknown version as an older one. If you receive a dataVersion you were not written
against, log it and skip it rather than guessing. A field whose meaning changed would otherwise be read wrongly
and silently, which is worse than not reading it at all.
Writing a consumer
1. Read `id`.
2. Have you seen it? Acknowledge and stop.
3. Check `dataVersion` is one you understand. If not, log and acknowledge.
4. Check `type` is one you handle. If not, acknowledge.
5. Do your work.
6. Record `id` as handled, durably.
7. Acknowledge.
Steps 2 and 6 are what make redelivery harmless. Record the identifier in the same transaction as your work, or you have the same two-write problem this system solves internally.
Do not use an in-memory set for step 2. It does not survive a restart and does not exist across instances.
Correlation
correlationId ties an event back to the API request that caused it. If you logged it when you made the call —
and you should — you can join your own request log to the event you receive.
Related
- Webhooks — how notifications reach you today
- Idempotency — the same principle on the request side
- Credential status