EPCIS 2.0 Capture Interface for Edge RFID: REST Binding, Capture Jobs, Limits and Event IDs

Sequence of an edge RFID reader buffering reads, sending OPTIONS and POST /capture to an EPCIS 2.0 repository, receiving 202 Accepted and polling the capture job

The EPCIS capture interface is where edge RFID software stops being a reader app and becomes part of a traceability system. This guide sets out the EPCIS 2.0 contract between an edge buffer and a repository, clause by clause: the all-or-nothing capture rule, the asynchronous POST /capture that answers 202 with a capture job, the GS1-* headers that advertise limits, rollback as the default, and the choice of who generates the eventID. It is written for engineers building that link, and every rule is tied to the section of the standard it comes from.

Scope and version pin

This post covers one hop: from software at the read point (on the reader itself or on an edge box beside it) to an EPCIS repository. The contract is the same whether the read point is a fixed reader on a dock door or an integrated reader at a yard gate.

Versions are taken from the documents’ cover pages: the EPCIS Standard, Release 2.0, Ratified, Jun 2022, and the CBV Standard, Release 2.0, Ratified, Jun 2022. The transport clauses are EPCIS section 8.1 (the capture operation), section 11 (message-queue and HTTP bindings) and section 12 (REST). Section 12.1 names ref.gs1.org/standards/epcis/2.0.0/openapi.json as the complete REST specification. It is an OpenAPI 3.0.3 file, “EPCIS 2.0 REST Bindings”, version 2.0.0, so generate client stubs from it. EPCIS 2.0 is also published as ISO/IEC 19987:2024, dated 22 March 2024.

Which fields a reader can fill is a separate topic. Here the edge already holds a valid event and has to get it into the repository exactly once, in order, under an ID both sides agree on.

Watch item: in September 2026, GS1’s EPCIS GitHub repository began hosting public-review draft files for version 2.1. A draft is not a ratified standard, so keep sending 2.0.0 in your version headers until 2.1 is ratified.

One operation, all or nothing

EPCIS section 8.1.2 defines capture as a single method, capture, that takes a list of events. Three rules in that clause shape every edge design:

The bindings then decide how the result reaches the client. The message-queue binding (section 11.1) leaves the broker, addressing and QoS to the implementer. Each message is one capture operation, and an acknowledgement may signal the outcome. The HTTP binding (section 11.2, XML payloads) returns 200 for a completed capture and any 3xx, 4xx or 5xx for a capture that did not happen. The REST binding (section 12) works differently. A client written for 11.2 that counts only 200 as success will misread a REST repository’s 202.

Section 8.1.1 expects trading partners who exchange data across organisational boundaries to use authenticated bindings. In the REST binding, section 12.4 requires the client to authenticate on every call; the server returns 401 when authentication fails and 403 when an authenticated client lacks permission.

The REST sequence: OPTIONS, POST /capture, 202, poll

Section 12.6 defines two capture endpoints. POST /capture takes an EPCISDocument with one or more events and runs asynchronously. POST /events takes one bare event and is synchronous: the OpenAPI returns 201 Created with the event’s URL, or 409 if that identifier already exists. Batches from an edge buffer go to /capture, in five steps:

  1. Discover. OPTIONS /capture. Section 12.3 requires every endpoint to return its allowed verbs and its supported headers with their defaults. In the OpenAPI this is a 204 carrying both capture limits and GS1-Capture-Error-Behaviour.
  2. Submit. POST /capture with the document, the version headers and the error behaviour you want.
  3. Accept. The server returns 202 Accepted, with the capture job’s URL in Location; its last path segment is the captureID. Section 12.6.2 says a 202 does not guarantee that the events were stored.
  4. Poll. GET /capture/{captureID} until running is false.
  5. Settle. Read success. Only then mark the batch done in the buffer.

On the wire, for one shipping read at a Delhi dock door (GS1’s example company prefix, our identifiers):

POST /capture HTTP/1.1
Host: epcis.example.com
Authorization: Bearer <token>
Content-Type: application/ld+json
GS1-EPCIS-Version: 2.0.0
GS1-CBV-Version: 2.0.0
GS1-Capture-Error-Behaviour: rollback

{
  "@context": ["https://ref.gs1.org/standards/epcis/2.0.0/epcis-context.jsonld"],
  "type": "EPCISDocument",
  "schemaVersion": "2.0",
  "creationDate": "2026-10-22T09:15:02.000+05:30",
  "epcisBody": {
    "eventList": [
      {
        "type": "ObjectEvent",
        "eventID": "urn:uuid:3f2b6c1e-8d4a-4c3e-9b7f-2a1d5e6f7a80",
        "eventTime": "2026-10-22T09:14:58.412+05:30",
        "eventTimeZoneOffset": "+05:30",
        "epcList": ["urn:epc:id:sgtin:0614141.107346.2017"],
        "action": "OBSERVE",
        "bizStep": "shipping",
        "disposition": "in_transit",
        "readPoint": {"id": "urn:epc:id:sgln:0614141.07346.1234"}
      }
    ]
  }
}

