Skip to content

Identifiers API guide

An identifier connects a physical marking to a Thread: a DUST tag, QR code, barcode, Data Matrix symbol, or NFC chip. Once bound, a scan in the field resolves to the digital record. The API namespace is /api/v1/tags — legacy naming that survives in paths and schemas; these docs say identifier in prose.

Full request/response schemas: API reference.

Operation Method & path Semantics
Extract POST /api/v1/tags/extract Parse a DUST capture into a canonical fingerprint, without binding
Bind POST /api/v1/tags/bind Associate an identifier with a Thread
Identify POST /api/v1/tags/identify Search: which Thread matches this scan?
Verify POST /api/v1/tags/verify Compare a scan against a specific Thread’s identifiers
Unbind POST /api/v1/tags/unbind Detach an identifier from its Thread
Set text POST /api/v1/tags/text Rename / re-describe a bound identifier
Update POST /api/v1/tags/update Lifecycle: privacy, archive/restore (value and type are immutable)

Identify vs. verify: identify answers “what is this?” — it searches your visible Threads (optionally scoped by searchTeamIds) and returns the match, if any. Verify answers “is this the item it claims to be?” — you name a threadId and the candidate tag IDs, and the API confirms or denies. Use verify for authentication decisions; identify for lookup.

Scan endpoints accept multipart/form-data, and the shape of data depends on the identifier type:

tagType data Where it comes from
DUST An image — a binary file part or a base64 data URL (data:image/jpeg;base64,…) A DUST optical capture from a scanner
QR, BAR_CODE, DATA_MATRIX, NFC The decoded string contents (or NFC hex ID) Any symbol scanner

A DUST capture is a photograph of the tag, not a decoded value — the server extracts the fingerprint. Captures come from DUST scanning hardware: see Integrate with DUST Go for mobile capture and the React Scanner for a drop-in web component that handles all modes.

In multipart bodies, structured fields (options, tags, searchTeamIds) are passed as JSON strings.

Extract parses a capture into a canonical fingerprint and returns its quality — useful for checking a capture before enrolling, or for staging a bind:

Terminal window
curl -fsS "$APID_URL/api/v1/tags/extract" \
-H "Authorization: Bearer $DUST_TOKEN" \
-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \
-F "data=@scan.jpeg" \
-F 'options={"enrollmentSessionId":"3d5e…"}'

The response is { id, qualityScore, annotatedImage?, forensics? }id is a fingerprint ID you can later bind without re-uploading the image (below). options also carries capture metadata (device, optics, geolocation) that the platform stores with the scan.

POST /api/v1/tags/bind accepts three shapes, distinguished by tagType and payload:

Terminal window
curl -fsS "$APID_URL/api/v1/tags/bind" \
-H "Authorization: Bearer $DUST_TOKEN" \
-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \
-F "threadId=$THREAD_ID" \
-F "tagType=DUST" \
-F "tagDescription=Inbound receiving scan" \
-F "data=@scan.jpeg" \
-F 'options={"enrollmentSessionId":"3d5e…"}'

DUST image binds group related captures with a client-generated options.enrollmentSessionId UUID, and can return the annotated capture (options.returnAnnotatedImage: true).

Terminal window
curl -fsS "$APID_URL/api/v1/tags/identify" \
-H "Authorization: Bearer $DUST_TOKEN" \
-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \
-F "tagType=DUST" \
-F "data=@scan.jpeg" \
-F 'searchTeamIds=["'"$TEAM_ID"'"]'

Identify also takes text payloads (tagType of QR/BAR_CODE/DATA_MATRIX/NFC with the decoded data) or a bare identifier ID (tagType: "ANY" with tagId). A hit returns the matched identifier and its Thread; a miss returns an unidentified result rather than an error.

Verify is the authentication primitive: given a fresh scan, a threadId, and the candidate tags (identifier IDs bound to that Thread), it succeeds if any candidate matches.

Terminal window
curl -fsS "$APID_URL/api/v1/tags/verify" \
-H "Authorization: Bearer $DUST_TOKEN" \
-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \
-F "threadId=$THREAD_ID" \
-F "tagType=DUST" \
-F "data=@scan.jpeg" \
-F 'tags=["'"$TAG_ID"'"]'

The response reports success, attemptedCount / failedCount, and on success the verifiedTag.

These are plain JSON endpoints; all of them require both the tagId and the threadId the identifier is bound to:

  • POST /api/v1/tags/text — set name and/or description.
  • POST /api/v1/tags/update — set name, description, isPrivate, and archivedAt (an ISO timestamp archives the identifier; null restores it). The identifier’s value and type are immutable — rebind instead.
  • POST /api/v1/tags/unbind — detach the identifier from the Thread.

Under /api/v1/tamper, a Tamper Analysis compares a fresh scan of a DUST identifier against the reference captured when it was bound and records what it measured. The API returns measurements and evidence only — there is no summary number, band, threshold, or platform-authored result field anywhere in the surface, and alignmentOutcome reports solely whether the two scans could be compared at all (when they could not, the measurements are not comparable, which is not a statement about the identifier).

Operation Method & path
Run an Analysis POST /api/v1/tamper/analyses — form-encoded: threadId, tagId, and exactly one of data or queryFingerprintId
Record an Observation POST /api/v1/tamper/observations{ analysisId, result }
List a Thread’s Analyses GET /api/v1/tamper/analyses?threadId=… (optionally tagId, limit)
Get one Analysis GET /api/v1/tamper/analyses/{analysis_id}
Fetch a result bitmap GET /api/v1/tamper/analyses/{analysis_id}/artifacts/{name}

Running an Analysis takes a multipart/form-data or application/x-www-form-urlencoded body with threadId, tagId, and exactly one of:

  • data — the DUST scan itself, as a file or base64-encoded image. The service extracts it for you.
  • queryFingerprintId — a fingerprint ID you already hold from POST /api/v1/tags/extract (above), if you extracted separately.

Sending both, or neither, is rejected. Either way an ordinary DUST capture is valid input — there is no separate capture path for tamper analysis. If the submitted scan cannot be read, the request fails and no Analysis is recorded.

A Tamper Observation is the only conclusion the platform stores, and it is authored by a person: result is one of consistent, expected, inconsistent, or unknown, has no default, and is required. expected records normal wear and tear for the identifier’s use case and substrate. Observations are immutable and attributed; a new one never replaces an earlier one, and reads return the whole series (observations, newest first) rather than a single current result. Do not derive a result from the metrics or collapse the series to one value in your own UI.

An Analysis carries metrics (a pass-through object of the algorithm’s coverage fractions and marker counts), optional markerPoints, and artifactNames. Marker coordinate sets are each in their own scan’s pixel space — compose them in one frame by applying metrics.transformation_matrix to the query points. Result bitmaps are protected content: fetch them through the artifact endpoint, which re-authorizes every request and returns non-cacheable bytes.