Skip to main content
GS1 barcodes carry data as a concatenation of Application Identifier (AI) + value pairs. AIs are numeric prefixes (2-4 digits) that announce what the next value means: 01 = GTIN, 10 = batch/lot, 21 = serial, 00 = SSCC, 8003 = GRAI, and so on. This skill teaches an agent how to take an element string off a barcode scan and unpack it into structured data the rest of the system can query.

When to use

  • A scanner / OCR / EDI feed produced a raw element string and you need structured fields (GTIN, lot, expiry, serial, SSCC, GRAI, GIAI).
  • The agent received a GS1 Digital Link (https://id.gs1.org/01/...) and needs to extract the AIs the path encodes.
  • A user pasted a GS1-128 barcode payload and asked “what does this mean?”.
For the inverse — building an AI element string or Digital Link URL from structured data — see build-gs1-digital-link.

The two encodings

Both encode the same data; bracketed form just makes the AI boundaries visible. The unbracketed form uses FNC1 (\x1d, also written <GS> or ASCII 29) to mark the end of a variable-length value.

Step 1 — Detect the variant

Three real shapes to expect:
  1. (01)09506000134352(17)270101(10)ABC123bracketed
  2. 010950600013435217270101\x1d10ABC123unbracketed
  3. https://id.gs1.org/01/09506000134352/10/ABC123Digital Link path

Step 2 — Know the fixed-length AIs

These AIs have a predefined length; the parser jumps that many characters and continues. No separator needed.

Step 3 — Know the variable-length AIs

These accept up to a maximum number of characters and must be terminated by FNC1 (\x1d) when followed by another AI. If the variable-length value is the last element in the string, no separator is needed.

Step 4 — Parse, bracketed → structured

Trivial — the brackets do the framing work for you.

Step 5 — Parse, unbracketed → structured

This is where most agents trip up. The algorithm:
  1. Read 2-4 leading digits and try them against the AI table. Try the longest prefix that matches a known AI (so 8003 is preferred over 80 + 03).
  2. If the AI is fixed-length, slice length characters as the value.
  3. If it’s variable-length, slice up to the next \x1d (or end of string, if there’s no separator).
  4. Repeat from the position after the value.
Production parsers should also:
  • Normalize bracketed input by stripping brackets and re-emitting an unbracketed string with FNC1 separators inserted, then run the unbracketed algorithm. One code path is easier to maintain than two.
  • Validate the Mod-10 check digit on AI 01 (GTIN). The Closient apps.core.types.GTIN.from_raw() does this — reuse if you’re in Python.
  • Accept the literal characters ^ or ~ as FNC1 sentinels (some scanners emit those instead of \x1d).
Digital Link path is path-based AI encoding: /{AI}/{value}/{AI}/{value}/…
Closient’s own resolver (apps.products.gs1_digital_link.parse_gs1_digital_link) returns a typed dataclass with gtin, lot_number, expiry_date, and serial_number already broken out — call that directly if you’re running inside the monorepo.

Step 7 — Validate

Beyond the AI table, two checks matter:
  • GTIN check digit (AI 01). Mod-10; the last digit is computed from the first 13. Reject silently-malformed scans rather than continuing with a corrupt GTIN.
  • Date plausibility (AI 17 in particular). YYMMDD with MM in 01-12, DD in 00-31 (where 00 is sentinel for “last day of month”). The Closient scanner emits structured freshness_meta for AI 17 — see apps.scanner.services.gs1_dates.

Example end-to-end

Input (off a meat-case label):
Steps:
  1. 01 → fixed 14 → 09506000134352 (GTIN).
  2. 17 → fixed 6 → 300901 (expiry 2030-09-01).
  3. 10 → variable, up to FNC1 → 45454GH (lot).
  4. 21 → variable, no separator left → SN0001 (serial).
Structured:
After that, Closient endpoints to call next:

Common mistakes

  • Forgetting FNC1 after a variable-length AI before another AI starts — produces a corrupted parse where 10ABC12321SN0001 is read as a 20-character lot ending in 21SN0001.
  • Not zero-padding GTIN-8 or GTIN-12 to GTIN-14 before lookups — Closient stores GTINs as GTIN-14 internally; pad with leading zeros.
  • Treating (01) from a Digital Link query string as the SSCC AI 00 because the parser was eager about 2-digit prefixes; always try the longest AI prefix first.
  • Trusting YY as 4-digit year. GS1 says YY in 00-49 → 20YY, 50-99 → 19YY (sliding window). Don’t hardcode the century.
  • build-gs1-digital-link — inverse: given GTIN + AIs, emit a valid Digital Link URI.
  • resolve-gtin — fetch product detail once you have the GTIN.
  • check-product-availability — find local stock for a GTIN.
  • capture-epcis-event — once you have GTIN + lot + serial, record a supply-chain event.
  • closient-api-quickstart — authenticate before calling any of the above.

Authoritative references

  • GS1 General Specifications (v25 or later) — section 3 (AIs) and section 7 (data carriers).
  • GS1 Digital Link Standard 1.4.
  • ISO/IEC 15418 — GS1 Application Identifiers.
  • Closient internal: backend/apps/products/gs1_digital_link.py (parser) and backend/apps/core/types/_gs1.py (typed value objects).