---
title: "EPCIS Events — Capture, Query, Subscriptions"
description: "EPCIS 2.0 REST and SOAP, live streaming subscriptions, resilient pagination, OpenSearch event store."
canonical_url: "https://openepcis.io/docs/platform-overview/modules/epcis-events"
last_updated: "2026-07-02T20:31:30.820Z"
---

This is the EPCIS 2.0 implementation: events come in at `/capture`, get queried via Named Queries at `/query`, and stream out to subscribers as they're indexed. It's the *movement* side of the platform — an item received, a pallet shipped, a sensor reading recorded — and it leans on the [Resolver](/docs/platform-overview/modules/resolver) and [Masterdata](/docs/platform-overview/modules/masterdata) modules for the descriptive side. Every event references identifiers that resolve through them.

Subscriptions deliver live. When an event lands in the index, every active subscription that matches fires immediately — no poll interval, no missed-window debate. The same subscription model serves WebSocket clients and HTTP webhooks; the platform picks the channel from the subscriber's destination URL. Cron-style scheduled subscriptions sit alongside for clients that prefer batch-style delivery (nightly digests, periodic compliance pulls), and they're opt-in.

A few platform-wide disciplines apply here first. Identifiers are stored in GS1 Digital Link form at rest — every comparison, index, and query runs against the Digital Link representation. URN form is rendered on demand for legacy clients but never re-materialised in storage. Event documents stay lean: no embedded masterdata, no `epcisHeader.epcisMasterData` block, and custom namespaces have to be declared via the `GS1-Extensions` header to be picked up by validation. Event-hash deduplication runs during capture, so retrying the same document after a network hiccup is safe.

Long-running queries handle disconnections gracefully. A regulator pulling six months of events can drop the connection, reconnect, and resume on the same scroll cursor without restarting the scan.

The event store underneath is OpenSearch by default; an Elasticsearch backend variant is available for deployments standardised on Elasticsearch, and a headless high-volume capture variant replaces the REST front-end with a Kafka stream consumer for ingestion at scale.

## Capabilities by edition

<table>
<thead>
  <tr>
    <th>
      Capability
    </th>
    
    <th>
      OSS
    </th>
    
    <th>
      Business
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      EPCIS 2.0 REST <code>
        /capture
      </code>
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      EPCIS 2.0 REST <code>
        /query
      </code>
      
       (Named Queries)
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      EPCIS 2.0 SOAP binding (legacy)
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      Hash-based event deduplication
    </td>
    
    <td>
      <span className="fm-basic">
        ✓ lib
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      Live streaming subscriptions
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      Scheduled subscriptions
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      WebSocket delivery
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      Webhook delivery
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      Digital Link canonical form at rest, URN on demand
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      Reliable paginated queries that survive reconnects
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      OpenSearch event store
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      Elasticsearch variant
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-variant">
        ✓ variant
      </span>
    </td>
  </tr>
  
  <tr>
    <td>
      High-volume capture variant (headless)
    </td>
    
    <td>
      <span className="fm-no">
        —
      </span>
    </td>
    
    <td>
      <span className="fm-yes">
        ✓
      </span>
    </td>
  </tr>
</tbody>
</table>

## REST surface

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Auth
    </th>
    
    <th>
      Role
    </th>
    
    <th>
      What it does
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /capture
      </code>
    </td>
    
    <td>
      OIDC / API-key
    </td>
    
    <td>
      <code>
        capture
      </code>
    </td>
    
    <td>
      Submit an EPCIS document (JSON-LD or XML)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /capture/{captureID}
      </code>
    </td>
    
    <td>
      OIDC / API-key
    </td>
    
    <td>
      <code>
        capture
      </code>
    </td>
    
    <td>
      Check capture-job status
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /events
      </code>
    </td>
    
    <td>
      OIDC / API-key
    </td>
    
    <td>
      <code>
        query
      </code>
    </td>
    
    <td>
      List events (paginated)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /events/{eventID}
      </code>
    </td>
    
    <td>
      OIDC / API-key
    </td>
    
    <td>
      <code>
        query
      </code>
    </td>
    
    <td>
      Get a single event
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /queries/{queryName}
      </code>
    </td>
    
    <td>
      OIDC / API-key
    </td>
    
    <td>
      <code>
        query
      </code>
    </td>
    
    <td>
      Define a Named Query
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /queries/{queryName}/events
      </code>
    </td>
    
    <td>
      OIDC / API-key
    </td>
    
    <td>
      <code>
        query
      </code>
    </td>
    
    <td>
      Execute a Named Query
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /queries/{queryName}/subscriptions
      </code>
    </td>
    
    <td>
      OIDC / API-key
    </td>
    
    <td>
      <code>
        query
      </code>
    </td>
    
    <td>
      Subscribe — streaming or scheduled
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WS
      </code>
    </td>
    
    <td>
      <code>
        /subscriptions/{id}/stream
      </code>
    </td>
    
    <td>
      cap-token
    </td>
    
    <td>
      <code>
        query
      </code>
    </td>
    
    <td>
      WebSocket delivery channel
    </td>
  </tr>
</tbody>
</table>

Standard EPCIS 2.0 dispositions, biz-locations, EPCs, biz-steps, read-points and event-types endpoints all sit under `/events/...` and respect the same auth/role model.

## See also

- [Architecture → Live events without polling](/docs/platform-overview/architecture#live-events-without-polling).
- [Architecture → Multi-tenant isolation at the data layer](/docs/platform-overview/architecture#multi-tenant-isolation-at-the-data-layer) — how queries see only the tenant's data.
- [Modules → Formats](/docs/platform-overview/modules/formats) — the validation chain that runs before capture.
- [Modules → Integration](/docs/platform-overview/modules/integration) — S3 backfill on top of the event store.
