@proveanything/smartlinks 2.1.11 → 2.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -117,7 +117,8 @@ export declare namespace claimSet {
117
117
  /** Record the chip a phone just programmed with this code. */
118
118
  function recordTag(scope: ClaimSetScope, claimSetId: string, rangeId: string, code: string, params: {
119
119
  uid: string;
120
- data?: Record<string, unknown>;
120
+ data?: Record<string, unknown>; /** A plain (NTAG 21x) tag's write password, kept with the tag */
121
+ password?: string;
121
122
  }): Promise<{
122
123
  tag: ClaimSetRangeTag;
123
124
  range: ClaimSetRange;
@@ -1,4 +1,4 @@
1
- import { Partner, PartnerClaimSet, CreatePartnerClaimSetRequest } from "../types/index.js";
1
+ import { Partner, PartnerClaimSet, CreatePartnerClaimSetRequest, PartnerTagHit } from "../types/index.js";
2
2
  /**
3
3
  * Partners: organisations above collections (FanConnect, Zenith Coins, …) that own NFC claim sets and
4
4
  * assign their tags into collections their people administer. Partners and their members are set up by
@@ -16,4 +16,9 @@ export declare namespace partner {
16
16
  function getClaimSet(partnerId: string, claimSetId: string): Promise<PartnerClaimSet>;
17
17
  /** A new claim set for the partner, on one of its tap domains (owners and admins). */
18
18
  function createClaimSet(partnerId: string, params: CreatePartnerClaimSetRequest): Promise<PartnerClaimSet>;
19
+ /** Find a tag across the partner's sets by its chip UID (8 or 14 hex) or its code (printed or bare). Never returns keys. */
20
+ function lookupTags(partnerId: string, query: {
21
+ uid?: string;
22
+ code?: string;
23
+ }): Promise<PartnerTagHit[]>;
19
24
  }
@@ -32,4 +32,14 @@ export var partner;
32
32
  return post(`/admin/partner/${encodeURIComponent(partnerId)}/claimSet`, params);
33
33
  }
34
34
  partner.createClaimSet = createClaimSet;
35
+ /** Find a tag across the partner's sets by its chip UID (8 or 14 hex) or its code (printed or bare). Never returns keys. */
36
+ async function lookupTags(partnerId, query) {
37
+ const q = new URLSearchParams();
38
+ if (query.uid)
39
+ q.set('uid', query.uid);
40
+ if (query.code)
41
+ q.set('code', query.code);
42
+ return request(`/admin/partner/${encodeURIComponent(partnerId)}/tags/lookup?${q}`);
43
+ }
44
+ partner.lookupTags = lookupTags;
35
45
  })(partner || (partner = {}));
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.1.11 | Generated: 2026-11-08T09:25:58.482Z
3
+ Version: 2.1.12 | Generated: 2026-11-08T14:30:42.034Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -5894,6 +5894,17 @@ interface PartnerClaimSet {
5894
5894
  }
5895
5895
  ```
5896
5896
 
5897
+ **PartnerTagHit** (interface)
5898
+ ```typescript
5899
+ interface PartnerTagHit {
5900
+ namespace: string
5901
+ claimSetId: string
5902
+ set: PartnerClaimSet | null
5903
+ tag: ClaimSetRangeTag & { url: string | null }
5904
+ range: ClaimSetRange | null
5905
+ }
5906
+ ```
5907
+
5897
5908
  **CreatePartnerClaimSetRequest** (interface)
5898
5909
  ```typescript
