> ## Documentation Index
> Fetch the complete documentation index at: https://docs.closient.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Query EPCIS Events

> Query the GS1 EPCIS 2.0 event ledger for the authenticated organization to reconstruct chain-of-custody for a GTIN, batch, location, or time window, returning a JSON-LD EPCISQueryDocument with cursor pagination.

EPCIS 2.0's `SimpleEventQuery` (§ 8.2.7 / § 8.2.8) is the standard
way to ask "what happened to this EPC?" across the captured ledger.
Closient implements it at `GET /epcis/api/2.0/events`, scoped to the
authenticated organization. This skill walks an agent through
building filter combinations that reconstruct chain-of-custody,
verify a recall, or audit a transformation step.

## When to use

* A brand owner asks "where did batch X go?" — walk forward from
  `bizStep=shipping` events.
* A retailer needs to confirm a receive against the supplier's
  shipping CTE — walk backward from `bizStep=receiving`.
* A compliance audit needs every CTE-relevant event for a GTIN in a
  date window for FSMA 204 § 1.1455(c) records request.
* Verify a recall: does the recalled batch have a corresponding
  `disposition=recalled` event in the ledger?

For full chain traversal with downstream impact counts, prefer the
dedicated traversal endpoints:
`GET /epcis/api/2.0/traceability/lots/{lot_id}/fda-export` (FDA
Sortable Spreadsheet export) and
`GET /epcis/api/2.0/traceability/lots/{lot_id}/blast-radius`
(quantified downstream footprint). This skill is for raw event-level
queries.

## Auth

OAuth token (or `X-API-Key`) with `OWNER` or `MANAGER` membership on
the organization. Results are scoped to events captured by that
organization — cross-org leakage is blocked by the org filter on the
server, not by client-side filtering.

## Flow

### Step 1 — Pick filter dimensions

The endpoint accepts the GS1 EPCIS 2.0 `SimpleEventQuery` parameter
set. Most agents need one or two dimensions:

| Param            | Type                                                                                                        | Use case                                                   |
| ---------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `eventType`      | one of `ObjectEvent` / `AggregationEvent` / `TransactionEvent` / `TransformationEvent` / `AssociationEvent` | Limit to e.g. only transformations.                        |
| `GE_eventTime`   | RFC 3339 timestamp                                                                                          | "Since when?" lower bound (inclusive).                     |
| `LT_eventTime`   | RFC 3339 timestamp                                                                                          | "Up to when?" upper bound (exclusive).                     |
| `EQ_bizStep`     | CBV BizStep token or canonical URI                                                                          | "Show me all shipping events".                             |
| `EQ_disposition` | CBV Disposition token or canonical URI                                                                      | "Show me everything currently `in_transit`" or `recalled`. |
| `EQ_readPoint`   | GS1 Digital Link `/414/{gln}` or `urn:epc:id:sgln:`                                                         | Events observed at one location.                           |
| `EQ_bizLocation` | same                                                                                                        | Events whose business location is one site.                |
| `MATCH_epc`      | single EPC URI (Digital Link or SGTIN URN)                                                                  | All events touching one EPC.                               |
| `MATCH_anyEPC`   | comma-separated EPC URIs                                                                                    | Events touching any of N EPCs.                             |
| `EQ_action`      | `ADD` / `OBSERVE` / `DELETE`                                                                                | Narrow to a single action verb.                            |
| `perPage`        | integer                                                                                                     | Page size (server default applies).                        |
| `nextPageToken`  | opaque cursor                                                                                               | Cursor pagination (Step 4).                                |

### Step 2 — Issue the query

```http theme={null}
GET /epcis/api/2.0/events?MATCH_epc=https%3A%2F%2Fid.gs1.org%2F01%2F00614141123456%2F10%2FLOT-2026-042&EQ_bizStep=shipping
Authorization: Bearer <token>
```

URL-encode EPC URIs — the `?` / `/` / `%` in Digital Link forms are
ambiguous without percent encoding.

### Step 3 — Read the response

The body is a JSON-LD `EPCISQueryDocument` (GS1 EPCIS 2.0 § 8.2.5):

```json theme={null}
{
  "@context": ["https://ref.gs1.org/standards/epcis/2.0.0/epcis-context.jsonld"],
  "type": "EPCISQueryDocument",
  "epcisBody": {
    "queryResults": {
      "eventList": [
        {
          "type": "ObjectEvent",
          "eventTime": "2026-05-18T14:30:00Z",
          "eventTimeZoneOffset": "+00:00",
          "epcList": ["https://id.gs1.org/01/00614141123456/10/LOT-2026-042"],
          "action": "OBSERVE",
          "bizStep": "shipping",
          "disposition": "in_transit",
          "readPoint": {"id": "https://id.gs1.org/414/0614141000005"}
        }
      ]
    }
  },
  "GS1-Next-Page-Token": "<opaque>"
}
```

Headers include `GS1-EPCIS-Version: 2.0.0` and `GS1-CBV-Version: 2.0.0`.

### Step 4 — Walk the cursor