HTTP/1.1 202 Accepted
Location: /capture/7f3a91c2

GET /capture/7f3a91c2 HTTP/1.1

HTTP/1.1 200 OK
{
  "captureID": "7f3a91c2",
  "running": false,
  "success": true,
  "captureErrorBehaviour": "rollback",
  "createdAt": "2026-10-22T03:45:02Z",
  "finishedAt": "2026-10-22T03:45:03Z",
  "errors": []
}

The document validates against the published 2.0.0 JSON Schema. Section 12.6.2 lists the job’s minimum fields: running, success, errors or errorFile (a URL to an error log), captureErrorBehaviour, createdAt and finishedAt. The two flags mean different things depending on the job’s error behaviour:

runningsuccessUnder rollbackUnder proceedEdge action
truetrueCapturing; no errors so farCapturing; no errors so farKeep polling
truefalseAn error occurred; rollback in progressAn error occurred; capture continuesKeep polling; prepare the error path
falsetrueAll events capturedAll events captured, no errorsMark the batch done
falsefalseAll events rejectedSome events captured, with errorsRollback: split and resend. Proceed: reconcile by eventID

Source: EPCIS 2.0 section 12.6.2, Tables 12-3 and 12-4. The edge-action column is our implementation advice.

GS1 headers, status codes and content negotiation

Section 12.3 (Table 12-2) defines the custom headers. These are the ones an edge capture client deals with:

HeaderSent byMeaning for the edgeValue shown by GS1
GS1-EPCIS-VersionBothEPCIS version. If the client omits it, the server uses its own.2.0.0
GS1-EPCIS-Min / -MaxBothRange of supported versions1.0.0 / 2.0.0 (OpenAPI)
GS1-CBV-VersionBothCBV version2.0.0
GS1-EPC-FormatBothWhether EPCs are given as Digital Link URIs or as URNs; the OpenAPI applies it to GET endpointsStandard: Always_DL. OpenAPI: Always_GS1_Digital_Link (default), Always_EPC_URN, No_Preference, Never_Translates
GS1-EPCIS-Capture-LimitServerMaximum events per call500 (example)
GS1-EPCIS-Capture-File-Size-LimitServerMaximum document size in octets1024 (example)
GS1-Capture-Error-BehaviourBothrollback (all or nothing; the required default) or proceed (capture as many as possible). In the OpenAPI, a server can also send all to show it supports both.rollback

The 500 and 1024 only illustrate the header format; the real numbers are whatever your repository returns. The standard’s Always_DL example does not match the OpenAPI enum, so send what the server’s OPTIONS response advertises. Section 12.3 requires the server to support both capture limits on /capture.

Every endpoint must support JSON and JSON-LD; XML is optional. An unsupported Accept type gets 406, and an unsupported request body gets 415. Section 12.10 requires errors in RFC 7807 Problem Details format and maps exceptions to status codes:

StatusEndpointMeaning for the edgeSource
202POST /captureJob created; storage not yet confirmed12.6.1, 12.6.2
201POST /eventsSingle event capturedOpenAPI
400BothValidationException; on /capture all events are rejected12.10, OpenAPI
401 / 403AllNot authenticated / not permitted12.4
406 / 415AllUnsupported response / request media type12.3, 12.10
409POST /eventsIdentifier already exists12.10, OpenAPI
413BothCaptureLimitExceededException12.6.1, 12.10
500 / 501AllImplementationException12.10

On a 413, the server may report the most restrictive limit, and the OpenAPI’s 413 response carries both limit headers. Resize the batch and resend. For any other /capture failure, section 12.6.1 requires a 4xx status.

Designing the edge buffer

Batch to the advertised limit, whichever binds first

Read both limits from OPTIONS /capture at start-up and after any 413. Cut each batch at whichever limit it reaches first; the OpenAPI notes that splitting large payloads avoids timeouts. Here is the arithmetic with GS1’s illustrative values (500 events, 1,024 octets). Minified, the sample document above is 529 bytes: a 200-byte envelope plus a 329-byte event. Two events come to 859 bytes, and a third takes the document to 1,189, over the limit. So the batch stops at two events, not 500. A carton read is heavier still: the same ObjectEvent with 24 SGTIN URNs is 1,226 bytes before the envelope, and each additional URN adds 39 bytes.