5899
5910
  interface CreatePartnerClaimSetRequest {
@@ -9330,6 +9341,8 @@ interface NfcValidateRequest {
9330
9341
  claimSetId: string
9331
9342
  codeId: string
9332
9343
  mirror?: string
9344
+ d?: string
9345
+ c?: string
9333
9346
  userId?: string
9334
9347
  }
9335
9348
  ```
@@ -12327,21 +12340,9 @@ The range's tags in index order, a page at a time (keys: owners/admins only).
12327
12340
  The next tag to program on a phone (lowest index without a chip), with its URL and key.
12328
12341
 
12329
12342
  **recordTag**(scope: ClaimSetScope, claimSetId: string, rangeId: string, code: string,
12330
- params: { uid: string; data?: Record<string, unknown> }) → `Promise<`
12343
+ params: { uid: string; data?: Record<string, unknown>; /** A plain (NTAG 21x) → `void`
12331
12344
  Record the chip a phone just programmed with this code.
12332
12345
 
12333
- **exportFile**(scope: ClaimSetScope, claimSetId: string, rangeId: string) → `Promise<ClaimSetRangeFile>`
12334
- The printer file (CSV text in `csv`). Carries the tags' keys: don't keep it around.
12335
-
12336
- **instructions**(scope: ClaimSetScope, claimSetId: string, rangeId: string, format: 'pdf' | 'md' = 'pdf') → `Promise<ClaimSetRangeFile>`
12337
- The manufacturer's instruction sheet: PDF (base64) or Markdown.
12338
-
12339
- **importUids**(scope: ClaimSetScope, claimSetId: string, rangeId: string, params: ImportClaimSetRangeUidsRequest) → `Promise<ImportClaimSetRangeUidsResponse>`
12340
- The manufacturer's file back: check it (commit false), then write the chip UIDs (commit true).
12341
-
12342
- **assign**(scope: ClaimSetScope, claimSetId: string, rangeId: string, params: AssignClaimSetRangeRequest) → `Promise<AssignClaimSetRangeResponse>`
12343
- Point the range (or from–to within it) at a collection / product / batch, or clear it.
12344
-
12345
12346
  ### collection
12346
12347
 
12347
12348
  **get**(collectionId: string, admin?: boolean) → `Promise<CollectionResponse>`
@@ -13270,6 +13271,9 @@ One of the partner's claim sets.
13270
13271
  **createClaimSet**(partnerId: string, params: CreatePartnerClaimSetRequest) → `Promise<PartnerClaimSet>`
13271
13272
  A new claim set for the partner, on one of its tap domains (owners and admins).
13272
13273
 
13274
+ **lookupTags**(partnerId: string, query: { uid?: string; code?: string }) → `Promise<PartnerTagHit[]>`
13275
+ Find a tag across the partner's sets by its chip UID (8 or 14 hex) or its code (printed or bare). Never returns keys.
13276
+
13273
13277
  ### podcast
13274
13278
 
13275
13279
  **generate**(collectionId: string,
@@ -1,75 +1,77 @@
1
- # NFC tag ranges and partners
2
-
3
- For partners and enterprise clients who run their own tag programmes: mint a batch of tags, send them
4
- to a manufacturer with instructions, get the chip UIDs back, then put the tags to work for a client or
5
- product. Ordinary business collections don't use this.
6
-
7
- ## The words
8
-
9
- | Word | Means |
10
- |---|---|
11
- | **Claim set** | A pool of codes on one tap domain and one tag type (NTAG 424 DNA, NTAG 213/215/216, or QR). Most partners need one. |
12
- | **Range** | A named, contiguous block of tags inside a set: "Tap to donate - February", 100 tags, indexes 201-300. |
13
- | **Index** | A tag's place in its set. Ranges never overlap and indexes are never reused. |
14
- | **Partner** | An organisation above collections that owns sets and assigns their tags into collections its people administer. Set up by SmartLinks staff. |
15
-
16
- ## Who can do what
17
-
18
- `scope` is `{ partnerId }` for a partner's sets, or `{ collectionId }` for a collection's own sets.
19
-
20
- | Role | Can |
21
- |---|---|
22
- | Partner owner / admin, or a collection admin | Make sets, mint ranges, download the printer file and instructions, import UIDs, program tags, assign |
23
- | Partner assigner | See ranges and tags (never keys), assign |
24
-
25
- Assigning always needs you to be an admin of the collection the tags go to.
26
-
27
- ## The flow
28
-
29
- ```ts
30
- import { partner, claimSet } from '@proveanything/smartlinks'
31
-
32
- const [p] = await partner.list() // the partners you belong to
33
- const scope = { partnerId: p.id }
34
- const [set] = await partner.claimSets(p.id) // or partner.createClaimSet(p.id, { name, tagType: 'ntag424', tapDomain })
35
-
36
- // 1. Mint
37
- const range = await claimSet.ranges.create(scope, set.id, { name: 'Tap to donate - February', quantity: 100 })
38
-
39
- // 2. Send to print: the CSV (carries each tag's key) and the instruction sheet
40
- const file = await claimSet.ranges.exportFile(scope, set.id, range.id) // { filename, csv }
41
- const sheet = await claimSet.ranges.instructions(scope, set.id, range.id, 'pdf') // { filename, base64 }
42
- await claimSet.ranges.update(scope, set.id, range.id, { stage: 'sent', manufacturer: 'Print Co', orderRef: 'PO-123' })
43
-
44
- // 3. Receive: the manufacturer sends back the same file with UID filled in
45
- const check = await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv })
46
- // check.rows: ok / index_mismatch / bad_uid / uid_in_use / … ; nothing written yet
47
- await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv, commit: true })
48
-
49
- // 4. Assign all of it, or from-to
50
- await claimSet.ranges.assign(scope, set.id, range.id, { collectionId: 'reading-fc', productId: 'scarf', start: 201, end: 250 })
51
- ```
52
-
53
- **Programming on a phone instead of a manufacturer** (Sidekick): `ranges.next()` gives the lowest
54
- tag without a chip, with its URL and key; write it, then `ranges.recordTag(scope, set.id, range.id, code, { uid })`.
55
-
56
- ## The printer file
57
-
58
- One row per tag, in index order. The manufacturer fills in `UID` and sends the same file back; rows are
59
- matched by `Code`, and `Index` is checked.
60
-
61
- | Tag type | Columns |
62
- |---|---|
63
- | NTAG 424 DNA | `Index, Code, URL, PiccOffset, Key, UID` |
64
- | NTAG 213/215/216 | `Index, Code, URL, MirrorOffset, MirrorPage, MirrorByte, UID` |
65
- | QR | `Index, Code, URL` |
66
-
67
- `URL` is `https://{tap domain}/t/{set}/{code}` plus the placeholder the chip fills on every read
68
- (`?p=` + 32 zeros for 424 SUN, `?m=00000000000000x000000` for the 21x UID and counter mirror). The
69
- offsets are byte positions in the NDEF data, assuming the `https://` URI prefix code and a short record.
70
-
71
- ## Notes
72
-
73
- - Range responses are never cached by the SDK: tags change as they're programmed and several carry keys.
74
- - A tag bound to a proof (minted or claimed) is never moved or cleared by `assign`; it's reported in `skipped`.
75
- - `status` is the stage (`minted`, `sent`, `received`), or `assigned` once every tag is assigned.
1
+ # NFC tag ranges and partners
2
+
3
+ For partners and enterprise clients who run their own tag programmes: mint a batch of tags, send them
4
+ to a manufacturer with instructions, get the chip UIDs back, then put the tags to work for a client or
5
+ product. Ordinary business collections don't use this.
6
+
7
+ ## The words
8
+
9
+ | Word | Means |
10
+ |---|---|
11
+ | **Claim set** | A pool of codes on one tap domain and one tag type (NTAG 424 DNA, NTAG 213/215/216, or QR). Most partners need one. |
12
+ | **Range** | A named, contiguous block of tags inside a set: "Tap to donate - February", 100 tags, indexes 201-300. |
13
+ | **Index** | A tag's place in its set. Ranges never overlap and indexes are never reused. |
14
+ | **Partner** | An organisation above collections that owns sets and assigns their tags into collections its people administer. Set up by SmartLinks staff. |
15
+
16
+ ## Who can do what
17
+
18
+ `scope` is `{ partnerId }` for a partner's sets, or `{ collectionId }` for a collection's own sets.
19
+
20
+ | Role | Can |
21
+ |---|---|
22
+ | Partner owner / admin, or a collection admin | Make sets, mint ranges, download the printer file and instructions, import UIDs, program tags, assign |
23
+ | Partner assigner | See ranges and tags (never keys), assign |
24
+
25
+ Assigning always needs you to be an admin of the collection the tags go to.
26
+
27
+ ## The flow
28
+
29
+ ```ts
30
+ import { partner, claimSet } from '@proveanything/smartlinks'
31
+
32
+ const [p] = await partner.list() // the partners you belong to
33
+ const scope = { partnerId: p.id }
34
+ const [set] = await partner.claimSets(p.id) // or partner.createClaimSet(p.id, { name, tagType: 'ntag424', tapDomain })
35
+
36
+ // 1. Mint
37
+ const range = await claimSet.ranges.create(scope, set.id, { name: 'Tap to donate - February', quantity: 100 })
38
+
39
+ // 2. Send to print: the CSV (carries each tag's key) and the instruction sheet
40
+ const file = await claimSet.ranges.exportFile(scope, set.id, range.id) // { filename, csv }
41
+ const sheet = await claimSet.ranges.instructions(scope, set.id, range.id, 'pdf') // { filename, base64 }
42
+ await claimSet.ranges.update(scope, set.id, range.id, { stage: 'sent', manufacturer: 'Print Co', orderRef: 'PO-123' })
43
+
44
+ // 3. Receive: the manufacturer sends back the same file with UID filled in
45
+ const check = await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv })
46
+ // check.rows: ok / index_mismatch / bad_uid / uid_in_use / … ; nothing written yet
47
+ await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv, commit: true })
48
+
49
+ // 4. Assign all of it, or from-to
50
+ await claimSet.ranges.assign(scope, set.id, range.id, { collectionId: 'reading-fc', productId: 'scarf', start: 201, end: 250 })
51
+ ```
52
+
53
+ **Programming on a phone instead of a manufacturer** (Sidekick): `ranges.next()` gives the lowest
54
+ tag without a chip, with its URL and key; write it, then `ranges.recordTag(scope, set.id, range.id, code, { uid })` (add `password` for a plain tag's write password).
55
+
56
+ To find where a chip or code went, `partner.lookupTags(partner.id, { uid })` or `{ code }` searches every set the partner owns and returns each hit's set, tag and range, never a key.
57
+
58
+ ## The printer file
59
+
60
+ One row per tag, in index order. The manufacturer fills in `UID` and sends the same file back; rows are
61
+ matched by `Code`, and `Index` is checked.
62
+
63
+ | Tag type | Columns |
64
+ |---|---|
65
+ | NTAG 424 DNA | `Index, Code, URL, PiccOffset, Key, UID` |
66
+ | NTAG 213/215/216 | `Index, Code, URL, MirrorOffset, MirrorPage, MirrorByte, UID` |
67
+ | QR | `Index, Code, URL` |
68
+
69
+ `URL` is `https://{tap domain}/t/{set}/{code}` plus the placeholder the chip fills on every read
70
+ (`?p=` + 32 zeros for 424 SUN, `?m=00000000000000x000000` for the 21x UID and counter mirror). The
71
+ offsets are byte positions in the NDEF data, assuming the `https://` URI prefix code and a short record.
72
+
73
+ ## Notes
74
+
75
+ - Range responses are never cached by the SDK: tags change as they're programmed and several carry keys.
76
+ - A tag bound to a proof (minted or claimed) is never moved or cleared by `assign`; it's reported in `skipped`.
77
+ - `status` is the stage (`minted`, `sent`, `received`), or `assigned` once every tag is assigned.
package/dist/openapi.yaml CHANGED
@@ -9141,6 +9141,45 @@ paths:
9141
9141
  description: Unauthorized
9142
9142
  404:
9143
9143
  description: Not found
9144
+ /admin/partner/{partnerId}/tags/lookup:
9145
+ get:
9146
+ tags:
9147
+ - partner
9148
+ summary: "Find a tag across the partner's sets by its chip UID (8 or 14 hex) or its code (printed or bare)."
9149
+ operationId: partner_lookupTags
9150
+ security:
9151
+ - bearerAuth: []
9152
+ parameters:
9153
+ - name: partnerId
9154
+ in: path
9155
+ required: true
9156
+ schema:
9157
+ type: string
9158
+ - name: uid
9159
+ in: query
9160
+ required: false
9161
+ schema:
9162
+ type: string
9163
+ - name: code
9164
+ in: query
9165
+ required: false
9166
+ schema:
9167
+ type: string
9168
+ responses:
9169
+ 200:
9170
+ description: Success
9171
+ content:
9172
+ application/json:
9173
+ schema:
9174
+ type: array
9175
+ items:
9176
+ $ref: "#/components/schemas/PartnerTagHit"
9177
+ 400:
9178
+ description: Bad request
9179
+ 401:
9180
+ description: Unauthorized
9181
+ 404:
9182
+ description: Not found
9144
9183
  /authKit/{authKitId}/config:
9145
9184
  get:
9146
9185
  tags:
@@ -26120,6 +26159,25 @@ components:
26120
26159
  - codes
26121
26160
  - deleted
26122
26161
  - createdAt
26162
+ PartnerTagHit:
26163
+ type: object
26164
+ properties:
26165
+ namespace:
26166
+ type: string
26167
+ claimSetId:
26168
+ type: string
26169
+ set:
26170
+ $ref: "#/components/schemas/PartnerClaimSet"
26171
+ tag:
26172
+ $ref: "#/components/schemas/ClaimSetRangeTag"
26173
+ range:
26174
+ $ref: "#/components/schemas/ClaimSetRange"
26175
+ required:
26176
+ - namespace
26177
+ - claimSetId
26178
+ - set
26179
+ - tag
26180
+ - range
26123
26181
  CreatePartnerClaimSetRequest:
26124
26182
  type: object
26125
26183
  properties:
@@ -31324,6 +31382,10 @@ components:
31324
31382
  type: string
31325
31383
  mirror:
31326
31384
  type: string
31385
+ d:
31386
+ type: string
31387
+ c:
31388
+ type: string
31327
31389
  userId:
31328
31390
  type: string
31329
31391
  required:
@@ -301,6 +301,16 @@ export interface PartnerClaimSet {
301
301
  deleted: boolean;
302
302
  createdAt: string | null;
303
303
  }
304
+ /** One hit from partner.lookupTags */
305
+ export interface PartnerTagHit {
306
+ namespace: string;
307
+ claimSetId: string;
308
+ set: PartnerClaimSet | null;
309
+ tag: ClaimSetRangeTag & {
310
+ url: string | null;
311
+ };
312
+ range: ClaimSetRange | null;
313
+ }
304
314
  export interface CreatePartnerClaimSetRequest {
305
315
  name: string;
306
316
  tagType: ClaimSetTagType;
@@ -14,6 +14,10 @@ export interface NfcValidateRequest {
14
14
  claimSetId: string;
15
15
  codeId: string;
16
16
  mirror?: string;
17
+ /** NTAG 424 DNA: the encrypted PICC block from the tag's URL (`?p=`, 32 hex) */
18
+ d?: string;
19
+ /** NTAG 424 DNA: the tag's SDM MAC from its URL (`&c=`, 16 hex). Verified by the server when present; always forward it. */
20
+ c?: string;
17
21
  userId?: string;
18
22
  }
19
23
  export interface NfcValidateResponse {
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.1.11 | Generated: 2026-11-08T09:25:58.482Z
3
+ Version: 2.1.12 | Generated: 2026-11-08T14:30:42.034Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -5894,6 +5894,17 @@ interface PartnerClaimSet {
5894
5894
  }
5895
5895
  ```
5896
5896
 
5897
+ **PartnerTagHit** (interface)
5898
+ ```typescript
5899
+ interface PartnerTagHit {
5900
+ namespace: string
5901
+ claimSetId: string
5902
+ set: PartnerClaimSet | null
5903
+ tag: ClaimSetRangeTag & { url: string | null }
5904
+ range: ClaimSetRange | null
5905
+ }
5906
+ ```
5907
+
5897
5908
  **CreatePartnerClaimSetRequest** (interface)
5898
5909
  ```typescript
5899
5910
  interface CreatePartnerClaimSetRequest {
@@ -9330,6 +9341,8 @@ interface NfcValidateRequest {
9330
9341
  claimSetId: string
9331
9342
  codeId: string
9332
9343
  mirror?: string
9344
+ d?: string
9345
+ c?: string
9333
9346
  userId?: string
9334
9347
  }
9335
9348
  ```
@@ -12327,21 +12340,9 @@ The range's tags in index order, a page at a time (keys: owners/admins only).
12327
12340
  The next tag to program on a phone (lowest index without a chip), with its URL and key.
12328
12341
 
12329
12342
  **recordTag**(scope: ClaimSetScope, claimSetId: string, rangeId: string, code: string,
12330
- params: { uid: string; data?: Record<string, unknown> }) → `Promise<`
12343
+ params: { uid: string; data?: Record<string, unknown>; /** A plain (NTAG 21x) → `void`
12331
12344
  Record the chip a phone just programmed with this code.
12332
12345
 
12333
- **exportFile**(scope: ClaimSetScope, claimSetId: string, rangeId: string) → `Promise<ClaimSetRangeFile>`
12334
- The printer file (CSV text in `csv`). Carries the tags' keys: don't keep it around.
12335
-
12336
- **instructions**(scope: ClaimSetScope, claimSetId: string, rangeId: string, format: 'pdf' | 'md' = 'pdf') → `Promise<ClaimSetRangeFile>`
12337
- The manufacturer's instruction sheet: PDF (base64) or Markdown.
12338
-
12339
- **importUids**(scope: ClaimSetScope, claimSetId: string, rangeId: string, params: ImportClaimSetRangeUidsRequest) → `Promise<ImportClaimSetRangeUidsResponse>`
12340
- The manufacturer's file back: check it (commit false), then write the chip UIDs (commit true).
12341
-
12342
- **assign**(scope: ClaimSetScope, claimSetId: string, rangeId: string, params: AssignClaimSetRangeRequest) → `Promise<AssignClaimSetRangeResponse>`
12343
- Point the range (or from–to within it) at a collection / product / batch, or clear it.
12344
-
12345
12346
  ### collection
12346
12347
 
12347
12348
  **get**(collectionId: string, admin?: boolean) → `Promise<CollectionResponse>`
@@ -13270,6 +13271,9 @@ One of the partner's claim sets.
13270
13271
  **createClaimSet**(partnerId: string, params: CreatePartnerClaimSetRequest) → `Promise<PartnerClaimSet>`
13271
13272
  A new claim set for the partner, on one of its tap domains (owners and admins).
13272
13273
 
13274
+ **lookupTags**(partnerId: string, query: { uid?: string; code?: string }) → `Promise<PartnerTagHit[]>`
13275
+ Find a tag across the partner's sets by its chip UID (8 or 14 hex) or its code (printed or bare). Never returns keys.
13276
+
13273
13277
  ### podcast
13274
13278
 
13275
13279
  **generate**(collectionId: string,
@@ -1,75 +1,77 @@
1
- # NFC tag ranges and partners
2
-
3
- For partners and enterprise clients who run their own tag programmes: mint a batch of tags, send them
4
- to a manufacturer with instructions, get the chip UIDs back, then put the tags to work for a client or
5
- product. Ordinary business collections don't use this.
6
-
7
- ## The words
8
-
9
- | Word | Means |
10
- |---|---|
11
- | **Claim set** | A pool of codes on one tap domain and one tag type (NTAG 424 DNA, NTAG 213/215/216, or QR). Most partners need one. |
12
- | **Range** | A named, contiguous block of tags inside a set: "Tap to donate - February", 100 tags, indexes 201-300. |
13
- | **Index** | A tag's place in its set. Ranges never overlap and indexes are never reused. |
14
- | **Partner** | An organisation above collections that owns sets and assigns their tags into collections its people administer. Set up by SmartLinks staff. |
15
-
16
- ## Who can do what
17
-
18
- `scope` is `{ partnerId }` for a partner's sets, or `{ collectionId }` for a collection's own sets.
19
-
20
- | Role | Can |
21
- |---|---|
22
- | Partner owner / admin, or a collection admin | Make sets, mint ranges, download the printer file and instructions, import UIDs, program tags, assign |
23
- | Partner assigner | See ranges and tags (never keys), assign |
24
-
25
- Assigning always needs you to be an admin of the collection the tags go to.
26
-
27
- ## The flow
28
-
29
- ```ts
30
- import { partner, claimSet } from '@proveanything/smartlinks'
31
-
32
- const [p] = await partner.list() // the partners you belong to
33
- const scope = { partnerId: p.id }
34
- const [set] = await partner.claimSets(p.id) // or partner.createClaimSet(p.id, { name, tagType: 'ntag424', tapDomain })
35
-
36
- // 1. Mint
37
- const range = await claimSet.ranges.create(scope, set.id, { name: 'Tap to donate - February', quantity: 100 })
38
-
39
- // 2. Send to print: the CSV (carries each tag's key) and the instruction sheet
40
- const file = await claimSet.ranges.exportFile(scope, set.id, range.id) // { filename, csv }
41
- const sheet = await claimSet.ranges.instructions(scope, set.id, range.id, 'pdf') // { filename, base64 }
42
- await claimSet.ranges.update(scope, set.id, range.id, { stage: 'sent', manufacturer: 'Print Co', orderRef: 'PO-123' })
43
-
44
- // 3. Receive: the manufacturer sends back the same file with UID filled in
45
- const check = await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv })
46
- // check.rows: ok / index_mismatch / bad_uid / uid_in_use / … ; nothing written yet
47
- await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv, commit: true })
48
-
49
- // 4. Assign all of it, or from-to
50
- await claimSet.ranges.assign(scope, set.id, range.id, { collectionId: 'reading-fc', productId: 'scarf', start: 201, end: 250 })
51
- ```
52
-
53
- **Programming on a phone instead of a manufacturer** (Sidekick): `ranges.next()` gives the lowest
54
- tag without a chip, with its URL and key; write it, then `ranges.recordTag(scope, set.id, range.id, code, { uid })`.
55
-
56
- ## The printer file
57
-
58
- One row per tag, in index order. The manufacturer fills in `UID` and sends the same file back; rows are
59
- matched by `Code`, and `Index` is checked.
60
-
61
- | Tag type | Columns |
62
- |---|---|
63
- | NTAG 424 DNA | `Index, Code, URL, PiccOffset, Key, UID` |
64
- | NTAG 213/215/216 | `Index, Code, URL, MirrorOffset, MirrorPage, MirrorByte, UID` |
65
- | QR | `Index, Code, URL` |
66
-
67
- `URL` is `https://{tap domain}/t/{set}/{code}` plus the placeholder the chip fills on every read
68
- (`?p=` + 32 zeros for 424 SUN, `?m=00000000000000x000000` for the 21x UID and counter mirror). The
69
- offsets are byte positions in the NDEF data, assuming the `https://` URI prefix code and a short record.
70
-
71
- ## Notes
72
-
73
- - Range responses are never cached by the SDK: tags change as they're programmed and several carry keys.
74
- - A tag bound to a proof (minted or claimed) is never moved or cleared by `assign`; it's reported in `skipped`.
75
- - `status` is the stage (`minted`, `sent`, `received`), or `assigned` once every tag is assigned.
1
+ # NFC tag ranges and partners
2
+
3
+ For partners and enterprise clients who run their own tag programmes: mint a batch of tags, send them
4
+ to a manufacturer with instructions, get the chip UIDs back, then put the tags to work for a client or
5
+ product. Ordinary business collections don't use this.
6
+
7
+ ## The words
8
+
9
+ | Word | Means |
10
+ |---|---|
11
+ | **Claim set** | A pool of codes on one tap domain and one tag type (NTAG 424 DNA, NTAG 213/215/216, or QR). Most partners need one. |
12
+ | **Range** | A named, contiguous block of tags inside a set: "Tap to donate - February", 100 tags, indexes 201-300. |
13
+ | **Index** | A tag's place in its set. Ranges never overlap and indexes are never reused. |
14
+ | **Partner** | An organisation above collections that owns sets and assigns their tags into collections its people administer. Set up by SmartLinks staff. |
15
+
16
+ ## Who can do what
17
+
18
+ `scope` is `{ partnerId }` for a partner's sets, or `{ collectionId }` for a collection's own sets.
19
+
20
+ | Role | Can |
21
+ |---|---|
22
+ | Partner owner / admin, or a collection admin | Make sets, mint ranges, download the printer file and instructions, import UIDs, program tags, assign |
23
+ | Partner assigner | See ranges and tags (never keys), assign |
24
+
25
+ Assigning always needs you to be an admin of the collection the tags go to.
26
+
27
+ ## The flow
28
+
29
+ ```ts
30
+ import { partner, claimSet } from '@proveanything/smartlinks'
31
+
32
+ const [p] = await partner.list() // the partners you belong to
33
+ const scope = { partnerId: p.id }
34
+ const [set] = await partner.claimSets(p.id) // or partner.createClaimSet(p.id, { name, tagType: 'ntag424', tapDomain })
35
+
36
+ // 1. Mint
37
+ const range = await claimSet.ranges.create(scope, set.id, { name: 'Tap to donate - February', quantity: 100 })
38
+
39
+ // 2. Send to print: the CSV (carries each tag's key) and the instruction sheet
40
+ const file = await claimSet.ranges.exportFile(scope, set.id, range.id) // { filename, csv }
41
+ const sheet = await claimSet.ranges.instructions(scope, set.id, range.id, 'pdf') // { filename, base64 }
42
+ await claimSet.ranges.update(scope, set.id, range.id, { stage: 'sent', manufacturer: 'Print Co', orderRef: 'PO-123' })
43
+
44
+ // 3. Receive: the manufacturer sends back the same file with UID filled in
45
+ const check = await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv })
46
+ // check.rows: ok / index_mismatch / bad_uid / uid_in_use / … ; nothing written yet
47
+ await claimSet.ranges.importUids(scope, set.id, range.id, { csv: returnedCsv, commit: true })
48
+
49
+ // 4. Assign all of it, or from-to
50
+ await claimSet.ranges.assign(scope, set.id, range.id, { collectionId: 'reading-fc', productId: 'scarf', start: 201, end: 250 })
51
+ ```
52
+
53
+ **Programming on a phone instead of a manufacturer** (Sidekick): `ranges.next()` gives the lowest
54
+ tag without a chip, with its URL and key; write it, then `ranges.recordTag(scope, set.id, range.id, code, { uid })` (add `password` for a plain tag's write password).
55
+
56
+ To find where a chip or code went, `partner.lookupTags(partner.id, { uid })` or `{ code }` searches every set the partner owns and returns each hit's set, tag and range, never a key.
57
+
58
+ ## The printer file
59
+
60
+ One row per tag, in index order. The manufacturer fills in `UID` and sends the same file back; rows are
61
+ matched by `Code`, and `Index` is checked.
62
+
63
+ | Tag type | Columns |
64
+ |---|---|
65
+ | NTAG 424 DNA | `Index, Code, URL, PiccOffset, Key, UID` |
66
+ | NTAG 213/215/216 | `Index, Code, URL, MirrorOffset, MirrorPage, MirrorByte, UID` |
67
+ | QR | `Index, Code, URL` |
68
+
69
+ `URL` is `https://{tap domain}/t/{set}/{code}` plus the placeholder the chip fills on every read
70
+ (`?p=` + 32 zeros for 424 SUN, `?m=00000000000000x000000` for the 21x UID and counter mirror). The
71
+ offsets are byte positions in the NDEF data, assuming the `https://` URI prefix code and a short record.
72
+
73
+ ## Notes
74
+
75
+ - Range responses are never cached by the SDK: tags change as they're programmed and several carry keys.
76
+ - A tag bound to a proof (minted or claimed) is never moved or cleared by `assign`; it's reported in `skipped`.
77
+ - `status` is the stage (`minted`, `sent`, `received`), or `assigned` once every tag is assigned.
package/openapi.yaml CHANGED
@@ -9141,6 +9141,45 @@ paths:
9141
9141
  description: Unauthorized
9142
9142
  404:
9143
9143
  description: Not found
9144
+ /admin/partner/{partnerId}/tags/lookup:
9145
+ get:
9146
+ tags:
9147
+ - partner
9148
+ summary: "Find a tag across the partner's sets by its chip UID (8 or 14 hex) or its code (printed or bare)."
9149
+ operationId: partner_lookupTags
9150
+ security:
9151
+ - bearerAuth: []
9152
+ parameters:
9153
+ - name: partnerId
9154
+ in: path
9155
+ required: true
9156
+ schema:
9157
+ type: string
9158
+ - name: uid
9159
+ in: query
9160
+ required: false
9161
+ schema:
9162
+ type: string
9163
+ - name: code
9164
+ in: query
9165
+ required: false
9166
+ schema:
9167
+ type: string
9168
+ responses:
9169
+ 200:
9170
+ description: Success
9171
+ content:
9172
+ application/json:
9173
+ schema:
9174
+ type: array
9175
+ items:
9176
+ $ref: "#/components/schemas/PartnerTagHit"
9177
+ 400:
9178
+ description: Bad request
9179
+ 401:
9180
+ description: Unauthorized
9181
+ 404:
9182
+ description: Not found
9144
9183
  /authKit/{authKitId}/config:
9145
9184
  get:
9146
9185
  tags:
@@ -26120,6 +26159,25 @@ components:
26120
26159
  - codes
26121
26160
  - deleted
26122
26161
  - createdAt
26162
+ PartnerTagHit:
26163
+ type: object
26164
+ properties:
26165
+ namespace:
26166
+ type: string
26167
+ claimSetId:
26168
+ type: string
26169
+ set:
26170
+ $ref: "#/components/schemas/PartnerClaimSet"
26171
+ tag:
26172
+ $ref: "#/components/schemas/ClaimSetRangeTag"
26173
+ range:
26174
+ $ref: "#/components/schemas/ClaimSetRange"
26175
+ required:
26176
+ - namespace
26177
+ - claimSetId
26178
+ - set
26179
+ - tag
26180
+ - range
26123
26181
  CreatePartnerClaimSetRequest:
26124
26182
  type: object
26125
26183
  properties:
@@ -31324,6 +31382,10 @@ components:
31324
31382
  type: string
31325
31383
  mirror:
31326
31384
  type: string
31385
+ d:
31386
+ type: string
31387
+ c:
31388
+ type: string
31327
31389
  userId:
31328
31390
  type: string
31329
31391
  required:
package/package.json CHANGED
@@ -1,87 +1,87 @@
1
- {
2
- "name": "@proveanything/smartlinks",
3
- "version": "2.1.11",
4
- "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js",
12
- "default": "./dist/index.js"
13
- },
14
- "./testing": {
15
- "types": "./dist/testing/index.d.ts",
16
- "import": "./dist/testing/index.js",
17
- "default": "./dist/testing/index.js"
18
- },
19
- "./*": {
20
- "types": "./dist/*.d.ts",
21
- "import": "./dist/*.js",
22
- "default": "./dist/*.js"
23
- },
24
- "./baseline.css": "./dist/baseline.css",
25
- "./baseline.classes.json": "./dist/baseline.classes.json",
26
- "./theme.css": "./dist/theme.css",
27
- "./theme-boot.js": "./dist/theme-boot.js",
28
- "./package.json": "./package.json"
29
- },
30
- "bin": {
31
- "smartlinks-register-release": "scripts/register-release.mjs",
32
- "smartlinks-publish": "scripts/publish.mjs",
33
- "smartlinks-doctor": "scripts/doctor.mjs",
34
- "smartlinks-headless": "scripts/headless-check.mjs"
35
- },
36
- "files": [
37
- "dist/",
38
- "docs/",
39
- "scripts/register-release.mjs",
40
- "scripts/publish.mjs",
41
- "scripts/doctor.mjs",
42
- "scripts/headless-check.mjs",
43
- "scripts/lib/",
44
- "openapi.yaml",
45
- "README.md"
46
- ],
47
- "scripts": {
48
- "test": "node test/run.cjs",
49
- "build": "tsc && node scripts/fix-esm-extensions.mjs && node generate-api-summary.cjs && node generate-openapi.cjs && node scripts/copy-docs-to-dist.cjs && node scripts/build-baseline.mjs",
50
- "docs": "typedoc",
51
- "docs:summary": "node generate-api-summary.cjs",
52
- "docs:openapi": "node generate-openapi.cjs",
53
- "docs:openapi:lint": "npx --yes @redocly/cli@latest lint openapi.yaml",
54
- "docs:openapi:check": "node generate-openapi.cjs && npx --yes @redocly/cli@latest lint openapi.yaml",
55
- "build:docs": "tsc build-docs.ts --outDir dist && node dist/build-docs.js",
56
- "prepublishOnly": "npm run build"
57
- },
58
- "keywords": [
59
- "smartlinks",
60
- "api",
61
- "authentication",
62
- "traceability",
63
- "blockchain",
64
- "typescript",
65
- "sdk"
66
- ],
67
- "author": "Glenn Shoosmith",
68
- "license": "MIT",
69
- "publishConfig": {
70
- "access": "public"
71
- },
72
- "dependencies": {
73
- "cross-fetch": "^3.1.5",
74
- "es-module-lexer": "^1.7.0"
75
- },
76
- "devDependencies": {
77
- "ts-node": "^10.9.1",
78
- "typedoc": "^0.25.13",
79
- "typedoc-plugin-markdown": "^3.17.1",
80
- "typescript": "^4.9.0"
81
- },
82
- "repository": {
83
- "type": "git",
84
- "url": "https://github.com/Prove-Anything/smartlinks.git"
85
- },
86
- "homepage": "https://smartlinks.app"
87
- }
1
+ {
2
+ "name": "@proveanything/smartlinks",
3
+ "version": "2.1.12",
4
+ "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./testing": {
15
+ "types": "./dist/testing/index.d.ts",
16
+ "import": "./dist/testing/index.js",
17
+ "default": "./dist/testing/index.js"
18
+ },
19
+ "./*": {
20
+ "types": "./dist/*.d.ts",
21
+ "import": "./dist/*.js",
22
+ "default": "./dist/*.js"
23
+ },
24
+ "./baseline.css": "./dist/baseline.css",
25
+ "./baseline.classes.json": "./dist/baseline.classes.json",
26
+ "./theme.css": "./dist/theme.css",
27
+ "./theme-boot.js": "./dist/theme-boot.js",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "bin": {
31
+ "smartlinks-register-release": "scripts/register-release.mjs",
32
+ "smartlinks-publish": "scripts/publish.mjs",
33
+ "smartlinks-doctor": "scripts/doctor.mjs",
34
+ "smartlinks-headless": "scripts/headless-check.mjs"
35
+ },
36
+ "files": [
37
+ "dist/",
38
+ "docs/",
39
+ "scripts/register-release.mjs",
40
+ "scripts/publish.mjs",
41
+ "scripts/doctor.mjs",
42
+ "scripts/headless-check.mjs",
43
+ "scripts/lib/",
44
+ "openapi.yaml",
45
+ "README.md"
46
+ ],
47
+ "scripts": {
48
+ "test": "node test/run.cjs",
49
+ "build": "tsc && node scripts/fix-esm-extensions.mjs && node generate-api-summary.cjs && node generate-openapi.cjs && node scripts/copy-docs-to-dist.cjs && node scripts/build-baseline.mjs",
50
+ "docs": "typedoc",
51
+ "docs:summary": "node generate-api-summary.cjs",
52
+ "docs:openapi": "node generate-openapi.cjs",
53
+ "docs:openapi:lint": "npx --yes @redocly/cli@latest lint openapi.yaml",
54
+ "docs:openapi:check": "node generate-openapi.cjs && npx --yes @redocly/cli@latest lint openapi.yaml",
55
+ "build:docs": "tsc build-docs.ts --outDir dist && node dist/build-docs.js",
56
+ "prepublishOnly": "npm run build"
57
+ },
58
+ "keywords": [
59
+ "smartlinks",
60
+ "api",
61
+ "authentication",
62
+ "traceability",
63
+ "blockchain",
64
+ "typescript",
65
+ "sdk"
66
+ ],
67
+ "author": "Glenn Shoosmith",
68
+ "license": "MIT",
69
+ "publishConfig": {
70
+ "access": "public"
71
+ },
72
+ "dependencies": {
73
+ "cross-fetch": "^3.1.5",
74
+ "es-module-lexer": "^1.7.0"
75
+ },
76
+ "devDependencies": {
77
+ "ts-node": "^10.9.1",
78
+ "typedoc": "^0.25.13",
79
+ "typedoc-plugin-markdown": "^3.17.1",
80
+ "typescript": "^4.9.0"
81
+ },
82
+ "repository": {
83
+ "type": "git",
84
+ "url": "https://github.com/Prove-Anything/smartlinks.git"
85
+ },
86
+ "homepage": "https://smartlinks.app"
87
+ }