Pass the body's `GS1-Next-Page-Token` back as the `nextPageToken`
query parameter to fetch the next page. When the field is absent,
the result set is exhausted.

```http theme={null}
GET /epcis/api/2.0/events?MATCH_epc=...&nextPageToken=<opaque>
```

### Step 5 — Fetch a single event by ID

```http theme={null}
GET /epcis/api/2.0/events/{event_id}
```

`event_id` is the 36-char canonical UUID assigned at capture (echoed
back as `eventID` on each event). Other organizations receive `404`
— no leak of existence.

## Required inputs

None at the protocol level (an empty query returns the org's events
in `-eventTime` order, paginated). Practically: at least one filter
(`MATCH_epc` or `GE_eventTime`) to keep results bounded.

## Optional inputs

All `SimpleEventQuery` params per the Step 1 table.

## Output

`200 OK` JSON-LD `EPCISQueryDocument` with:

* `@context` — EPCIS 2.0 context URI.
* `type` — always `EPCISQueryDocument`.
* `epcisBody.queryResults.eventList` — array of events (newest first).
* `GS1-Next-Page-Token` — present when more pages exist.

Error responses follow RFC 9457 Problem Details
(`application/problem+json`):

* `401` — missing or invalid auth.
* `403` — caller is not `OWNER` / `MANAGER`.
* `422` — malformed query parameters (e.g. unparsable timestamp).
* `429` — rate limited (300 req/min, 10k req/day per key).

## Guidance for agents

* **Read `bizStep` like a verb.** The CBV vocabulary is human-readable
  on purpose: `commissioning` (born), `harvesting` (picked),
  `cooling`, `packing`, `shipping`, `receiving`, `retail_selling`,
  `dispensing`, `destroying`. Reconstruct narrative by sorting events
  by `eventTime` and reading the bizStep column.
* **Disposition is current-state, bizStep is verb.** A `shipping`
  event with `disposition=in_transit` means "we shipped it; it's
  currently in transit." A subsequent `receiving` with
  `disposition=available` means "the receiver took possession; it's
  now available at their location." A `disposition=recalled` event
  is what `check-recalls` looks for when answering "is this lot
  under recall?".
* **`MATCH_epc` is exact-string.** The server matches the full EPC URI
  literally — `https://id.gs1.org/01/00614141123456/10/LOT-A` and
  `https://id.gs1.org/01/00614141123456/10/LOT-A/21/0001` are
  different EPCs. Use `MATCH_anyEPC` (comma-separated) when you have
  multiple serialization candidates.
* **Time windows narrow first.** A query with no time bound has to
  scan the whole org's event table. Always include `GE_eventTime`
  (and `LT_eventTime` when chasing a specific incident window) before
  fanning out to other dimensions.
* **Chain reconstruction is sort-then-walk.** EPCIS doesn't natively
  give you the parent/child traversal that FSMA 204 expects (that
  lives on the materialized CTE rows). For a single-lot story, sort
  the returned events by `eventTime` and read the bizStep column;
  for full traversal of a CTE chain, use the
  `/traceability/lots/{lot_id}/fda-export` endpoint.
* **Don't reconstruct blast radius from this endpoint.** Use
  `GET /epcis/api/2.0/traceability/lots/{lot_id}/blast-radius` — it
  walks the materialized CTE graph and returns aggregate counts in
  one call. Doing it by hand from event-level data is brittle (no
  parent/child linkage on raw events) and wastes API quota.

## Known gaps (planned)

* **No `MASTER_DATA` query.** EPCIS 2.0 § 8.2.9 defines a master-data
  query (vocabulary lookup for biz locations, read points, etc.) —
  Closient does not implement it. Use the management API
  (`/epcis/api/v1/trading-partners`, `/epcis/api/v1/reference-documents`)
  for master data CRUD instead.
* **No subscription / poll endpoint.** EPCIS 2.0 § 8.3 standing-query
  subscription is not implemented. Agents must poll.
* **No `MATCH_epcClass` filter.** Class-level matches (all events on
  any EPC sharing a GTIN, regardless of lot/serial) require a
  pre-filter by `MATCH_anyEPC` or a post-query group-by on `epcList`.
  Planned alongside FSMA 204 GTIN-keyed dashboards.
* **No `EQ_quantity` filter.** Quantity-list-based filtering (events
  involving > N units) is out of scope for the standard
  `SimpleEventQuery` and not supported.
* **Cursor opacity is server-internal.** The `GS1-Next-Page-Token`
  is not the GS1-suggested base64-encoded `eventID|eventTime` form —
  it's a Closient-internal opaque cursor. Tokens are not durable
  across schema migrations.

## Related skills

* `capture-epcis-event` — write-side; produces the rows this skill
  reads.
* `check-fsma-204-readiness` — uses query results to determine which
  KDEs are present per lot.
* `manage-recall` — checks for `disposition=recalled` events when
  reviewing a recall record.
* `check-recalls` — consumer side; resolves "is GTIN X under recall
  right now?" via `disposition=recalled` (joined with the regulator
  recall feed).
