Supply-Y Protocol

Developer Documentation

Protocol 1.0
Documentation

Search Supply-Y

All documentation
MarkdownDemo
Reliable Delivery

Events And Webhooks

The signed, retryable and recoverable event-delivery contract implemented by participating Agents.

24 / 24 delivery checksOpenAPI JSONSigned vector

Status: Normative notification-delivery profile for Supply-Y Protocol 1.0

Supply-Y notifications tell an Agent that Package metadata changed. They never carry the encrypted Package body or customer plaintext. Webhooks provide fast delivery; the ordered notification API provides recovery. A customer should implement both against one durable local inbox.

The short version

  1. Supply-Y records a lifecycle event durably.
  2. Supply-Y sends a signed webhook containing metadata only.
  3. The Agent verifies the signature and body digest, then writes the event to its own durable inbox.
  4. The Agent returns 204 even when the same event was already stored.
  5. The Agent processes inbox events in sequence order.
  6. If a sequence gap appears, the Agent calls GET /v1/notifications to fill it.

204 means durably stored by the receiver. It does not mean the Package was opened, accepted by a human or used in a business decision. Those facts require their own signed Package receipts.

Three identifiers, three jobs

FieldStable acrossPurpose
event_idAutomatic retries and manual replayIdentifies one immutable lifecycle fact; this is the receiver's primary deduplication key
delivery_idAutomatic retries to one endpointIdentifies one delivery task; attempt increases while this ID stays fixed
notification_idWebhook and polling viewsIdentifies the receiving organization's retained notification record and acknowledgement target

An operator-triggered replay creates a new delivery_id but keeps the same event_id. The receiver therefore cannot mistake replay for a new business event.

Notification envelope

Every event has a monotonic sequence within the receiving organization's stream. It also binds the lifecycle fact to the Package identity, transport mode and original encrypted-content digest.

{
  "notification_id": "notification_material_constraint_001",
  "event_id": "event_material_constraint_accepted_001",
  "event_type": "package.accepted",
  "protocol_version": "supply-y/1.0",
  "sequence": 41,
  "occurred_at": "2026-07-04T00:00:00Z",
  "organization_id": "org_tier2_connector",
  "package_id": "pkg_material_constraint_001",
  "thread_id": "thread_material_constraint_01",
  "state": "accepted",
  "transport_mode": "native",
  "content_digest": "sha-256=:25ek/LF8FWyZGJbg+9ecD3Q5czs7LIvBVU4YtWy/Sd8=:",
  "correlation_id": "corr_material_constraint_001"
}

content_digest refers to the Package content, not the webhook body. The HTTP Content-Digest header separately protects the exact webhook body bytes.

Signed webhook profile

Webhook requests use TLS and an RFC 9421 HTTP Message Signature. The body uses the RFC 9530 Content-Digest field with SHA-256.

Supply-Y uses:

  • signature label sig1;
  • algorithm ecdsa-p256-sha256;
  • raw 64-byte P-256 r || s signature output;
  • created, expires, keyid, alg and tag="supply-y-webhook" signature parameters;
  • a maximum five-minute signature lifetime;
  • a pinned Supply-Y notification-signing public key from the verified key directory.

Before endpoint activation, the Agent calls GET /v1/directory/notification-keys, pins every eligible active or retiring public key, and records the returned directory_version. POST /v1/webhook-endpoints returns the exact signing_key_id selected for that endpoint. The receiver rejects a webhook whose keyid is absent, not yet valid, retired for new delivery or revoked according to the key lifecycle rules.

During planned rotation, the directory publishes the replacement key before Supply-Y begins signing with it. Agents refresh the directory before valid_until and keep the old public key only for historical verification. Supply-Y never asks a customer to upload or share a private key for webhook verification.

The signature must cover, in this order:

"@method"
"@authority"
"@path"
"content-digest"
"content-type"
"supply-y-delivery-id"
"supply-y-event-id"
"supply-y-delivery-attempt"

The receiver must reject the request before storage when a required component is absent, the key is unknown or ineligible, the signature is outside its time window, the signature fails, the body digest fails, or the header event ID differs from the body event ID.

Durable inbox rule

The webhook handler should do very little synchronously:

  1. read the exact request bytes;
  2. validate the required signature profile and time window;
  3. recompute Content-Digest over those bytes;
  4. parse and validate the notification schema;
  5. confirm header and body identifiers match;
  6. insert by unique event_id into a local durable inbox;
  7. return 204 for both a new insert and an existing identical event.

Skill execution, ERP writes, Package decryption and human workflow happen after this durable boundary. A slow business system therefore does not cause repeated webhook delivery.

Retry decisions

Automatic delivery has at most seven attempts. The delay shown is measured after the preceding failed attempt.

Next attemptMinimum delay
230 seconds
32 minutes
410 minutes
51 hour
66 hours
724 hours

Decision rules:

ResultSupply-Y action
Any 2xxComplete the delivery
Network error, 408, 425, 429, or 5xxRetry with backoff
429 or 503 with Retry-AfterUse the later of normal backoff and Retry-After, capped at 24 hours
410Disable the endpoint
Other 4xxRecord permanent failure; do not repeat a malformed or unauthorized request
Retryable failure on attempt 7Move the delivery to the dead-letter queue and alert the endpoint owner

Webhook failure never rolls back Package acceptance and never resends the business Package. Only the metadata notification is retried.

Ordering and gap recovery

Webhook requests may arrive out of order. An Agent must store a valid out-of-order event, return 204, and delay ordered processing until the missing sequence arrives.

The Agent keeps a last_contiguous_sequence high-water mark. If sequence 43 arrives while the mark is 41, the Agent stores 43, then polls from its last cursor. When 42 arrives, it can apply 42 and the already stored 43 in order.

GET /v1/notifications?after={opaque_cursor}&limit=100

Polling pages are ordered by ascending sequence. The cursor is opaque and must not be constructed by the client. A client persists the returned cursor only after all events on that page are durably stored.

Acknowledgement

Polling consumers acknowledge durable inbox storage with:

POST /v1/notifications/{notification_id}/ack
Idempotency-Key: idem_ack_stream_001
{
  "event_id": "event_stream_001",
  "disposition": "stored",
  "stored_at": "2026-07-04T00:03:30Z",
  "last_contiguous_sequence": 41
}

An exact retry returns the original acknowledgement. Reusing the idempotency key with different content returns 409 idempotency_key_reuse.

What operators can replay

Operators may replay a failed notification delivery from the durable event stream. They must not replay the original Package creation request, create a second Package, change the event body or assign a new event ID. The audit trail records the new delivery ID, replay actor and reason.

Executable evidence

Run:

npm run validate:notifications

The committed suite passes 24 / 24 checks across:

  • OpenAPI event-envelope validation;
  • RFC 9530 body integrity;
  • RFC 9421 P-256 verification in Node and WebCrypto;
  • covered-header, body and stale-signature rejection;
  • retry and dead-letter decisions;
  • duplicate, replay and out-of-order inbox behavior;
  • ordered polling and cursor resume;
  • idempotent acknowledgement and conflict rejection.

The exact signed request is published in test-vectors/webhook-signature-0.1.json. Delivery behavior inputs are published in conformance/notification-delivery.cases.json.

On this page