@proveanything/smartlinks 1.16.6 → 1.16.7
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.
- package/dist/api/attestations.d.ts +39 -10
- package/dist/api/attestations.js +42 -9
- package/dist/api/proof.js +3 -0
- package/dist/docs/API_SUMMARY.md +48 -3
- package/dist/docs/attestations.md +19 -0
- package/dist/docs/proof-share-grants.md +78 -5
- package/dist/openapi.yaml +82 -1
- package/dist/types/attestations.d.ts +51 -4
- package/dist/types/proof.d.ts +22 -2
- package/docs/API_SUMMARY.md +48 -3
- package/docs/attestations.md +19 -0
- package/docs/proof-share-grants.md +78 -5
- package/openapi.yaml +82 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListAttestationsResponse, AttestationSummaryParams, AttestationSummaryResponse, PublicAttestationSummaryResponse, AttestationLatestParams, AttestationLatestResponse, PublicAttestationLatestResponse, AttestationVerifyParams, ChainVerifyResult, AttestationTreeSummaryParams, AttestationTreeSummaryResponse, PublicAttestationTreeSummaryResponse, AttestationTreeLatestParams, AttestationTreeLatestResponse, PublicAttestationTreeLatestResponse, PublicListAttestationsResponse, OwnerAttestationInput, CreateOwnerAttestationResponse } from "../types/attestations";
|
|
1
|
+
import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListAttestationsResponse, AttestationSummaryParams, AttestationSummaryResponse, PublicAttestationSummaryResponse, AttestationLatestParams, AttestationLatestResponse, PublicAttestationLatestResponse, AttestationVerifyParams, ChainVerifyResult, AttestationTreeSummaryParams, AttestationTreeSummaryResponse, PublicAttestationTreeSummaryResponse, AttestationTreeLatestParams, AttestationTreeLatestResponse, PublicAttestationTreeLatestResponse, PublicListAttestationsResponse, OwnerAttestationInput, CreateOwnerAttestationResponse, ModerateAttestationInput, ModerateAttestationResponse } from "../types/attestations";
|
|
2
2
|
/**
|
|
3
3
|
* Postgres-backed Attestations API (v2).
|
|
4
4
|
*
|
|
@@ -13,9 +13,11 @@ import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListA
|
|
|
13
13
|
* admin session or bearer token. All three data zones are returned, and the
|
|
14
14
|
* business can write any zone/visibility ({@link create}).
|
|
15
15
|
* - **Public** endpoints (`/public/collection/:id/attestations`) are read-only,
|
|
16
|
-
* EXCEPT {@link publicCreate}
|
|
17
|
-
*
|
|
18
|
-
* zone)
|
|
16
|
+
* EXCEPT {@link publicCreate} (the proof OWNER, or a `contribute`-grant holder,
|
|
17
|
+
* authors an attestation — value + ownerData, visibility public|owner, never the
|
|
18
|
+
* admin zone) and {@link moderate} (owner/admin approves or rejects a contributed
|
|
19
|
+
* record). Owner elevation is via `Authorization: Bearer <Firebase ID token>`;
|
|
20
|
+
* contributor access via a grant token set with `setGrantToken`.
|
|
19
21
|
*
|
|
20
22
|
* @see docs/attestations.md
|
|
21
23
|
*/
|
|
@@ -211,22 +213,49 @@ export declare namespace attestations {
|
|
|
211
213
|
*/
|
|
212
214
|
function publicList(collectionId: string, params: ListAttestationsParams): Promise<PublicListAttestationsResponse>;
|
|
213
215
|
/**
|
|
214
|
-
* Create
|
|
215
|
-
*
|
|
216
|
-
* (identity,
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
216
|
+
* Create a public attestation — the counterpart to the admin {@link create}.
|
|
217
|
+
* Authorised two ways, same call (the server decides from the request):
|
|
218
|
+
* 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`)
|
|
219
|
+
* adds an attestation to their own item; or
|
|
220
|
+
* 2. a holder of a `contribute`-scope grant adds one — call
|
|
221
|
+
* {@link setGrantToken} with the grant token first; for a public-link
|
|
222
|
+
* (anonymous) grant, pass `guestName` for attribution.
|
|
223
|
+
*
|
|
224
|
+
* Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped),
|
|
225
|
+
* `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped.
|
|
226
|
+
* If the contribute grant was issued with `moderate: true`, the returned record
|
|
227
|
+
* has `moderationStatus: 'pending'` — held to the owner until {@link moderate}.
|
|
228
|
+
* The record joins the same tamper-evident hash chain.
|
|
220
229
|
* POST /public/collection/:collectionId/attestations
|
|
221
230
|
* ```ts
|
|
231
|
+
* // Owner:
|
|
222
232
|
* await attestations.publicCreate('coll_123', {
|
|
223
233
|
* subjectType: 'proof', subjectId: 'proof_1',
|
|
224
234
|
* attestationType: 'condition-report',
|
|
225
235
|
* value: { grade: 'excellent' }, visibility: 'public',
|
|
226
236
|
* })
|
|
237
|
+
* // Contributor on a shared link:
|
|
238
|
+
* setGrantToken(shareToken)
|
|
239
|
+
* await attestations.publicCreate('coll_123', {
|
|
240
|
+
* subjectType: 'proof', subjectId: 'proof_1',
|
|
241
|
+
* attestationType: 'photo', value: { url }, visibility: 'public',
|
|
242
|
+
* guestName: 'Sam',
|
|
243
|
+
* })
|
|
227
244
|
* ```
|
|
228
245
|
*/
|
|
229
246
|
function publicCreate(collectionId: string, data: OwnerAttestationInput): Promise<CreateOwnerAttestationResponse>;
|
|
247
|
+
/**
|
|
248
|
+
* Moderate a contributed attestation (proof OWNER by identity, or collection
|
|
249
|
+
* admin). `'approve'` releases it to its declared visibility; `'reject'` keeps
|
|
250
|
+
* it author + admin only. Only the `moderationStatus` changes — the hashed fact
|
|
251
|
+
* and its chain are untouched. Find pending items with
|
|
252
|
+
* {@link publicList} + `moderationStatus: 'pending'`.
|
|
253
|
+
* POST /public/collection/:collectionId/attestations/:attestationId/moderate
|
|
254
|
+
* ```ts
|
|
255
|
+
* await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' })
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
function moderate(collectionId: string, attestationId: string, input: ModerateAttestationInput): Promise<ModerateAttestationResponse>;
|
|
230
259
|
/**
|
|
231
260
|
* Time-series summary (public).
|
|
232
261
|
*
|
package/dist/api/attestations.js
CHANGED
|
@@ -26,9 +26,11 @@ function buildAttestationQuery(params) {
|
|
|
26
26
|
* admin session or bearer token. All three data zones are returned, and the
|
|
27
27
|
* business can write any zone/visibility ({@link create}).
|
|
28
28
|
* - **Public** endpoints (`/public/collection/:id/attestations`) are read-only,
|
|
29
|
-
* EXCEPT {@link publicCreate}
|
|
30
|
-
*
|
|
31
|
-
* zone)
|
|
29
|
+
* EXCEPT {@link publicCreate} (the proof OWNER, or a `contribute`-grant holder,
|
|
30
|
+
* authors an attestation — value + ownerData, visibility public|owner, never the
|
|
31
|
+
* admin zone) and {@link moderate} (owner/admin approves or rejects a contributed
|
|
32
|
+
* record). Owner elevation is via `Authorization: Bearer <Firebase ID token>`;
|
|
33
|
+
* contributor access via a grant token set with `setGrantToken`.
|
|
32
34
|
*
|
|
33
35
|
* @see docs/attestations.md
|
|
34
36
|
*/
|
|
@@ -277,19 +279,34 @@ export var attestations;
|
|
|
277
279
|
}
|
|
278
280
|
attestations.publicList = publicList;
|
|
279
281
|
/**
|
|
280
|
-
* Create
|
|
281
|
-
*
|
|
282
|
-
* (identity,
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
282
|
+
* Create a public attestation — the counterpart to the admin {@link create}.
|
|
283
|
+
* Authorised two ways, same call (the server decides from the request):
|
|
284
|
+
* 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`)
|
|
285
|
+
* adds an attestation to their own item; or
|
|
286
|
+
* 2. a holder of a `contribute`-scope grant adds one — call
|
|
287
|
+
* {@link setGrantToken} with the grant token first; for a public-link
|
|
288
|
+
* (anonymous) grant, pass `guestName` for attribution.
|
|
289
|
+
*
|
|
290
|
+
* Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped),
|
|
291
|
+
* `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped.
|
|
292
|
+
* If the contribute grant was issued with `moderate: true`, the returned record
|
|
293
|
+
* has `moderationStatus: 'pending'` — held to the owner until {@link moderate}.
|
|
294
|
+
* The record joins the same tamper-evident hash chain.
|
|
286
295
|
* POST /public/collection/:collectionId/attestations
|
|
287
296
|
* ```ts
|
|
297
|
+
* // Owner:
|
|
288
298
|
* await attestations.publicCreate('coll_123', {
|
|
289
299
|
* subjectType: 'proof', subjectId: 'proof_1',
|
|
290
300
|
* attestationType: 'condition-report',
|
|
291
301
|
* value: { grade: 'excellent' }, visibility: 'public',
|
|
292
302
|
* })
|
|
303
|
+
* // Contributor on a shared link:
|
|
304
|
+
* setGrantToken(shareToken)
|
|
305
|
+
* await attestations.publicCreate('coll_123', {
|
|
306
|
+
* subjectType: 'proof', subjectId: 'proof_1',
|
|
307
|
+
* attestationType: 'photo', value: { url }, visibility: 'public',
|
|
308
|
+
* guestName: 'Sam',
|
|
309
|
+
* })
|
|
293
310
|
* ```
|
|
294
311
|
*/
|
|
295
312
|
async function publicCreate(collectionId, data) {
|
|
@@ -297,6 +314,22 @@ export var attestations;
|
|
|
297
314
|
return post(path, data);
|
|
298
315
|
}
|
|
299
316
|
attestations.publicCreate = publicCreate;
|
|
317
|
+
/**
|
|
318
|
+
* Moderate a contributed attestation (proof OWNER by identity, or collection
|
|
319
|
+
* admin). `'approve'` releases it to its declared visibility; `'reject'` keeps
|
|
320
|
+
* it author + admin only. Only the `moderationStatus` changes — the hashed fact
|
|
321
|
+
* and its chain are untouched. Find pending items with
|
|
322
|
+
* {@link publicList} + `moderationStatus: 'pending'`.
|
|
323
|
+
* POST /public/collection/:collectionId/attestations/:attestationId/moderate
|
|
324
|
+
* ```ts
|
|
325
|
+
* await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' })
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
async function moderate(collectionId, attestationId, input) {
|
|
329
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/attestations/${encodeURIComponent(attestationId)}/moderate`;
|
|
330
|
+
return post(path, input);
|
|
331
|
+
}
|
|
332
|
+
attestations.moderate = moderate;
|
|
300
333
|
/**
|
|
301
334
|
* Time-series summary (public).
|
|
302
335
|
*
|
package/dist/api/proof.js
CHANGED
|
@@ -222,6 +222,9 @@ export var proof;
|
|
|
222
222
|
body.audience = options.audience;
|
|
223
223
|
if (options.expiresAt)
|
|
224
224
|
body.expiresAt = options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt;
|
|
225
|
+
// Only applied server-side when scope includes 'contribute'.
|
|
226
|
+
if (options.moderate !== undefined)
|
|
227
|
+
body.moderate = options.moderate;
|
|
225
228
|
return post(grantBase(collectionId, productId, proofId), body);
|
|
226
229
|
}
|
|
227
230
|
proof.createGrant = createGrant;
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.7 | Generated: 2026-09-09T11:29:21.917Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -2745,6 +2745,15 @@ interface Attestation {
|
|
|
2745
2745
|
unit?: string
|
|
2746
2746
|
source?: string
|
|
2747
2747
|
authorId?: string
|
|
2748
|
+
* When authored under a `contribute` grant (rather than by identity), the id of
|
|
2749
|
+
* the granting token — provenance for a contributed record. `null`/absent for
|
|
2750
|
+
* owner/admin/identity writes.
|
|
2751
|
+
grantId?: string | null
|
|
2752
|
+
* Moderation gate, orthogonal to {@link visibility} and excluded from the hash
|
|
2753
|
+
* chain. `'approved'` (default) is live; `'pending'` is held for owner review
|
|
2754
|
+
* (visible only to its author and owner/admin audiences); `'rejected'` was
|
|
2755
|
+
* declined. Contributions under a `moderate` grant start `'pending'`.
|
|
2756
|
+
moderationStatus?: AttestationModerationStatus
|
|
2748
2757
|
metadata?: Record<string, any>
|
|
2749
2758
|
contentHash: string
|
|
2750
2759
|
prevHash?: string
|
|
@@ -2810,6 +2819,23 @@ interface OwnerAttestationInput {
|
|
|
2810
2819
|
unit?: string
|
|
2811
2820
|
source?: string
|
|
2812
2821
|
metadata?: Record<string, any>
|
|
2822
|
+
* Attribution for an anonymous (public-link) contribute-grant write. Ignored
|
|
2823
|
+
* for owner writes and for named-grant writes (attributed to the signed-in uid).
|
|
2824
|
+
guestName?: string
|
|
2825
|
+
}
|
|
2826
|
+
```
|
|
2827
|
+
|
|
2828
|
+
**ModerateAttestationInput** (interface)
|
|
2829
|
+
```typescript
|
|
2830
|
+
interface ModerateAttestationInput {
|
|
2831
|
+
decision: 'approve' | 'reject'
|
|
2832
|
+
}
|
|
2833
|
+
```
|
|
2834
|
+
|
|
2835
|
+
**ModerateAttestationResponse** (interface)
|
|
2836
|
+
```typescript
|
|
2837
|
+
interface ModerateAttestationResponse {
|
|
2838
|
+
attestation: Attestation
|
|
2813
2839
|
}
|
|
2814
2840
|
```
|
|
2815
2841
|
|
|
@@ -2905,6 +2931,10 @@ interface ListAttestationsParams {
|
|
|
2905
2931
|
subjectType: AttestationSubjectType
|
|
2906
2932
|
subjectId: string
|
|
2907
2933
|
attestationType?: string
|
|
2934
|
+
* Filter by moderation state. Primarily for the owner review queue
|
|
2935
|
+
* (`moderationStatus: 'pending'`). ANDs with the server's audience gate, so a
|
|
2936
|
+
* public caller can never use it to widen access.
|
|
2937
|
+
moderationStatus?: AttestationModerationStatus
|
|
2908
2938
|
recordedAfter?: string
|
|
2909
2939
|
recordedBefore?: string
|
|
2910
2940
|
limit?: number
|
|
@@ -2969,6 +2999,8 @@ interface AttestationTreeLatestParams {
|
|
|
2969
2999
|
|
|
2970
3000
|
**AttestationVisibility** = `'public' | 'owner' | 'admin'`
|
|
2971
3001
|
|
|
3002
|
+
**AttestationModerationStatus** = `'approved' | 'pending' | 'rejected'`
|
|
3003
|
+
|
|
2972
3004
|
**AttestationAudience** = `'public' | 'owner' | 'admin'`
|
|
2973
3005
|
|
|
2974
3006
|
**AttestationGroupBy** = `'hour' | 'day' | 'week' | 'month'`
|
|
@@ -7564,6 +7596,9 @@ interface ProofGrant {
|
|
|
7564
7596
|
proofId: string
|
|
7565
7597
|
productId?: string | null
|
|
7566
7598
|
scope: GrantScope[]
|
|
7599
|
+
* `contribute` grants only: when true, records/attestations added under this
|
|
7600
|
+
* grant land `pending` (owner-only) until the owner approves them.
|
|
7601
|
+
moderate?: boolean
|
|
7567
7602
|
audience: GrantAudience
|
|
7568
7603
|
createdBy: string
|
|
7569
7604
|
expiresAt?: string | null
|
|
@@ -7582,6 +7617,11 @@ interface CreateGrantOptions {
|
|
|
7582
7617
|
scope: GrantScope[]
|
|
7583
7618
|
audience?: GrantAudience
|
|
7584
7619
|
expiresAt?: Date | string
|
|
7620
|
+
* Only meaningful with the `contribute` scope: hold contributions made under
|
|
7621
|
+
* this grant for owner review (they start `pending` and are owner-only until
|
|
7622
|
+
* approved). Ignored for other scopes. Defaults to `false` (contributions live
|
|
7623
|
+
* on write).
|
|
7624
|
+
moderate?: boolean
|
|
7585
7625
|
}
|
|
7586
7626
|
```
|
|
7587
7627
|
|
|
@@ -7679,7 +7719,7 @@ interface CancelTransferOptions {
|
|
|
7679
7719
|
|
|
7680
7720
|
**ProofFieldDef** = `ScopedFieldDef & { scope?: ProofFieldScope }`
|
|
7681
7721
|
|
|
7682
|
-
**GrantScope** = `'read' | 'comment' | 'admin' | 'verify_owner'`
|
|
7722
|
+
**GrantScope** = `'read' | 'comment' | 'contribute' | 'admin' | 'verify_owner'`
|
|
7683
7723
|
|
|
7684
7724
|
**RedeemGrantResult** = ``
|
|
7685
7725
|
|
|
@@ -9088,7 +9128,12 @@ List attestations for a subject (public). Records with `visibility='admin'` are
|
|
|
9088
9128
|
|
|
9089
9129
|
**publicCreate**(collectionId: string,
|
|
9090
9130
|
data: OwnerAttestationInput) → `Promise<CreateOwnerAttestationResponse>`
|
|
9091
|
-
Create
|
|
9131
|
+
Create a public attestation — the counterpart to the admin {@link create}. Authorised two ways, same call (the server decides from the request): 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`) adds an attestation to their own item; or 2. a holder of a `contribute`-scope grant adds one — call {@link setGrantToken} with the grant token first; for a public-link (anonymous) grant, pass `guestName` for attribution. Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped), `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped. If the contribute grant was issued with `moderate: true`, the returned record has `moderationStatus: 'pending'` — held to the owner until {@link moderate}. The record joins the same tamper-evident hash chain. POST /public/collection/:collectionId/attestations ```ts // Owner: await attestations.publicCreate('coll_123', { subjectType: 'proof', subjectId: 'proof_1', attestationType: 'condition-report', value: { grade: 'excellent' }, visibility: 'public', }) // Contributor on a shared link: setGrantToken(shareToken) await attestations.publicCreate('coll_123', { subjectType: 'proof', subjectId: 'proof_1', attestationType: 'photo', value: { url }, visibility: 'public', guestName: 'Sam', }) ```
|
|
9132
|
+
|
|
9133
|
+
**moderate**(collectionId: string,
|
|
9134
|
+
attestationId: string,
|
|
9135
|
+
input: ModerateAttestationInput) → `Promise<ModerateAttestationResponse>`
|
|
9136
|
+
Moderate a contributed attestation (proof OWNER by identity, or collection admin). `'approve'` releases it to its declared visibility; `'reject'` keeps it author + admin only. Only the `moderationStatus` changes — the hashed fact and its chain are untouched. Find pending items with {@link publicList} + `moderationStatus: 'pending'`. POST /public/collection/:collectionId/attestations/:attestationId/moderate ```ts await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' }) ```
|
|
9092
9137
|
|
|
9093
9138
|
**publicSummary**(collectionId: string,
|
|
9094
9139
|
params: AttestationSummaryParams) → `Promise<PublicAttestationSummaryResponse>`
|
|
@@ -166,6 +166,25 @@ When ownership is confirmed the request is served at `audience='owner'`, which i
|
|
|
166
166
|
- `visibility` is clamped to `'public' | 'owner'` (never `'admin'`), defaulting to `'owner'`;
|
|
167
167
|
- `authorId` is forced to the caller and `metadata.authorType = 'owner'` is stamped.
|
|
168
168
|
|
|
169
|
+
**Contribute write (grant)** — the same endpoint also accepts a holder of a
|
|
170
|
+
`contribute`-scope share grant (set the token with `setGrantToken` first), so a
|
|
171
|
+
third party can add an attestation without owning the proof. Named grant →
|
|
172
|
+
attributed to the signed-in uid; public-link grant → anonymous, with `guestName`
|
|
173
|
+
for attribution; `grantId` is stamped either way. If the grant was issued with
|
|
174
|
+
`moderate: true`, the record is created with `moderationStatus: 'pending'` and is
|
|
175
|
+
held to the owner until approved.
|
|
176
|
+
|
|
177
|
+
**Moderation** — `moderationStatus` (`approved` | `pending` | `rejected`) is a gate
|
|
178
|
+
**orthogonal to `visibility`** and excluded from the hash chain. A `pending` record
|
|
179
|
+
is returned only to its author and to owner/admin audiences — never to the public,
|
|
180
|
+
whatever its target visibility — and is excluded from public analytics. The owner
|
|
181
|
+
(identity) or a collection admin resolves it with `attestations.moderate(collectionId,
|
|
182
|
+
attestationId, { decision: 'approve' | 'reject' })`; approve releases it to its
|
|
183
|
+
declared visibility, reject keeps it author+admin-only. The review queue is
|
|
184
|
+
`attestations.publicList(..., { moderationStatus: 'pending' })`. Flipping the status
|
|
185
|
+
leaves the hashed fact and its chain untouched. See
|
|
186
|
+
[Proof Share Grants → Contribute access](proof-share-grants.md#contribute-access--let-someone-add-to-a-proof).
|
|
187
|
+
|
|
169
188
|
The record joins the same append-only, hash-chained log as business/system writes. Business writes (any zone/visibility) remain admin-only.
|
|
170
189
|
|
|
171
190
|
### Visibility vs audience
|
|
@@ -22,11 +22,12 @@ re-checks the grant **server-side** against the database, so revocation is immed
|
|
|
22
22
|
|-------|--------------------|
|
|
23
23
|
| `read` | read owner-tier data on the proof (attestations, threads, records, cases) |
|
|
24
24
|
| `comment` | create threads/replies on the proof (guest comments) |
|
|
25
|
+
| `contribute` | add attestations / records to the proof for the life of the grant (temporary contribute access) — optionally held for owner review via `moderate` |
|
|
25
26
|
| `admin` | read owner-tier data (reserved for elevated share cases; never exposes the platform admin zone) |
|
|
26
27
|
| `verify_owner` | redeem a shareable ownership **assertion** (not the account) |
|
|
27
28
|
|
|
28
29
|
A grant can carry several scopes, e.g. `['read', 'comment']` for a shareable,
|
|
29
|
-
commentable album.
|
|
30
|
+
commentable album, or `['read', 'contribute']` to let someone add photos.
|
|
30
31
|
|
|
31
32
|
### Security & lifecycle
|
|
32
33
|
|
|
@@ -124,6 +125,65 @@ Other grant holders (and the owner) see these comments because a `read` grant re
|
|
|
124
125
|
|
|
125
126
|
---
|
|
126
127
|
|
|
128
|
+
## Contribute access — let someone add to a proof
|
|
129
|
+
|
|
130
|
+
A `contribute` scope grant is **temporary write access**: the bearer can add
|
|
131
|
+
attestations (a photo, a video reference, a check-in) and app records to the proof
|
|
132
|
+
for the life of the grant — no account or proof claim required. It's the successor
|
|
133
|
+
to ad-hoc "claim windows": properly time-boxed (`expiresAt`), revocable, and voided
|
|
134
|
+
on ownership transfer like every other grant.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Owner issues a contribute grant that expires in 48h and holds contributions
|
|
138
|
+
// for review before they go public.
|
|
139
|
+
const grant = await proof.createGrant(collectionId, productId, proofId, {
|
|
140
|
+
scope: ['read', 'contribute'],
|
|
141
|
+
moderate: true, // contributions land 'pending'
|
|
142
|
+
expiresAt: new Date(Date.now() + 48 * 60 * 60 * 1000),
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Contributor (on the shared link) adds a photo attestation.
|
|
146
|
+
setGrantToken(shareToken)
|
|
147
|
+
await attestation.publicCreate(collectionId, {
|
|
148
|
+
subjectType: 'proof', subjectId: proofId,
|
|
149
|
+
attestationType: 'photo',
|
|
150
|
+
value: { url: 'https://…/photo.jpg' },
|
|
151
|
+
visibility: 'public',
|
|
152
|
+
guestName: 'Sam', // attribution for a public-link bearer
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Moderation — the two-step review
|
|
157
|
+
|
|
158
|
+
When the grant is issued with `moderate: true`, each contribution is created with
|
|
159
|
+
`moderationStatus: 'pending'` and is **held to the owner**: it is returned only to
|
|
160
|
+
its own author and to owner/admin audiences — never to the public, whatever its
|
|
161
|
+
target `visibility`. The owner reviews and approves (or rejects) it:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Owner lists what's waiting (owner-tier read = identity or an owner session):
|
|
165
|
+
const { attestations } = await attestation.publicList(collectionId, {
|
|
166
|
+
subjectType: 'proof', subjectId: proofId,
|
|
167
|
+
moderationStatus: 'pending',
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Approve → the record goes live at its declared visibility; reject → author+admin only.
|
|
171
|
+
await attestation.moderate(collectionId, attestations[0].id, { decision: 'approve' })
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The moderation gate is enforced **server-side** — a front end may badge a pending
|
|
175
|
+
item, but it is the server that withholds it from other viewers. Only
|
|
176
|
+
`moderationStatus` changes on approve/reject; the hashed fact and its chain are
|
|
177
|
+
untouched. Contributions under a grant issued with `moderate: false` (the default)
|
|
178
|
+
go live immediately.
|
|
179
|
+
|
|
180
|
+
> **Media note.** A contributed photo/video is best modelled as an attestation
|
|
181
|
+
> (`attestationType: 'photo'`, `value: { url }`) so it rides this moderation model.
|
|
182
|
+
> The legacy per-proof asset upload path is unchanged and is not grant- or
|
|
183
|
+
> moderation-aware.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
127
187
|
## Proof of ownership — `verify_owner`
|
|
128
188
|
|
|
129
189
|
Ownership itself is **not** a grant — it is `proof.ownerId`, established via the
|
|
@@ -154,6 +214,7 @@ granted proof (and only that proof):
|
|
|
154
214
|
- **Attestations** — `attestation.publicList({ subjectType: 'proof', subjectId })`
|
|
155
215
|
- **Threads / Records / Cases** — `app.threads.list`, `app.records.*`, `app.cases.list`, and the single-item GETs, filtered to the granted proof
|
|
156
216
|
- **Thread creation / replies** — with a `comment` scope grant (see below)
|
|
217
|
+
- **Attestation / record creation** — with a `contribute` scope grant (see [Contribute access](#contribute-access--let-someone-add-to-a-proof))
|
|
157
218
|
|
|
158
219
|
The token never exposes the platform `admin` zone, and only reveals `owner`-visibility
|
|
159
220
|
rows for the granted `proofId`.
|
|
@@ -175,14 +236,24 @@ at `sites/{collectionId}/apps/{appId}` — a `grant` branch alongside
|
|
|
175
236
|
"requireScope": "comment",
|
|
176
237
|
"enforce": { "visibility": "owner", "status": "open" }
|
|
177
238
|
}
|
|
239
|
+
},
|
|
240
|
+
"records": {
|
|
241
|
+
"grant": {
|
|
242
|
+
"allow": true,
|
|
243
|
+
"requireScope": "contribute",
|
|
244
|
+
"enforce": { "visibility": "owner" } // held to the owner until promoted
|
|
245
|
+
}
|
|
178
246
|
}
|
|
179
247
|
}
|
|
180
248
|
}
|
|
181
249
|
```
|
|
182
250
|
|
|
183
|
-
This enables grant-scoped commenting **without** opening up
|
|
184
|
-
`enforce.visibility: "owner"` keeps
|
|
185
|
-
owner and other grant holders, not the wider
|
|
251
|
+
This enables grant-scoped commenting and contribution **without** opening up
|
|
252
|
+
anonymous creation. The `enforce.visibility: "owner"` keeps the created object
|
|
253
|
+
private to the proof (visible to the owner and other grant holders, not the wider
|
|
254
|
+
public). Attestation contribution is gated the same way, by the `contribute` scope;
|
|
255
|
+
whether those contributions are held for review is set per-grant with `moderate`,
|
|
256
|
+
not in app config.
|
|
186
257
|
|
|
187
258
|
---
|
|
188
259
|
|
|
@@ -200,12 +271,13 @@ namespace proof {
|
|
|
200
271
|
function setGrantToken(token: string | undefined): void
|
|
201
272
|
function getGrantToken(): string | undefined
|
|
202
273
|
|
|
203
|
-
type GrantScope = 'read' | 'comment' | 'admin' | 'verify_owner'
|
|
274
|
+
type GrantScope = 'read' | 'comment' | 'contribute' | 'admin' | 'verify_owner'
|
|
204
275
|
|
|
205
276
|
interface CreateGrantOptions {
|
|
206
277
|
scope: GrantScope[] // at least one
|
|
207
278
|
audience?: { kind: 'public_link' } | { kind: 'named'; email?: string; userId?: string }
|
|
208
279
|
expiresAt?: Date | string
|
|
280
|
+
moderate?: boolean // 'contribute' only — hold for review
|
|
209
281
|
}
|
|
210
282
|
|
|
211
283
|
interface RedeemGrantOptions { guestName?: string }
|
|
@@ -219,6 +291,7 @@ interface ProofGrant {
|
|
|
219
291
|
proofId: string
|
|
220
292
|
productId?: string | null
|
|
221
293
|
scope: GrantScope[]
|
|
294
|
+
moderate?: boolean // 'contribute' grants: contributions held for review when true
|
|
222
295
|
audience: { kind: 'public_link' | 'named'; email?: string; userId?: string }
|
|
223
296
|
createdBy: string
|
|
224
297
|
expiresAt?: string | null
|
package/dist/openapi.yaml
CHANGED
|
@@ -1261,6 +1261,11 @@ paths:
|
|
|
1261
1261
|
required: false
|
|
1262
1262
|
schema:
|
|
1263
1263
|
type: string
|
|
1264
|
+
- name: moderationStatus
|
|
1265
|
+
in: query
|
|
1266
|
+
required: false
|
|
1267
|
+
schema:
|
|
1268
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
1264
1269
|
- name: recordedAfter
|
|
1265
1270
|
in: query
|
|
1266
1271
|
required: false
|
|
@@ -10237,6 +10242,11 @@ paths:
|
|
|
10237
10242
|
required: false
|
|
10238
10243
|
schema:
|
|
10239
10244
|
type: string
|
|
10245
|
+
- name: moderationStatus
|
|
10246
|
+
in: query
|
|
10247
|
+
required: false
|
|
10248
|
+
schema:
|
|
10249
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
10240
10250
|
- name: recordedAfter
|
|
10241
10251
|
in: query
|
|
10242
10252
|
required: false
|
|
@@ -10273,7 +10283,7 @@ paths:
|
|
|
10273
10283
|
post:
|
|
10274
10284
|
tags:
|
|
10275
10285
|
- attestations
|
|
10276
|
-
summary:
|
|
10286
|
+
summary: attestations.publicCreate
|
|
10277
10287
|
operationId: attestations_publicCreate
|
|
10278
10288
|
security: []
|
|
10279
10289
|
parameters:
|
|
@@ -10505,6 +10515,43 @@ paths:
|
|
|
10505
10515
|
description: Unauthorized
|
|
10506
10516
|
404:
|
|
10507
10517
|
description: Not found
|
|
10518
|
+
/public/collection/{collectionId}/attestations/{attestationId}/moderate:
|
|
10519
|
+
post:
|
|
10520
|
+
tags:
|
|
10521
|
+
- attestations
|
|
10522
|
+
summary: Moderate a contributed attestation (proof OWNER by identity, or collection admin).
|
|
10523
|
+
operationId: attestations_moderate
|
|
10524
|
+
security: []
|
|
10525
|
+
parameters:
|
|
10526
|
+
- name: collectionId
|
|
10527
|
+
in: path
|
|
10528
|
+
required: true
|
|
10529
|
+
schema:
|
|
10530
|
+
type: string
|
|
10531
|
+
- name: attestationId
|
|
10532
|
+
in: path
|
|
10533
|
+
required: true
|
|
10534
|
+
schema:
|
|
10535
|
+
type: string
|
|
10536
|
+
responses:
|
|
10537
|
+
200:
|
|
10538
|
+
description: Success
|
|
10539
|
+
content:
|
|
10540
|
+
application/json:
|
|
10541
|
+
schema:
|
|
10542
|
+
$ref: "#/components/schemas/ModerateAttestationResponse"
|
|
10543
|
+
400:
|
|
10544
|
+
description: Bad request
|
|
10545
|
+
401:
|
|
10546
|
+
description: Unauthorized
|
|
10547
|
+
404:
|
|
10548
|
+
description: Not found
|
|
10549
|
+
requestBody:
|
|
10550
|
+
required: true
|
|
10551
|
+
content:
|
|
10552
|
+
application/json:
|
|
10553
|
+
schema:
|
|
10554
|
+
$ref: "#/components/schemas/ModerateAttestationInput"
|
|
10508
10555
|
/public/collection/{collectionId}/comm/email/register:
|
|
10509
10556
|
post:
|
|
10510
10557
|
tags:
|
|
@@ -11115,6 +11162,11 @@ paths:
|
|
|
11115
11162
|
required: false
|
|
11116
11163
|
schema:
|
|
11117
11164
|
type: string
|
|
11165
|
+
- name: moderationStatus
|
|
11166
|
+
in: query
|
|
11167
|
+
required: false
|
|
11168
|
+
schema:
|
|
11169
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
11118
11170
|
- name: recordedAfter
|
|
11119
11171
|
in: query
|
|
11120
11172
|
required: false
|
|
@@ -18720,6 +18772,10 @@ components:
|
|
|
18720
18772
|
type: string
|
|
18721
18773
|
authorId:
|
|
18722
18774
|
type: string
|
|
18775
|
+
grantId:
|
|
18776
|
+
type: string
|
|
18777
|
+
moderationStatus:
|
|
18778
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
18723
18779
|
metadata:
|
|
18724
18780
|
type: object
|
|
18725
18781
|
additionalProperties: true
|
|
@@ -18842,10 +18898,29 @@ components:
|
|
|
18842
18898
|
metadata:
|
|
18843
18899
|
type: object
|
|
18844
18900
|
additionalProperties: true
|
|
18901
|
+
guestName:
|
|
18902
|
+
type: string
|
|
18845
18903
|
required:
|
|
18846
18904
|
- subjectType
|
|
18847
18905
|
- subjectId
|
|
18848
18906
|
- attestationType
|
|
18907
|
+
ModerateAttestationInput:
|
|
18908
|
+
type: object
|
|
18909
|
+
properties:
|
|
18910
|
+
decision:
|
|
18911
|
+
type: string
|
|
18912
|
+
enum:
|
|
18913
|
+
- approve
|
|
18914
|
+
- reject
|
|
18915
|
+
required:
|
|
18916
|
+
- decision
|
|
18917
|
+
ModerateAttestationResponse:
|
|
18918
|
+
type: object
|
|
18919
|
+
properties:
|
|
18920
|
+
attestation:
|
|
18921
|
+
$ref: "#/components/schemas/Attestation"
|
|
18922
|
+
required:
|
|
18923
|
+
- attestation
|
|
18849
18924
|
ListAttestationsResponse:
|
|
18850
18925
|
type: object
|
|
18851
18926
|
properties:
|
|
@@ -18985,6 +19060,8 @@ components:
|
|
|
18985
19060
|
type: string
|
|
18986
19061
|
attestationType:
|
|
18987
19062
|
type: string
|
|
19063
|
+
moderationStatus:
|
|
19064
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
18988
19065
|
recordedAfter:
|
|
18989
19066
|
type: string
|
|
18990
19067
|
recordedBefore:
|
|
@@ -25832,6 +25909,8 @@ components:
|
|
|
25832
25909
|
type: array
|
|
25833
25910
|
items:
|
|
25834
25911
|
$ref: "#/components/schemas/GrantScope"
|
|
25912
|
+
moderate:
|
|
25913
|
+
type: boolean
|
|
25835
25914
|
audience:
|
|
25836
25915
|
$ref: "#/components/schemas/GrantAudience"
|
|
25837
25916
|
createdBy:
|
|
@@ -25872,6 +25951,8 @@ components:
|
|
|
25872
25951
|
expiresAt:
|
|
25873
25952
|
type: object
|
|
25874
25953
|
additionalProperties: true
|
|
25954
|
+
moderate:
|
|
25955
|
+
type: boolean
|
|
25875
25956
|
required:
|
|
25876
25957
|
- scope
|
|
25877
25958
|
RedeemGrantOptions:
|
|
@@ -17,6 +17,14 @@ export type AttestationSubjectType = 'container' | 'proof' | 'product' | 'tag' |
|
|
|
17
17
|
* - `'admin'` — visible to admin callers only
|
|
18
18
|
*/
|
|
19
19
|
export type AttestationVisibility = 'public' | 'owner' | 'admin';
|
|
20
|
+
/**
|
|
21
|
+
* Moderation state of an attestation (separate from {@link AttestationVisibility}).
|
|
22
|
+
* - `'approved'` — live; follows its declared visibility (the default)
|
|
23
|
+
* - `'pending'` — awaiting owner review; returned only to its author and to
|
|
24
|
+
* owner/admin audiences, never to the public, whatever its target visibility
|
|
25
|
+
* - `'rejected'` — owner declined; author + admin only
|
|
26
|
+
*/
|
|
27
|
+
export type AttestationModerationStatus = 'approved' | 'pending' | 'rejected';
|
|
20
28
|
/**
|
|
21
29
|
* Resolved audience tier returned by public endpoints.
|
|
22
30
|
* Tells the client which data zones are populated in the response.
|
|
@@ -59,6 +67,19 @@ export interface Attestation {
|
|
|
59
67
|
source?: string;
|
|
60
68
|
/** User ID or service account that recorded the fact */
|
|
61
69
|
authorId?: string;
|
|
70
|
+
/**
|
|
71
|
+
* When authored under a `contribute` grant (rather than by identity), the id of
|
|
72
|
+
* the granting token — provenance for a contributed record. `null`/absent for
|
|
73
|
+
* owner/admin/identity writes.
|
|
74
|
+
*/
|
|
75
|
+
grantId?: string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Moderation gate, orthogonal to {@link visibility} and excluded from the hash
|
|
78
|
+
* chain. `'approved'` (default) is live; `'pending'` is held for owner review
|
|
79
|
+
* (visible only to its author and owner/admin audiences); `'rejected'` was
|
|
80
|
+
* declined. Contributions under a `moderate` grant start `'pending'`.
|
|
81
|
+
*/
|
|
82
|
+
moderationStatus?: AttestationModerationStatus;
|
|
62
83
|
/** Arbitrary extra metadata */
|
|
63
84
|
metadata?: Record<string, any>;
|
|
64
85
|
/** SHA-256 digest of this record (includes `prevHash`) */
|
|
@@ -129,10 +150,18 @@ export interface CreateAttestationInput {
|
|
|
129
150
|
metadata?: Record<string, any>;
|
|
130
151
|
}
|
|
131
152
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
153
|
+
* Public attestation write input, used by {@link attestations.publicCreate}.
|
|
154
|
+
*
|
|
155
|
+
* Authorised two ways, same input shape (the server decides from the request):
|
|
156
|
+
* 1. the proof OWNER (Firebase ID token) adds an attestation to their own item;
|
|
157
|
+
* 2. a holder of a `contribute`-scope grant adds one — set the grant token with
|
|
158
|
+
* `setGrantToken(token)` first; for a public-link (anonymous) grant, pass
|
|
159
|
+
* `guestName` for attribution.
|
|
160
|
+
*
|
|
161
|
+
* Restricted vs {@link CreateAttestationInput}: no `adminData` (business-only
|
|
162
|
+
* zone), `visibility` limited to `'public' | 'owner'`, and `authorId` / `grantId`
|
|
163
|
+
* are server-stamped (so they're omitted here). If the contribute grant was issued
|
|
164
|
+
* with `moderate: true`, the created record comes back `moderationStatus: 'pending'`.
|
|
136
165
|
*/
|
|
137
166
|
export interface OwnerAttestationInput {
|
|
138
167
|
subjectType: AttestationSubjectType;
|
|
@@ -146,6 +175,18 @@ export interface OwnerAttestationInput {
|
|
|
146
175
|
unit?: string;
|
|
147
176
|
source?: string;
|
|
148
177
|
metadata?: Record<string, any>;
|
|
178
|
+
/**
|
|
179
|
+
* Attribution for an anonymous (public-link) contribute-grant write. Ignored
|
|
180
|
+
* for owner writes and for named-grant writes (attributed to the signed-in uid).
|
|
181
|
+
*/
|
|
182
|
+
guestName?: string;
|
|
183
|
+
}
|
|
184
|
+
/** Owner/admin decision on a pending contributed attestation. */
|
|
185
|
+
export interface ModerateAttestationInput {
|
|
186
|
+
decision: 'approve' | 'reject';
|
|
187
|
+
}
|
|
188
|
+
export interface ModerateAttestationResponse {
|
|
189
|
+
attestation: Attestation;
|
|
149
190
|
}
|
|
150
191
|
export interface ListAttestationsResponse {
|
|
151
192
|
attestations: Attestation[];
|
|
@@ -197,6 +238,12 @@ export interface ListAttestationsParams {
|
|
|
197
238
|
/** Required */
|
|
198
239
|
subjectId: string;
|
|
199
240
|
attestationType?: string;
|
|
241
|
+
/**
|
|
242
|
+
* Filter by moderation state. Primarily for the owner review queue
|
|
243
|
+
* (`moderationStatus: 'pending'`). ANDs with the server's audience gate, so a
|
|
244
|
+
* public caller can never use it to widen access.
|
|
245
|
+
*/
|
|
246
|
+
moderationStatus?: AttestationModerationStatus;
|
|
200
247
|
/** ISO 8601 lower bound (inclusive) */
|
|
201
248
|
recordedAfter?: string;
|
|
202
249
|
/** ISO 8601 upper bound (inclusive) */
|
package/dist/types/proof.d.ts
CHANGED
|
@@ -164,8 +164,16 @@ export type ProofFieldDef = ScopedFieldDef & {
|
|
|
164
164
|
export interface ProofFieldsConfig {
|
|
165
165
|
fields: ProofFieldDef[];
|
|
166
166
|
}
|
|
167
|
-
/**
|
|
168
|
-
|
|
167
|
+
/**
|
|
168
|
+
* What a grant authorises the bearer to do on the proof.
|
|
169
|
+
* - `read` — see owner-tier data
|
|
170
|
+
* - `comment` — post app threads/comments
|
|
171
|
+
* - `contribute` — add records / attestations (temporary contribute access); pair
|
|
172
|
+
* with `moderate` on {@link CreateGrantOptions} to hold contributions for review
|
|
173
|
+
* - `admin` — owner-tier read across the proof
|
|
174
|
+
* - `verify_owner` — redeem an ownership assertion (never the account)
|
|
175
|
+
*/
|
|
176
|
+
export type GrantScope = 'read' | 'comment' | 'contribute' | 'admin' | 'verify_owner';
|
|
169
177
|
/** Who may redeem a grant. */
|
|
170
178
|
export interface GrantAudience {
|
|
171
179
|
kind: 'public_link' | 'named';
|
|
@@ -178,6 +186,11 @@ export interface ProofGrant {
|
|
|
178
186
|
proofId: string;
|
|
179
187
|
productId?: string | null;
|
|
180
188
|
scope: GrantScope[];
|
|
189
|
+
/**
|
|
190
|
+
* `contribute` grants only: when true, records/attestations added under this
|
|
191
|
+
* grant land `pending` (owner-only) until the owner approves them.
|
|
192
|
+
*/
|
|
193
|
+
moderate?: boolean;
|
|
181
194
|
audience: GrantAudience;
|
|
182
195
|
createdBy: string;
|
|
183
196
|
expiresAt?: string | null;
|
|
@@ -200,6 +213,13 @@ export interface CreateGrantOptions {
|
|
|
200
213
|
audience?: GrantAudience;
|
|
201
214
|
/** Optional expiry — a `Date` or ISO string. */
|
|
202
215
|
expiresAt?: Date | string;
|
|
216
|
+
/**
|
|
217
|
+
* Only meaningful with the `contribute` scope: hold contributions made under
|
|
218
|
+
* this grant for owner review (they start `pending` and are owner-only until
|
|
219
|
+
* approved). Ignored for other scopes. Defaults to `false` (contributions live
|
|
220
|
+
* on write).
|
|
221
|
+
*/
|
|
222
|
+
moderate?: boolean;
|
|
203
223
|
}
|
|
204
224
|
export interface RedeemGrantOptions {
|
|
205
225
|
/** Display name to stamp on guest activity when the redeemer is not signed in. */
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.7 | Generated: 2026-09-09T11:29:21.917Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -2745,6 +2745,15 @@ interface Attestation {
|
|
|
2745
2745
|
unit?: string
|
|
2746
2746
|
source?: string
|
|
2747
2747
|
authorId?: string
|
|
2748
|
+
* When authored under a `contribute` grant (rather than by identity), the id of
|
|
2749
|
+
* the granting token — provenance for a contributed record. `null`/absent for
|
|
2750
|
+
* owner/admin/identity writes.
|
|
2751
|
+
grantId?: string | null
|
|
2752
|
+
* Moderation gate, orthogonal to {@link visibility} and excluded from the hash
|
|
2753
|
+
* chain. `'approved'` (default) is live; `'pending'` is held for owner review
|
|
2754
|
+
* (visible only to its author and owner/admin audiences); `'rejected'` was
|
|
2755
|
+
* declined. Contributions under a `moderate` grant start `'pending'`.
|
|
2756
|
+
moderationStatus?: AttestationModerationStatus
|
|
2748
2757
|
metadata?: Record<string, any>
|
|
2749
2758
|
contentHash: string
|
|
2750
2759
|
prevHash?: string
|
|
@@ -2810,6 +2819,23 @@ interface OwnerAttestationInput {
|
|
|
2810
2819
|
unit?: string
|
|
2811
2820
|
source?: string
|
|
2812
2821
|
metadata?: Record<string, any>
|
|
2822
|
+
* Attribution for an anonymous (public-link) contribute-grant write. Ignored
|
|
2823
|
+
* for owner writes and for named-grant writes (attributed to the signed-in uid).
|
|
2824
|
+
guestName?: string
|
|
2825
|
+
}
|
|
2826
|
+
```
|
|
2827
|
+
|
|
2828
|
+
**ModerateAttestationInput** (interface)
|
|
2829
|
+
```typescript
|
|
2830
|
+
interface ModerateAttestationInput {
|
|
2831
|
+
decision: 'approve' | 'reject'
|
|
2832
|
+
}
|
|
2833
|
+
```
|
|
2834
|
+
|
|
2835
|
+
**ModerateAttestationResponse** (interface)
|
|
2836
|
+
```typescript
|
|
2837
|
+
interface ModerateAttestationResponse {
|
|
2838
|
+
attestation: Attestation
|
|
2813
2839
|
}
|
|
2814
2840
|
```
|
|
2815
2841
|
|
|
@@ -2905,6 +2931,10 @@ interface ListAttestationsParams {
|
|
|
2905
2931
|
subjectType: AttestationSubjectType
|
|
2906
2932
|
subjectId: string
|
|
2907
2933
|
attestationType?: string
|
|
2934
|
+
* Filter by moderation state. Primarily for the owner review queue
|
|
2935
|
+
* (`moderationStatus: 'pending'`). ANDs with the server's audience gate, so a
|
|
2936
|
+
* public caller can never use it to widen access.
|
|
2937
|
+
moderationStatus?: AttestationModerationStatus
|
|
2908
2938
|
recordedAfter?: string
|
|
2909
2939
|
recordedBefore?: string
|
|
2910
2940
|
limit?: number
|
|
@@ -2969,6 +2999,8 @@ interface AttestationTreeLatestParams {
|
|
|
2969
2999
|
|
|
2970
3000
|
**AttestationVisibility** = `'public' | 'owner' | 'admin'`
|
|
2971
3001
|
|
|
3002
|
+
**AttestationModerationStatus** = `'approved' | 'pending' | 'rejected'`
|
|
3003
|
+
|
|
2972
3004
|
**AttestationAudience** = `'public' | 'owner' | 'admin'`
|
|
2973
3005
|
|
|
2974
3006
|
**AttestationGroupBy** = `'hour' | 'day' | 'week' | 'month'`
|
|
@@ -7564,6 +7596,9 @@ interface ProofGrant {
|
|
|
7564
7596
|
proofId: string
|
|
7565
7597
|
productId?: string | null
|
|
7566
7598
|
scope: GrantScope[]
|
|
7599
|
+
* `contribute` grants only: when true, records/attestations added under this
|
|
7600
|
+
* grant land `pending` (owner-only) until the owner approves them.
|
|
7601
|
+
moderate?: boolean
|
|
7567
7602
|
audience: GrantAudience
|
|
7568
7603
|
createdBy: string
|
|
7569
7604
|
expiresAt?: string | null
|
|
@@ -7582,6 +7617,11 @@ interface CreateGrantOptions {
|
|
|
7582
7617
|
scope: GrantScope[]
|
|
7583
7618
|
audience?: GrantAudience
|
|
7584
7619
|
expiresAt?: Date | string
|
|
7620
|
+
* Only meaningful with the `contribute` scope: hold contributions made under
|
|
7621
|
+
* this grant for owner review (they start `pending` and are owner-only until
|
|
7622
|
+
* approved). Ignored for other scopes. Defaults to `false` (contributions live
|
|
7623
|
+
* on write).
|
|
7624
|
+
moderate?: boolean
|
|
7585
7625
|
}
|
|
7586
7626
|
```
|
|
7587
7627
|
|
|
@@ -7679,7 +7719,7 @@ interface CancelTransferOptions {
|
|
|
7679
7719
|
|
|
7680
7720
|
**ProofFieldDef** = `ScopedFieldDef & { scope?: ProofFieldScope }`
|
|
7681
7721
|
|
|
7682
|
-
**GrantScope** = `'read' | 'comment' | 'admin' | 'verify_owner'`
|
|
7722
|
+
**GrantScope** = `'read' | 'comment' | 'contribute' | 'admin' | 'verify_owner'`
|
|
7683
7723
|
|
|
7684
7724
|
**RedeemGrantResult** = ``
|
|
7685
7725
|
|
|
@@ -9088,7 +9128,12 @@ List attestations for a subject (public). Records with `visibility='admin'` are
|
|
|
9088
9128
|
|
|
9089
9129
|
**publicCreate**(collectionId: string,
|
|
9090
9130
|
data: OwnerAttestationInput) → `Promise<CreateOwnerAttestationResponse>`
|
|
9091
|
-
Create
|
|
9131
|
+
Create a public attestation — the counterpart to the admin {@link create}. Authorised two ways, same call (the server decides from the request): 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`) adds an attestation to their own item; or 2. a holder of a `contribute`-scope grant adds one — call {@link setGrantToken} with the grant token first; for a public-link (anonymous) grant, pass `guestName` for attribution. Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped), `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped. If the contribute grant was issued with `moderate: true`, the returned record has `moderationStatus: 'pending'` — held to the owner until {@link moderate}. The record joins the same tamper-evident hash chain. POST /public/collection/:collectionId/attestations ```ts // Owner: await attestations.publicCreate('coll_123', { subjectType: 'proof', subjectId: 'proof_1', attestationType: 'condition-report', value: { grade: 'excellent' }, visibility: 'public', }) // Contributor on a shared link: setGrantToken(shareToken) await attestations.publicCreate('coll_123', { subjectType: 'proof', subjectId: 'proof_1', attestationType: 'photo', value: { url }, visibility: 'public', guestName: 'Sam', }) ```
|
|
9132
|
+
|
|
9133
|
+
**moderate**(collectionId: string,
|
|
9134
|
+
attestationId: string,
|
|
9135
|
+
input: ModerateAttestationInput) → `Promise<ModerateAttestationResponse>`
|
|
9136
|
+
Moderate a contributed attestation (proof OWNER by identity, or collection admin). `'approve'` releases it to its declared visibility; `'reject'` keeps it author + admin only. Only the `moderationStatus` changes — the hashed fact and its chain are untouched. Find pending items with {@link publicList} + `moderationStatus: 'pending'`. POST /public/collection/:collectionId/attestations/:attestationId/moderate ```ts await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' }) ```
|
|
9092
9137
|
|
|
9093
9138
|
**publicSummary**(collectionId: string,
|
|
9094
9139
|
params: AttestationSummaryParams) → `Promise<PublicAttestationSummaryResponse>`
|
package/docs/attestations.md
CHANGED
|
@@ -166,6 +166,25 @@ When ownership is confirmed the request is served at `audience='owner'`, which i
|
|
|
166
166
|
- `visibility` is clamped to `'public' | 'owner'` (never `'admin'`), defaulting to `'owner'`;
|
|
167
167
|
- `authorId` is forced to the caller and `metadata.authorType = 'owner'` is stamped.
|
|
168
168
|
|
|
169
|
+
**Contribute write (grant)** — the same endpoint also accepts a holder of a
|
|
170
|
+
`contribute`-scope share grant (set the token with `setGrantToken` first), so a
|
|
171
|
+
third party can add an attestation without owning the proof. Named grant →
|
|
172
|
+
attributed to the signed-in uid; public-link grant → anonymous, with `guestName`
|
|
173
|
+
for attribution; `grantId` is stamped either way. If the grant was issued with
|
|
174
|
+
`moderate: true`, the record is created with `moderationStatus: 'pending'` and is
|
|
175
|
+
held to the owner until approved.
|
|
176
|
+
|
|
177
|
+
**Moderation** — `moderationStatus` (`approved` | `pending` | `rejected`) is a gate
|
|
178
|
+
**orthogonal to `visibility`** and excluded from the hash chain. A `pending` record
|
|
179
|
+
is returned only to its author and to owner/admin audiences — never to the public,
|
|
180
|
+
whatever its target visibility — and is excluded from public analytics. The owner
|
|
181
|
+
(identity) or a collection admin resolves it with `attestations.moderate(collectionId,
|
|
182
|
+
attestationId, { decision: 'approve' | 'reject' })`; approve releases it to its
|
|
183
|
+
declared visibility, reject keeps it author+admin-only. The review queue is
|
|
184
|
+
`attestations.publicList(..., { moderationStatus: 'pending' })`. Flipping the status
|
|
185
|
+
leaves the hashed fact and its chain untouched. See
|
|
186
|
+
[Proof Share Grants → Contribute access](proof-share-grants.md#contribute-access--let-someone-add-to-a-proof).
|
|
187
|
+
|
|
169
188
|
The record joins the same append-only, hash-chained log as business/system writes. Business writes (any zone/visibility) remain admin-only.
|
|
170
189
|
|
|
171
190
|
### Visibility vs audience
|
|
@@ -22,11 +22,12 @@ re-checks the grant **server-side** against the database, so revocation is immed
|
|
|
22
22
|
|-------|--------------------|
|
|
23
23
|
| `read` | read owner-tier data on the proof (attestations, threads, records, cases) |
|
|
24
24
|
| `comment` | create threads/replies on the proof (guest comments) |
|
|
25
|
+
| `contribute` | add attestations / records to the proof for the life of the grant (temporary contribute access) — optionally held for owner review via `moderate` |
|
|
25
26
|
| `admin` | read owner-tier data (reserved for elevated share cases; never exposes the platform admin zone) |
|
|
26
27
|
| `verify_owner` | redeem a shareable ownership **assertion** (not the account) |
|
|
27
28
|
|
|
28
29
|
A grant can carry several scopes, e.g. `['read', 'comment']` for a shareable,
|
|
29
|
-
commentable album.
|
|
30
|
+
commentable album, or `['read', 'contribute']` to let someone add photos.
|
|
30
31
|
|
|
31
32
|
### Security & lifecycle
|
|
32
33
|
|
|
@@ -124,6 +125,65 @@ Other grant holders (and the owner) see these comments because a `read` grant re
|
|
|
124
125
|
|
|
125
126
|
---
|
|
126
127
|
|
|
128
|
+
## Contribute access — let someone add to a proof
|
|
129
|
+
|
|
130
|
+
A `contribute` scope grant is **temporary write access**: the bearer can add
|
|
131
|
+
attestations (a photo, a video reference, a check-in) and app records to the proof
|
|
132
|
+
for the life of the grant — no account or proof claim required. It's the successor
|
|
133
|
+
to ad-hoc "claim windows": properly time-boxed (`expiresAt`), revocable, and voided
|
|
134
|
+
on ownership transfer like every other grant.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Owner issues a contribute grant that expires in 48h and holds contributions
|
|
138
|
+
// for review before they go public.
|
|
139
|
+
const grant = await proof.createGrant(collectionId, productId, proofId, {
|
|
140
|
+
scope: ['read', 'contribute'],
|
|
141
|
+
moderate: true, // contributions land 'pending'
|
|
142
|
+
expiresAt: new Date(Date.now() + 48 * 60 * 60 * 1000),
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Contributor (on the shared link) adds a photo attestation.
|
|
146
|
+
setGrantToken(shareToken)
|
|
147
|
+
await attestation.publicCreate(collectionId, {
|
|
148
|
+
subjectType: 'proof', subjectId: proofId,
|
|
149
|
+
attestationType: 'photo',
|
|
150
|
+
value: { url: 'https://…/photo.jpg' },
|
|
151
|
+
visibility: 'public',
|
|
152
|
+
guestName: 'Sam', // attribution for a public-link bearer
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Moderation — the two-step review
|
|
157
|
+
|
|
158
|
+
When the grant is issued with `moderate: true`, each contribution is created with
|
|
159
|
+
`moderationStatus: 'pending'` and is **held to the owner**: it is returned only to
|
|
160
|
+
its own author and to owner/admin audiences — never to the public, whatever its
|
|
161
|
+
target `visibility`. The owner reviews and approves (or rejects) it:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Owner lists what's waiting (owner-tier read = identity or an owner session):
|
|
165
|
+
const { attestations } = await attestation.publicList(collectionId, {
|
|
166
|
+
subjectType: 'proof', subjectId: proofId,
|
|
167
|
+
moderationStatus: 'pending',
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Approve → the record goes live at its declared visibility; reject → author+admin only.
|
|
171
|
+
await attestation.moderate(collectionId, attestations[0].id, { decision: 'approve' })
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The moderation gate is enforced **server-side** — a front end may badge a pending
|
|
175
|
+
item, but it is the server that withholds it from other viewers. Only
|
|
176
|
+
`moderationStatus` changes on approve/reject; the hashed fact and its chain are
|
|
177
|
+
untouched. Contributions under a grant issued with `moderate: false` (the default)
|
|
178
|
+
go live immediately.
|
|
179
|
+
|
|
180
|
+
> **Media note.** A contributed photo/video is best modelled as an attestation
|
|
181
|
+
> (`attestationType: 'photo'`, `value: { url }`) so it rides this moderation model.
|
|
182
|
+
> The legacy per-proof asset upload path is unchanged and is not grant- or
|
|
183
|
+
> moderation-aware.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
127
187
|
## Proof of ownership — `verify_owner`
|
|
128
188
|
|
|
129
189
|
Ownership itself is **not** a grant — it is `proof.ownerId`, established via the
|
|
@@ -154,6 +214,7 @@ granted proof (and only that proof):
|
|
|
154
214
|
- **Attestations** — `attestation.publicList({ subjectType: 'proof', subjectId })`
|
|
155
215
|
- **Threads / Records / Cases** — `app.threads.list`, `app.records.*`, `app.cases.list`, and the single-item GETs, filtered to the granted proof
|
|
156
216
|
- **Thread creation / replies** — with a `comment` scope grant (see below)
|
|
217
|
+
- **Attestation / record creation** — with a `contribute` scope grant (see [Contribute access](#contribute-access--let-someone-add-to-a-proof))
|
|
157
218
|
|
|
158
219
|
The token never exposes the platform `admin` zone, and only reveals `owner`-visibility
|
|
159
220
|
rows for the granted `proofId`.
|
|
@@ -175,14 +236,24 @@ at `sites/{collectionId}/apps/{appId}` — a `grant` branch alongside
|
|
|
175
236
|
"requireScope": "comment",
|
|
176
237
|
"enforce": { "visibility": "owner", "status": "open" }
|
|
177
238
|
}
|
|
239
|
+
},
|
|
240
|
+
"records": {
|
|
241
|
+
"grant": {
|
|
242
|
+
"allow": true,
|
|
243
|
+
"requireScope": "contribute",
|
|
244
|
+
"enforce": { "visibility": "owner" } // held to the owner until promoted
|
|
245
|
+
}
|
|
178
246
|
}
|
|
179
247
|
}
|
|
180
248
|
}
|
|
181
249
|
```
|
|
182
250
|
|
|
183
|
-
This enables grant-scoped commenting **without** opening up
|
|
184
|
-
`enforce.visibility: "owner"` keeps
|
|
185
|
-
owner and other grant holders, not the wider
|
|
251
|
+
This enables grant-scoped commenting and contribution **without** opening up
|
|
252
|
+
anonymous creation. The `enforce.visibility: "owner"` keeps the created object
|
|
253
|
+
private to the proof (visible to the owner and other grant holders, not the wider
|
|
254
|
+
public). Attestation contribution is gated the same way, by the `contribute` scope;
|
|
255
|
+
whether those contributions are held for review is set per-grant with `moderate`,
|
|
256
|
+
not in app config.
|
|
186
257
|
|
|
187
258
|
---
|
|
188
259
|
|
|
@@ -200,12 +271,13 @@ namespace proof {
|
|
|
200
271
|
function setGrantToken(token: string | undefined): void
|
|
201
272
|
function getGrantToken(): string | undefined
|
|
202
273
|
|
|
203
|
-
type GrantScope = 'read' | 'comment' | 'admin' | 'verify_owner'
|
|
274
|
+
type GrantScope = 'read' | 'comment' | 'contribute' | 'admin' | 'verify_owner'
|
|
204
275
|
|
|
205
276
|
interface CreateGrantOptions {
|
|
206
277
|
scope: GrantScope[] // at least one
|
|
207
278
|
audience?: { kind: 'public_link' } | { kind: 'named'; email?: string; userId?: string }
|
|
208
279
|
expiresAt?: Date | string
|
|
280
|
+
moderate?: boolean // 'contribute' only — hold for review
|
|
209
281
|
}
|
|
210
282
|
|
|
211
283
|
interface RedeemGrantOptions { guestName?: string }
|
|
@@ -219,6 +291,7 @@ interface ProofGrant {
|
|
|
219
291
|
proofId: string
|
|
220
292
|
productId?: string | null
|
|
221
293
|
scope: GrantScope[]
|
|
294
|
+
moderate?: boolean // 'contribute' grants: contributions held for review when true
|
|
222
295
|
audience: { kind: 'public_link' | 'named'; email?: string; userId?: string }
|
|
223
296
|
createdBy: string
|
|
224
297
|
expiresAt?: string | null
|
package/openapi.yaml
CHANGED
|
@@ -1261,6 +1261,11 @@ paths:
|
|
|
1261
1261
|
required: false
|
|
1262
1262
|
schema:
|
|
1263
1263
|
type: string
|
|
1264
|
+
- name: moderationStatus
|
|
1265
|
+
in: query
|
|
1266
|
+
required: false
|
|
1267
|
+
schema:
|
|
1268
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
1264
1269
|
- name: recordedAfter
|
|
1265
1270
|
in: query
|
|
1266
1271
|
required: false
|
|
@@ -10237,6 +10242,11 @@ paths:
|
|
|
10237
10242
|
required: false
|
|
10238
10243
|
schema:
|
|
10239
10244
|
type: string
|
|
10245
|
+
- name: moderationStatus
|
|
10246
|
+
in: query
|
|
10247
|
+
required: false
|
|
10248
|
+
schema:
|
|
10249
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
10240
10250
|
- name: recordedAfter
|
|
10241
10251
|
in: query
|
|
10242
10252
|
required: false
|
|
@@ -10273,7 +10283,7 @@ paths:
|
|
|
10273
10283
|
post:
|
|
10274
10284
|
tags:
|
|
10275
10285
|
- attestations
|
|
10276
|
-
summary:
|
|
10286
|
+
summary: attestations.publicCreate
|
|
10277
10287
|
operationId: attestations_publicCreate
|
|
10278
10288
|
security: []
|
|
10279
10289
|
parameters:
|
|
@@ -10505,6 +10515,43 @@ paths:
|
|
|
10505
10515
|
description: Unauthorized
|
|
10506
10516
|
404:
|
|
10507
10517
|
description: Not found
|
|
10518
|
+
/public/collection/{collectionId}/attestations/{attestationId}/moderate:
|
|
10519
|
+
post:
|
|
10520
|
+
tags:
|
|
10521
|
+
- attestations
|
|
10522
|
+
summary: Moderate a contributed attestation (proof OWNER by identity, or collection admin).
|
|
10523
|
+
operationId: attestations_moderate
|
|
10524
|
+
security: []
|
|
10525
|
+
parameters:
|
|
10526
|
+
- name: collectionId
|
|
10527
|
+
in: path
|
|
10528
|
+
required: true
|
|
10529
|
+
schema:
|
|
10530
|
+
type: string
|
|
10531
|
+
- name: attestationId
|
|
10532
|
+
in: path
|
|
10533
|
+
required: true
|
|
10534
|
+
schema:
|
|
10535
|
+
type: string
|
|
10536
|
+
responses:
|
|
10537
|
+
200:
|
|
10538
|
+
description: Success
|
|
10539
|
+
content:
|
|
10540
|
+
application/json:
|
|
10541
|
+
schema:
|
|
10542
|
+
$ref: "#/components/schemas/ModerateAttestationResponse"
|
|
10543
|
+
400:
|
|
10544
|
+
description: Bad request
|
|
10545
|
+
401:
|
|
10546
|
+
description: Unauthorized
|
|
10547
|
+
404:
|
|
10548
|
+
description: Not found
|
|
10549
|
+
requestBody:
|
|
10550
|
+
required: true
|
|
10551
|
+
content:
|
|
10552
|
+
application/json:
|
|
10553
|
+
schema:
|
|
10554
|
+
$ref: "#/components/schemas/ModerateAttestationInput"
|
|
10508
10555
|
/public/collection/{collectionId}/comm/email/register:
|
|
10509
10556
|
post:
|
|
10510
10557
|
tags:
|
|
@@ -11115,6 +11162,11 @@ paths:
|
|
|
11115
11162
|
required: false
|
|
11116
11163
|
schema:
|
|
11117
11164
|
type: string
|
|
11165
|
+
- name: moderationStatus
|
|
11166
|
+
in: query
|
|
11167
|
+
required: false
|
|
11168
|
+
schema:
|
|
11169
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
11118
11170
|
- name: recordedAfter
|
|
11119
11171
|
in: query
|
|
11120
11172
|
required: false
|
|
@@ -18720,6 +18772,10 @@ components:
|
|
|
18720
18772
|
type: string
|
|
18721
18773
|
authorId:
|
|
18722
18774
|
type: string
|
|
18775
|
+
grantId:
|
|
18776
|
+
type: string
|
|
18777
|
+
moderationStatus:
|
|
18778
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
18723
18779
|
metadata:
|
|
18724
18780
|
type: object
|
|
18725
18781
|
additionalProperties: true
|
|
@@ -18842,10 +18898,29 @@ components:
|
|
|
18842
18898
|
metadata:
|
|
18843
18899
|
type: object
|
|
18844
18900
|
additionalProperties: true
|
|
18901
|
+
guestName:
|
|
18902
|
+
type: string
|
|
18845
18903
|
required:
|
|
18846
18904
|
- subjectType
|
|
18847
18905
|
- subjectId
|
|
18848
18906
|
- attestationType
|
|
18907
|
+
ModerateAttestationInput:
|
|
18908
|
+
type: object
|
|
18909
|
+
properties:
|
|
18910
|
+
decision:
|
|
18911
|
+
type: string
|
|
18912
|
+
enum:
|
|
18913
|
+
- approve
|
|
18914
|
+
- reject
|
|
18915
|
+
required:
|
|
18916
|
+
- decision
|
|
18917
|
+
ModerateAttestationResponse:
|
|
18918
|
+
type: object
|
|
18919
|
+
properties:
|
|
18920
|
+
attestation:
|
|
18921
|
+
$ref: "#/components/schemas/Attestation"
|
|
18922
|
+
required:
|
|
18923
|
+
- attestation
|
|
18849
18924
|
ListAttestationsResponse:
|
|
18850
18925
|
type: object
|
|
18851
18926
|
properties:
|
|
@@ -18985,6 +19060,8 @@ components:
|
|
|
18985
19060
|
type: string
|
|
18986
19061
|
attestationType:
|
|
18987
19062
|
type: string
|
|
19063
|
+
moderationStatus:
|
|
19064
|
+
$ref: "#/components/schemas/AttestationModerationStatus"
|
|
18988
19065
|
recordedAfter:
|
|
18989
19066
|
type: string
|
|
18990
19067
|
recordedBefore:
|
|
@@ -25832,6 +25909,8 @@ components:
|
|
|
25832
25909
|
type: array
|
|
25833
25910
|
items:
|
|
25834
25911
|
$ref: "#/components/schemas/GrantScope"
|
|
25912
|
+
moderate:
|
|
25913
|
+
type: boolean
|
|
25835
25914
|
audience:
|
|
25836
25915
|
$ref: "#/components/schemas/GrantAudience"
|
|
25837
25916
|
createdBy:
|
|
@@ -25872,6 +25951,8 @@ components:
|
|
|
25872
25951
|
expiresAt:
|
|
25873
25952
|
type: object
|
|
25874
25953
|
additionalProperties: true
|
|
25954
|
+
moderate:
|
|
25955
|
+
type: boolean
|
|
25875
25956
|
required:
|
|
25876
25957
|
- scope
|
|
25877
25958
|
RedeemGrantOptions:
|