When a single ObjectEvent is over the size limit, split its EPC list across several events with the same time, place and business step, each with its own eventID. Section 7.4.2 says EPCs that share an ObjectEvent have no relationship beyond having been through the same event, so the split loses nothing. Check the semantics before splitting other event types.

Keep rollback, and plan the split

Under rollback, one bad event rejects the whole batch and nothing is left half-captured. The edge then recovers: read errors or errorFile, bisect the batch, resend the halves, and send the event that is finally isolated to a quarantine queue for someone to fix. Use proceed only when the business has agreed that partial capture is acceptable. Even then, success: false says that something failed, not which events failed, so reconcile with GET /events/{eventID}.

Make retries recognisable

Assign the eventID when the event enters the buffer, and reuse it on every retry. If a POST times out with no 202, a job may still exist. Before resending, list jobs with GET /capture and look the events up with GET /events/{eventID}; a 404 means the event has not landed. Whether /capture rejects a duplicate eventID is up to the implementation (section 8.1.2 permits such checks), while POST /events answers a duplicate with 409. Once you hold a 202, poll the job with back-off and do not post the batch again.

Throughput depends on the repository’s limits and how fast its jobs finish, so measure it in staging. On the readers we ship, ReaderSense Edge already provides this buffer. Every read goes to SQLite on the board’s eMMC, the queue survives network loss and reboots, and it drains in order through batched HTTP or WebSocket uploads. Wrapping those batches as EPCISDocuments and settling them against capture jobs is the adapter this post describes.

Who generates the eventID: UUID or Event Hash ID

CBV 2.0 section 8.9 makes eventID optional. When it is present it must be globally unique, and a CBV-Compliant document must use either a UUID URI (section 8.9.1) or an EPCIS Event Hash ID (section 8.9.2). Section 12.6.1 lets the repository fill an empty eventID in one of those two forms. That gives four options:

OptionEdge implementsGainWatch
UUID v4 at the edgeRandom urn:uuid:…Known before sending; retries and lookups work; no canonicalisationCreate it once and store it; a fresh UUID on retry creates a new event
UUID v1 at the edgeTime-based UUIDSame as v4RFC 9562 says the v1 node field holds an IEEE 802 MAC address or a random value, so it can expose the reader’s hardware address
Event Hash ID at the edgeThe CBV 8.9.2 canonicaliser, then a hashAny party can recompute it; a re-sent copy of the event gets the same IDMost code to get right; test it against GS1’s vectors
Left emptyNothingSimplest edgeThe edge never learns the ID, so it cannot look the event up after a lost response

Our default is a UUIDv4 created when the read enters the buffer. Switch to an edge-computed hash when the business needs content-based duplicate detection or notarisation, which are the uses the CBV gives for it. CBV 2.0 cites RFC 4122 and allows only UUID versions 1 and 4. RFC 9562 (May 2024) replaced RFC 4122 and added the time-ordered version 7, but a v7 UUID is not one of the two forms a CBV-Compliant document may use.

What the Event Hash ID requires

The CBV sets three limits on the hash. A redacted copy of an event never reproduces the original’s hash. The hash does not prove authorship, since anyone who alters an event can recompute it, so the CBV points to digital signatures for tamper protection. And an error declaration carries the original event’s eventID instead of a new hash. To test an implementation, use GS1’s event_with_identical_hash_id_1 to _7. They express one event several ways (shuffled EPC and quantity lists, a different namespace prefix, a JSON rendering), and every version must produce the same ID.

Clocks, time zones and event order

Section 7.4.1 defines eventTime as the time the capturing application says the event happened, which at an RFID read point means the reader’s clock. The 2.0.0 JSON Schema requires eventTime and eventTimeZoneOffset on every event. The offset is written as ±hh:mm, with hours up to 14 and only 14:00 allowed at 14. The standard’s own example is +05:30. In our test, +0530, the form strftime’s %z produces, failed validation, as did +14:30.

The offset records where the event happened; it does not fix the moment (section 7.4.1.1). A Delhi read at 09:14:58.412 local time can be sent as 09:14:58.412+05:30 or as 03:44:58.412Z, and in both cases the offset field stays +05:30.

EPCIS mentions clock synchronisation only for sensor timestamps: section 7.3.7.1.1 says capture applications should use an established protocol such as IEEE 1588-2008. The same applies to eventTime. If a reader’s clock drifts, the repository’s order no longer matches what happened on the floor, which matters most across the many sites of a multi-hub supply chain. Keep each reader on a disciplined time source. Record with each buffered event whether the clock was synchronised when it was stamped, and hold events stamped before sync is confirmed after a cold boot.

