Skip to main content
This page covers the append-only timeline behind every Intent ID, how destination events reach it, how composerID signs its own deliveries, and how drift is detected after go-live. Read it if you operate a destination integration or consume composerID’s webhooks.

The timeline

Every step composerID takes is written as an event with a type, a timestamp (at) and an actor (a user id, system or webhook), plus the version of the intent at the time. The provider is present on stamp, link and publish events, and the external_id on stamp and link events. Events are never edited or deleted; a correction is a new event. GET /intent/{intent_id}/timeline returns them in order as {intent_id, events[]}. Scope: intents:read. The TimelineEvent schema fixes this list. intent.stamped records the Intent ID being written into a destination record; intent.linked records a linkage kept on the composerID side only.

Webhooks in: destinations

Where a destination pushes events, composerID subscribes to the start of each lifecycle it cares about and lands each verified delivery on the timeline against the Intent ID.
1

Receive

The destination posts to POST /webhooks/{platform}. No bearer token: the caller is a destination platform, so the delivery authenticates by its signature header instead. It is the only route authenticated by signature rather than by a bearer token; the other routes that declare no bearer token, POST /oauth/token and POST /sandbox/keys, issue credentials rather than touch an intent.
2

Verify

composerID computes HMAC-SHA256 over the raw request body with every active signing key and compares in constant time against every X-Signature-HMAC-SHA256-{n} header supplied. A delivery with no header, or one no active key matches, is rejected with 400, not queued.
3

Correlate

The body must reference an Intent ID this tenant minted; an unknown destination or a foreign intent_id returns 404. The WebhookEvent envelope also declares a delivery_id for de-duplication, because platforms document that duplicates can occur: it is the key a receiver discards repeats on, and the Beeline receiver de-duplicates on the delivery’s event id.
4

Record

The event lands on the intent’s timeline as webhook.received and drives back-sync. The response is 202 with {ok: true, intent_id, provider}.
Some platforms need more than the generic route. The Beeline receiver completes the webhook.validation handshake that keeps a new subscription pending until its validation code is echoed back, de-duplicates on the event id, and correlates Beeline record identifiers such as assignmentId to the Intent ID from publish receipts. Where a destination offers an event-history endpoint, as Beeline does, recovery after an outage sweeps history through the same ingestion path rather than hoping the push arrived.

Webhooks out: composerID

The Intent API document declares the events composerID delivers to an endpoint you register, using the same WebhookEvent envelope: event, intent_id, intent_version, provider, external_id, timestamp, payload and delivery_id. Four are declared under webhooks:
  • vms.requisition.updated: a requisition changed in the destination system
  • vms.assignment.updated: an assignment changed
  • vms.timesheet.updated: a timesheet changed
  • vms.connection.revoked: a tenant’s authorisation was revoked
Each is signed with HMAC-SHA256 over the raw body under the convention below, and an unverifiable delivery is rejected rather than queued.
Publish confirmations, drift and enrichment gaps are not among the declared outbound events. You read them from the API instead: the 201 receipt from POST /publish, the drift report from POST /reconcile, the 422 enrichment_request from preflight, and the timeline events behind all three.
The current event taxonomy is VMS-scoped. Additional event families (assessment, document, workflow) are planned and will use the same envelope and signing convention.

One signing convention

Outbound deliveries are signed the way the Manuscript API signs its own.
  • One or more X-Signature-HMAC-SHA256-{n} headers. The numeric suffix is an identifier only and maps to no particular key.
  • Each value is the Base64-encoded HMAC-SHA256 digest of the raw request body under one active signing key. Never digest a parsed or re-serialised payload.
  • Several keys are active during rotation, so a request may carry several headers. A request is valid if any computed digest matches any supplied header.
  • Comparison is constant time. A request with no valid signature is rejected, never queued.
  • The signature travels in the headers, never in the body, so the signed bytes are exactly the bytes received. The body’s signature field is deprecated and ignored.
One convention means one verifier for everything that arrives from Deployed: the reference implementation is adapter/webhook_verify.py, its rotation rules run as tests, and the comparison itself uses hmac.compare_digest.

Reconciliation

POST /reconcile reads a destination record back and compares it, field by field through the mapping profile, with the payload that was published, not with the intent as it stands today. The request names the intent_id and platform, plus the external_id for records composerID linked rather than created. Scope: reconcile. The response is a drift report: intent_id, platform, external_id and a drift array in which each entry names the destination field, the value published and the value current. An empty array means the record is in sync.
Drift is the destination no longer matching what was written to it. A pending amendment on composerID’s side is not drift. Each check that finds drift puts a reconciliation.detected event on the timeline; its resolution is a reconciliation.resolved event.
Inbound verification, the timeline and reconciliation are implemented in the reference sandbox. Outbound delivery is documented contract only: the sandbox verifies and records what arrives, it does not emit the vms.* events to a registered endpoint. The destination-specific receivers, such as the Beeline receiver with its validation handshake and event-history sweep, are proven against specification-faithful fakes; a live subscription is confirmed per tenant at onboarding.

Next steps

Manuscript API webhooks

The signing rules as the manuscript service documents them.

Beeline guide

Subscription, handshake and event-history sweep against a fake tenant.

The Compliance File

How timeline events become the evidence pack behind a decision.