# Push, don't poll.

*31 July 2026 · the launch journal*

If your product needs to react to supply-chain events — a receiving discrepancy view, a recall workbench, a customer-facing "where is it" — the naive integration polls a query endpoint on a timer. Polling has a floor cost you pay forever: latency equal to your interval, load proportional to your tenant count, and a scheduler you now own. The EPCIS 2.0 query interface ships the alternative in the standard itself: **subscriptions**. Any query you can run, you can stand up as a standing query with a delivery destination, and events arrive as they land instead of when you ask.

This is not a vendor bolt-on beside the standard — it is the query interface's own mechanism, with the trigger vocabulary pinned in the official query schema this surface validates against (sha256 pins on [the conformance page](/conformance/)): `second`, `minute`, `hour`, `dayOfMonth`, `month`, `dayOfWeek` for scheduled evaluation, `stream` for trigger-on-capture, `initialRecordTime` to anchor the starting point, `reportIfEmpty` to distinguish "no news" from "no delivery."

## The lifecycle, end to end

**Create.** A subscription is a named query plus a destination:

```
POST /queries/receiving-exceptions/subscriptions
content-type: application/json

{ "dest": "https://isv.example/hooks/epcis",
  "signatureToken": "…",
  "stream": true,
  "reportIfEmpty": false,
  "initialRecordTime": "2026-07-31T00:00:00.000Z" }
```

`stream: true` is trigger semantics: the query is evaluated against events as they are captured, and matches are delivered promptly. The schedule fields are the alternative — `"minute": "0"` delivers on the hour, cron-shaped, for consumers that want batches. One subscription carries one semantics; both are declared, never inferred.

**Deliver.** A delivery is an `EPCISQueryDocument` — the same shape a pull query returns, so your handler parses one format regardless of how the events arrived — plus a signature header computed over the payload with the subscription's `signatureToken`:

```
POST /hooks/epcis
GS1-Signature: sha-256=…
content-type: application/json

{ "type": "EPCISQueryDocument",
  "epcisBody": { "queryResults": {
    "queryName": "receiving-exceptions",
    "subscriptionID": "sub-4f21…",
    "resultsBody": { "eventList": [ … ] } } } }
```

Verify the signature before you trust the body. The destination is your endpoint on your domain; the token was set by you at create time; a delivery that fails verification is somebody else's problem wearing your URL.

**Consume.** The delivery contract, stated as the table your on-call engineer needs:

| Property | Contract |
|---|---|
| Delivery semantics | at-least-once — a delivery may repeat; it is never silently dropped |
| Retry | failed deliveries retry on a backoff ladder; the subscription survives your outage |
| Ordering | within one delivery, events are ordered; across deliveries, arrival order is not a promise — order by `eventTime`/`recordTime` yourself |
| Dedupe | by the CBV 2.0 §8.9 event hash — see below |
| Empty windows | delivered only if `reportIfEmpty: true` |

## The hash closes the at-least-once gap

At-least-once delivery means your handler sees duplicates: a retry after a timeout your side actually processed, an overlap after an outage. Systems without content-derived identity handle this with delivery IDs and a dedup table keyed on the transport — fragile, because the transport is exactly what failed.

Here every event carries computed identity: the §8.9 hash of its own content, e.g. `ni:///sha-256;28339e05…?ver=CBV2.0`. Your consumer's dedupe is one set-membership check on a value that is *derivable from the event itself* — no coordination with the deliverer, no shared counter, and the same identity your pull queries, your partner's store, and the [capture door's idempotency](/journal/capture-interface-contract/) already agree on. Processing a delivery twice converges to processing it once. That is the whole exactly-once story, and it lives in the data, not the pipe.

## Replay is a query, because the subscription is a view

The deepest property: a subscription holds no state your record does not already hold. It is a standing view over the same append-only store the pull interface reads. So catch-up after a consumer outage is not a support ticket about redelivery — it is a pull query with `GE_recordTime` set to the last event you processed, against the same query definition, returning the same document shape your webhook handler already parses. Run the backfill, resume the stream, dedupe by hash across the seam; the overlap is harmless by construction.

This is also the honest answer to "what if your webhook infrastructure has a bad day": the durable record is the source of truth, delivery is a convenience over it, and nothing about your correctness depends on the pipe being perfect. Compare that with event buses where the topic *is* the record and a retention window is quietly part of your data model.

## Where this lands in an ISV product

The embed pattern: your product subscribes per tenant with the tenant's scoped key, your handler verifies, dedupes by hash, and projects events into your own UI's shape. Your customers see your product reacting in seconds; the standards machinery — query validation against the pinned schema, scoped reads, signed delivery, replay — is this layer's job, not your roadmap's. The same events join to the document layer (purchase orders, despatch advices, receiving advices) at [/business-transactions/](/business-transactions/), and the executive views your buyers ask for are computed over the identical record — [visibility.cloud](https://visibility.cloud) is that reading.

Prove the query half on your own machine first: the local toolkit ships the full pull surface — capture into a session store, then query and trace over [the MCP door](/mcp/) or CLI, no account ([run it yourself](/run-it-yourself/)). Standing delivery rides the hosted surface with scoped keys: [say what you will read](/get-a-key/?segment=query), and name the destination when you do.

---

**Proof, not adjectives.** This build could not verify the gateway's test suite green, so no sentence on this page claims a passing run — the last verified run is recorded in the repository's own CI, not here. Live on this origin, no key: POST /translate, /validate, /hash. No conformance attestation has ever been issued. The dated ledger is /what-ships-today/.