The EPCIS and CBV Implementation Guideline, Release 2.0, Ratified, Mar 2023, section 4.6.2, covers events that happen together. Its example is a machine that commissions twelve items and a case and packs them in one step. The guideline advises giving the packing event a later eventTime than the commissioning events, even if only one millisecond later, so a sort by time gives a logical sequence. Encode-and-pack RFID stations need exactly this.

Validate before go-live

Section 10.3 makes two validation files normative: epcis-json-schema.json (JSON Schema draft-07) for JSON and epcis-shacl.ttl for JSON-LD, both under ref.gs1.org/standards/epcis/2.0.0/. Both take an open-shape approach: terms from other namespaces are ignored. That is what allows extension fields, and it is also why a misspelled extension key passes without complaint, so test your extensions yourself.

A second trap comes from the tooling. The schema accepts any property name that is a URI, and draft-07 makes enforcement of format optional. With Python’s jsonschema, our sample with an extra lower-case bizstep key passed when format checking was off and failed when it was on. An eventTime with no time zone behaved the same way. Turn format assertion on in CI.

GS1 publishes examples at ref.gs1.org/docs/epcis/examples/ (section 10.4). These include capture-job documents (running, success, with errors, with an error file), an error declaration with its corrective event, and the identical-hash set. Run them through both the edge and the staging repository.

Go-live checklist

  1. OPTIONS /capture is read at start-up and after every 413, and both limits are stored.
  2. Batches are cut at the tighter of the two limits, and oversized ObjectEvents are split.
  3. GS1-EPCIS-Version and GS1-CBV-Version are pinned to 2.0.0.
  4. Rollback has been tested: one invalid event rejects its batch, and the bisect sends that event to quarantine.
  5. A batch is marked done only when running is false and success is true.
  6. eventIDs are stored with buffered events, and the lost-response path has been tested.
  7. Reader clocks are synchronised, offsets are written as +hh:mm, and packing events are stamped after their commissioning events.
  8. CI runs schema validation with format assertion on, and SHACL validation for JSON-LD.

The same list applies whether events come from dock doors or from shelf readers in an RFID inventory system.

Frequently asked questions

What is the EPCIS capture interface?

It is the interface through which a capturing application, such as reader middleware, an edge gateway or an ERP, delivers events to an EPCIS repository. EPCIS 2.0 defines it in section 8.1.2 as a single capture operation that stores every event in a list or none of them. Section 11 binds that operation to message queues and HTTP, and section 12 to REST endpoints: POST /capture for documents and POST /events for single events.

What does HTTP 202 mean when capturing EPCIS events?

On the REST /capture endpoint, 202 Accepted means the document was syntactically acceptable and the client was allowed to call the endpoint. A capture job now exists, and its URL is in the Location header. It does not mean the events are stored. Poll GET /capture/{captureID} until running is false, then read success. The older XML HTTP binding in section 11.2 uses 200 for a completed capture instead.

What is GS1-Capture-Error-Behaviour?

It is a header defined in EPCIS 2.0 section 12.3 that sets what /capture does when some events fail. With rollback, which the standard requires as the default, either every event is captured or all are rejected. With proceed, the server captures as many valid events as it can. The OpenAPI file adds a third value, all, which only a server uses, to announce that it supports both.

Should an RFID reader generate the EPCIS eventID?

In most edge designs, yes. An ID created when the event enters the edge buffer, and stored with it, lets the edge check whether the event landed after a lost response (GET /events/{eventID}) and makes retries recognisable. CBV 2.0 allows a UUID version 1 or 4, or an EPCIS Event Hash ID. A random UUIDv4 is the simpler choice. The hash suits teams that need content-derived duplicate detection or notarisation and can implement the full canonicalisation.

How many events can I send in one EPCIS capture request?

As many as the repository advertises. For /capture, EPCIS 2.0 requires the server to support GS1-EPCIS-Capture-Limit (events per call) and GS1-EPCIS-Capture-File-Size-Limit (document size in octets); read both with OPTIONS /capture and cut batches at whichever binds first. The 500 shown in the standard is an example value, not a default. A request over either limit may get 413 Payload Too Large.

Does EPCIS 2.0 support message queues as well as REST?

Yes. Section 11.1 defines a message-queue binding. The server provides one or more queue or topic endpoints, each message carries an EPCIS document and counts as one capture operation, and positive or negative acknowledgements may signal the outcome. The choice of broker, addressing and quality of service is left to the implementer, and the all-or-nothing rule still applies to every message.

Sources