@proveanything/smartlinks 1.11.5 → 1.11.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/interactions.d.ts +8 -5
- package/dist/api/interactions.js +6 -4
- package/dist/docs/API_SUMMARY.md +40 -12
- package/dist/docs/app-objects.md +56 -0
- package/dist/docs/interactions.md +26 -3
- package/dist/openapi.yaml +58 -0
- package/dist/types/interaction.d.ts +15 -0
- package/dist/utils/anonId.d.ts +27 -0
- package/dist/utils/anonId.js +38 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/docs/API_SUMMARY.md +40 -12
- package/docs/app-objects.md +56 -0
- package/docs/interactions.md +26 -3
- package/openapi.yaml +58 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdminInteractionsCountsByOutcomeRequest, AdminInteractionsQueryRequest, AdminInteractionsAggregateRequest, AdminInteractionsAggregateResponse, AppendInteractionBody, UpdateInteractionBody, OutcomeCount, InteractionEventRow, PublicInteractionsCountsByOutcomeRequest, PublicInteractionsByUserRequest, CreateInteractionTypeBody, UpdateInteractionTypeBody, ListInteractionTypesQuery, InteractionTypeRecord, InteractionTypeList } from "../types/interaction";
|
|
1
|
+
import type { AdminInteractionsCountsByOutcomeRequest, AdminInteractionsQueryRequest, AdminInteractionsAggregateRequest, AdminInteractionsAggregateResponse, AppendInteractionBody, UpdateInteractionBody, OutcomeCount, InteractionEventRow, PublicInteractionsCountsByOutcomeRequest, PublicInteractionsByUserRequest, SubmitInteractionResponse, SubmitInteractionError, CreateInteractionTypeBody, UpdateInteractionTypeBody, ListInteractionTypesQuery, InteractionTypeRecord, InteractionTypeList } from "../types/interaction";
|
|
2
2
|
export declare namespace interactions {
|
|
3
3
|
/**
|
|
4
4
|
* POST /admin/collection/:collectionId/interactions/query
|
|
@@ -30,11 +30,14 @@ export declare namespace interactions {
|
|
|
30
30
|
success: true;
|
|
31
31
|
}>;
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* POST /api/v1/public/collection/:collectionId/interactions/submit
|
|
34
|
+
*
|
|
35
|
+
* Submits an interaction event from a public/client-side context.
|
|
36
|
+
* When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor
|
|
37
|
+
* `contactId` is required. Pass `anonId` inside `metadata` to enable
|
|
38
|
+
* device-level deduplication via `uniquePerAnonId`.
|
|
34
39
|
*/
|
|
35
|
-
function submitPublicEvent(collectionId: string, body: AppendInteractionBody): Promise<
|
|
36
|
-
success: true;
|
|
37
|
-
}>;
|
|
40
|
+
function submitPublicEvent(collectionId: string, body: AppendInteractionBody): Promise<SubmitInteractionResponse | SubmitInteractionError>;
|
|
38
41
|
function create(collectionId: string, body: CreateInteractionTypeBody): Promise<InteractionTypeRecord>;
|
|
39
42
|
function list(collectionId: string, query?: ListInteractionTypesQuery): Promise<InteractionTypeList>;
|
|
40
43
|
function get(collectionId: string, id: string): Promise<InteractionTypeRecord>;
|
package/dist/api/interactions.js
CHANGED
|
@@ -73,12 +73,14 @@ export var interactions;
|
|
|
73
73
|
}
|
|
74
74
|
interactions.updateEvent = updateEvent;
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
76
|
+
* POST /api/v1/public/collection/:collectionId/interactions/submit
|
|
77
|
+
*
|
|
78
|
+
* Submits an interaction event from a public/client-side context.
|
|
79
|
+
* When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor
|
|
80
|
+
* `contactId` is required. Pass `anonId` inside `metadata` to enable
|
|
81
|
+
* device-level deduplication via `uniquePerAnonId`.
|
|
77
82
|
*/
|
|
78
83
|
async function submitPublicEvent(collectionId, body) {
|
|
79
|
-
if (!body.userId && !body.contactId) {
|
|
80
|
-
throw new Error("AppendInteractionBody must include one of userId or contactId");
|
|
81
|
-
}
|
|
82
84
|
const path = `/public/collection/${encodeURIComponent(collectionId)}/interactions/submit`;
|
|
83
85
|
return post(path, body);
|
|
84
86
|
}
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.11.
|
|
3
|
+
Version: 1.11.7 | Generated: 2026-05-02T09:06:52.034Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -5153,6 +5153,10 @@ interface InteractionPermissions {
|
|
|
5153
5153
|
* Authenticated summary visibility (counts, aggregates) when user is signed in.
|
|
5154
5154
|
allowAuthenticatedSummary?: boolean
|
|
5155
5155
|
allowOwnRead?: boolean
|
|
5156
|
+
uniquePerAnonId?: boolean
|
|
5157
|
+
* Time window in seconds for `uniquePerAnonId` enforcement.
|
|
5158
|
+
* `0` or omitted means all-time deduplication.
|
|
5159
|
+
uniquePerAnonIdWindowSeconds?: number
|
|
5156
5160
|
}
|
|
5157
5161
|
```
|
|
5158
5162
|
|
|
@@ -5220,6 +5224,30 @@ interface ListInteractionTypesQuery {
|
|
|
5220
5224
|
}
|
|
5221
5225
|
```
|
|
5222
5226
|
|
|
5227
|
+
**SubmitInteractionResponse** (interface)
|
|
5228
|
+
```typescript
|
|
5229
|
+
interface SubmitInteractionResponse {
|
|
5230
|
+
success: true
|
|
5231
|
+
eventId: string
|
|
5232
|
+
}
|
|
5233
|
+
```
|
|
5234
|
+
|
|
5235
|
+
**SubmitInteractionError** (interface)
|
|
5236
|
+
```typescript
|
|
5237
|
+
interface SubmitInteractionError {
|
|
5238
|
+
error: 'FORBIDDEN'
|
|
5239
|
+
reason:
|
|
5240
|
+
| 'not_public'
|
|
5241
|
+
| 'auth_required'
|
|
5242
|
+
| 'duplicate'
|
|
5243
|
+
| 'duplicate_anon'
|
|
5244
|
+
| 'disabled'
|
|
5245
|
+
| 'before_start'
|
|
5246
|
+
| 'after_end'
|
|
5247
|
+
| 'origin_forbidden'
|
|
5248
|
+
}
|
|
5249
|
+
```
|
|
5250
|
+
|
|
5223
5251
|
### jobs
|
|
5224
5252
|
|
|
5225
5253
|
**Job** (interface)
|
|
@@ -8333,47 +8361,47 @@ POST /admin/collection/:collectionId/interactions/append Appends one interaction
|
|
|
8333
8361
|
POST /admin/collection/:collectionId/interactions/append Appends one interaction event.
|
|
8334
8362
|
|
|
8335
8363
|
**submitPublicEvent**(collectionId: string,
|
|
8336
|
-
body: AppendInteractionBody) → `Promise
|
|
8337
|
-
|
|
8364
|
+
body: AppendInteractionBody) → `Promise<SubmitInteractionResponse | SubmitInteractionError>`
|
|
8365
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8338
8366
|
|
|
8339
8367
|
**create**(collectionId: string,
|
|
8340
8368
|
body: CreateInteractionTypeBody) → `Promise<InteractionTypeRecord>`
|
|
8341
|
-
|
|
8369
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8342
8370
|
|
|
8343
8371
|
**list**(collectionId: string,
|
|
8344
8372
|
query: ListInteractionTypesQuery = {}) → `Promise<InteractionTypeList>`
|
|
8345
|
-
|
|
8373
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8346
8374
|
|
|
8347
8375
|
**get**(collectionId: string,
|
|
8348
8376
|
id: string) → `Promise<InteractionTypeRecord>`
|
|
8349
|
-
|
|
8377
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8350
8378
|
|
|
8351
8379
|
**update**(collectionId: string,
|
|
8352
8380
|
id: string,
|
|
8353
8381
|
patchBody: UpdateInteractionTypeBody) → `Promise<InteractionTypeRecord>`
|
|
8354
|
-
|
|
8382
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8355
8383
|
|
|
8356
8384
|
**remove**(collectionId: string,
|
|
8357
8385
|
id: string) → `Promise<void>`
|
|
8358
|
-
|
|
8386
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8359
8387
|
|
|
8360
8388
|
**publicCountsByOutcome**(collectionId: string,
|
|
8361
8389
|
body: PublicInteractionsCountsByOutcomeRequest,
|
|
8362
8390
|
authToken?: string) → `Promise<OutcomeCount[]>`
|
|
8363
|
-
|
|
8391
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8364
8392
|
|
|
8365
8393
|
**publicMyInteractions**(collectionId: string,
|
|
8366
8394
|
body: PublicInteractionsByUserRequest,
|
|
8367
8395
|
authToken?: string) → `Promise<InteractionEventRow[]>`
|
|
8368
|
-
|
|
8396
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8369
8397
|
|
|
8370
8398
|
**publicList**(collectionId: string,
|
|
8371
8399
|
query: ListInteractionTypesQuery = {}) → `Promise<InteractionTypeList>`
|
|
8372
|
-
|
|
8400
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8373
8401
|
|
|
8374
8402
|
**publicGet**(collectionId: string,
|
|
8375
8403
|
id: string) → `Promise<InteractionTypeRecord>`
|
|
8376
|
-
|
|
8404
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8377
8405
|
|
|
8378
8406
|
### jobs
|
|
8379
8407
|
|
package/dist/docs/app-objects.md
CHANGED
|
@@ -85,6 +85,7 @@ Zones are **automatically filtered** based on the caller's role:
|
|
|
85
85
|
### Zone Writing Rules
|
|
86
86
|
|
|
87
87
|
- **Non-admin callers** attempting to write to the `admin` zone are silently ignored
|
|
88
|
+
- **Authenticated record owners** can write to `data` and `owner` by default; individual keys can be restricted via the `ownerEdit` app config policy (see [Owner Edit Policy](#owner-edit-policy) below)
|
|
88
89
|
- **Public callers** can write to `data` and `owner` (if visibility allows)
|
|
89
90
|
- **Admins** can write to all three zones
|
|
90
91
|
|
|
@@ -1098,6 +1099,61 @@ The `enforce` values are **merged over** the caller's request body, so you can l
|
|
|
1098
1099
|
|
|
1099
1100
|
---
|
|
1100
1101
|
|
|
1102
|
+
## Owner Edit Policy
|
|
1103
|
+
|
|
1104
|
+
Gives per-zone, field-level control over what an **authenticated record owner** can update via `PATCH /api/v1/public/collection/:collectionId/app/:appId/records/:recordId`.
|
|
1105
|
+
|
|
1106
|
+
Set the policy in the same app config document used for `publicCreate` (stored at `sites/{collectionId}/apps/{appId}`):
|
|
1107
|
+
|
|
1108
|
+
```json
|
|
1109
|
+
{
|
|
1110
|
+
"ownerEdit": {
|
|
1111
|
+
"records": {
|
|
1112
|
+
"data": { "allow": ["paypalEmail"] },
|
|
1113
|
+
"owner": { "allow": ["paypalEmail", "paypalEmailUpdatedAt"] }
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
### Zone visibility and write access
|
|
1120
|
+
|
|
1121
|
+
| Zone | Who can read | Who can write (owner) |
|
|
1122
|
+
|---------|------------------------|----------------------------------------------------------|
|
|
1123
|
+
| `data` | public | Allow-listed keys only (if policy set); all keys if not |
|
|
1124
|
+
| `owner` | owner + admin | Allow-listed keys only (if policy set); all keys if not |
|
|
1125
|
+
| `admin` | admin | Never — admin zone is always immutable to owners |
|
|
1126
|
+
|
|
1127
|
+
### Allow-list semantics
|
|
1128
|
+
|
|
1129
|
+
| Config | Behaviour |
|
|
1130
|
+
|----------------------------|-------------------------------------------------------------------------------|
|
|
1131
|
+
| No `ownerEdit` key | Default-allow — both zones fully writable (no change to existing behaviour) |
|
|
1132
|
+
| `allow` array with keys | Only the listed keys are accepted from the PATCH body; the rest are silently ignored and their existing values preserved |
|
|
1133
|
+
| `allow: []` (empty array) | Zone is effectively read-only for the owner |
|
|
1134
|
+
|
|
1135
|
+
Accepted keys are **merged** onto the existing zone blob — you do not need to re-send unchanged values.
|
|
1136
|
+
|
|
1137
|
+
### Example: commission record with protected fields
|
|
1138
|
+
|
|
1139
|
+
An app that lets owners update their payout email but not their commission total:
|
|
1140
|
+
|
|
1141
|
+
```json
|
|
1142
|
+
{
|
|
1143
|
+
"ownerEdit": {
|
|
1144
|
+
"records": {
|
|
1145
|
+
"owner": { "allow": ["paypalEmail", "paypalEmailUpdatedAt"] }
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
A PATCH body of `{ "owner": { "paypalEmail": "x@y.com", "totalCommission": 99 } }` will update `paypalEmail` only. `totalCommission` is silently ignored and its existing value is preserved.
|
|
1152
|
+
|
|
1153
|
+
> **App design note:** If your app creates records with sensitive fields that owners should never modify (e.g. computed totals, server-assigned fields), add an `ownerEdit` policy from the start. It is significantly easier to relax restrictions later than to tighten them after data has been mutated.
|
|
1154
|
+
|
|
1155
|
+
---
|
|
1156
|
+
|
|
1101
1157
|
## Anonymous Edit Tokens
|
|
1102
1158
|
|
|
1103
1159
|
Enables an anonymous caller to amend a record they just created — without authentication — by presenting a short-lived secret token.
|
|
@@ -119,18 +119,37 @@ await SL.interactions.appendEvent(collectionId, {
|
|
|
119
119
|
|
|
120
120
|
### Public Event Submit
|
|
121
121
|
|
|
122
|
-
Use in client-side app code.
|
|
122
|
+
Use in client-side app code. Hits the public endpoint and respects interaction permissions (`allowPublicSubmit`, `allowAnonymousSubmit`, `requireAuth`, etc.).
|
|
123
123
|
|
|
124
124
|
```typescript
|
|
125
|
+
// Authenticated submission
|
|
125
126
|
await SL.interactions.submitPublicEvent(collectionId, {
|
|
126
127
|
appId: 'my-app',
|
|
127
128
|
interactionId: 'competition-entry',
|
|
128
129
|
outcome: 'entered',
|
|
129
|
-
|
|
130
|
+
contactId: currentUser.contactId,
|
|
130
131
|
metadata: { answer: 'Paris' },
|
|
131
132
|
});
|
|
133
|
+
|
|
134
|
+
// Anonymous submission (interaction must have allowAnonymousSubmit: true)
|
|
135
|
+
const response = await SL.interactions.submitPublicEvent(collectionId, {
|
|
136
|
+
appId: 'my-app',
|
|
137
|
+
interactionId: 'nps-score',
|
|
138
|
+
outcome: '9',
|
|
139
|
+
metadata: {
|
|
140
|
+
anonId: SL.utils.getAnonId(), // device-level dedup signal
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!response.success) {
|
|
145
|
+
if (response.reason === 'duplicate_anon') {
|
|
146
|
+
// this device has already submitted
|
|
147
|
+
}
|
|
148
|
+
}
|
|
132
149
|
```
|
|
133
150
|
|
|
151
|
+
> **Anonymous submissions** — when `allowAnonymousSubmit: true` is set on the interaction, neither `userId` nor `contactId` is required. Use `utils.getAnonId()` to generate a stable browser-local UUID and pass it as `metadata.anonId`; the server will enforce `uniquePerAnonId` if configured.
|
|
152
|
+
|
|
134
153
|
### Update an Existing Event
|
|
135
154
|
|
|
136
155
|
```typescript
|
|
@@ -237,6 +256,8 @@ Set on the interaction type definition via `permissions`:
|
|
|
237
256
|
| `uniquePerUser` | boolean | Prevent duplicate submissions per user |
|
|
238
257
|
| `uniquePerUserWindowSeconds` | number | Time window for uniqueness (e.g., `86400` = 1 day) |
|
|
239
258
|
| `uniqueOutcome` | string | Outcome tag to check for duplicates (e.g., `"submitted"`) |
|
|
259
|
+
| `uniquePerAnonId` | boolean | Reject a second submission that carries the same `anonId` in metadata |
|
|
260
|
+
| `uniquePerAnonIdWindowSeconds` | number | Time window for `uniquePerAnonId` enforcement; `0` or omitted = all-time |
|
|
240
261
|
| `allowPublicSummary` | boolean | Show counts/aggregates to unauthenticated users |
|
|
241
262
|
| `allowAuthenticatedSummary` | boolean | Show counts/aggregates to authenticated users |
|
|
242
263
|
| `allowOwnRead` | boolean | Let users read their own event history via public API |
|
|
@@ -263,8 +284,10 @@ When defining a journey trigger, reference the `interactionId` that should fire
|
|
|
263
284
|
|
|
264
285
|
```typescript
|
|
265
286
|
import type {
|
|
266
|
-
AppendInteractionBody, // Event body for appendEvent
|
|
287
|
+
AppendInteractionBody, // Event body for appendEvent and submitPublicEvent
|
|
267
288
|
UpdateInteractionBody, // Event body for updateEvent
|
|
289
|
+
SubmitInteractionResponse, // { success: true; eventId: string }
|
|
290
|
+
SubmitInteractionError, // { error: 'FORBIDDEN'; reason: string }
|
|
268
291
|
InteractionEventRow, // Raw event record returned by query()
|
|
269
292
|
OutcomeCount, // { outcome: string | null; count: number }
|
|
270
293
|
InteractionPermissions, // Full permissions config shape
|
package/dist/openapi.yaml
CHANGED
|
@@ -10355,6 +10355,40 @@ paths:
|
|
|
10355
10355
|
application/json:
|
|
10356
10356
|
schema:
|
|
10357
10357
|
$ref: "#/components/schemas/PublicInteractionsCountsByOutcomeRequest"
|
|
10358
|
+
/public/collection/{collectionId}/interactions/submit:
|
|
10359
|
+
post:
|
|
10360
|
+
tags:
|
|
10361
|
+
- interactions
|
|
10362
|
+
summary: "POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context."
|
|
10363
|
+
operationId: interactions_submitPublicEvent
|
|
10364
|
+
security: []
|
|
10365
|
+
parameters:
|
|
10366
|
+
- name: collectionId
|
|
10367
|
+
in: path
|
|
10368
|
+
required: true
|
|
10369
|
+
schema:
|
|
10370
|
+
type: string
|
|
10371
|
+
responses:
|
|
10372
|
+
200:
|
|
10373
|
+
description: Success
|
|
10374
|
+
content:
|
|
10375
|
+
application/json:
|
|
10376
|
+
schema:
|
|
10377
|
+
oneOf:
|
|
10378
|
+
- $ref: "#/components/schemas/SubmitInteractionResponse"
|
|
10379
|
+
- $ref: "#/components/schemas/SubmitInteractionError"
|
|
10380
|
+
400:
|
|
10381
|
+
description: Bad request
|
|
10382
|
+
401:
|
|
10383
|
+
description: Unauthorized
|
|
10384
|
+
404:
|
|
10385
|
+
description: Not found
|
|
10386
|
+
requestBody:
|
|
10387
|
+
required: true
|
|
10388
|
+
content:
|
|
10389
|
+
application/json:
|
|
10390
|
+
schema:
|
|
10391
|
+
$ref: "#/components/schemas/AppendInteractionBody"
|
|
10358
10392
|
/public/collection/{collectionId}/interactions/{id}:
|
|
10359
10393
|
get:
|
|
10360
10394
|
tags:
|
|
@@ -20187,6 +20221,10 @@ components:
|
|
|
20187
20221
|
type: boolean
|
|
20188
20222
|
allowOwnRead:
|
|
20189
20223
|
type: boolean
|
|
20224
|
+
uniquePerAnonId:
|
|
20225
|
+
type: boolean
|
|
20226
|
+
uniquePerAnonIdWindowSeconds:
|
|
20227
|
+
type: number
|
|
20190
20228
|
InteractionDisplay:
|
|
20191
20229
|
type: object
|
|
20192
20230
|
properties:
|
|
@@ -20275,6 +20313,26 @@ components:
|
|
|
20275
20313
|
type: number
|
|
20276
20314
|
offset:
|
|
20277
20315
|
type: number
|
|
20316
|
+
SubmitInteractionResponse:
|
|
20317
|
+
type: object
|
|
20318
|
+
properties:
|
|
20319
|
+
success:
|
|
20320
|
+
type: object
|
|
20321
|
+
additionalProperties: true
|
|
20322
|
+
eventId:
|
|
20323
|
+
type: string
|
|
20324
|
+
required:
|
|
20325
|
+
- success
|
|
20326
|
+
- eventId
|
|
20327
|
+
SubmitInteractionError:
|
|
20328
|
+
type: object
|
|
20329
|
+
properties:
|
|
20330
|
+
error:
|
|
20331
|
+
type: string
|
|
20332
|
+
enum:
|
|
20333
|
+
- FORBIDDEN
|
|
20334
|
+
required:
|
|
20335
|
+
- error
|
|
20278
20336
|
Job:
|
|
20279
20337
|
type: object
|
|
20280
20338
|
properties:
|
|
@@ -162,6 +162,13 @@ export interface InteractionPermissions {
|
|
|
162
162
|
allowAuthenticatedSummary?: boolean;
|
|
163
163
|
/** Allow an authenticated user to read their own interaction history via the public API. */
|
|
164
164
|
allowOwnRead?: boolean;
|
|
165
|
+
/** Reject a second submission that carries the same `anonId` in metadata. */
|
|
166
|
+
uniquePerAnonId?: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Time window in seconds for `uniquePerAnonId` enforcement.
|
|
169
|
+
* `0` or omitted means all-time deduplication.
|
|
170
|
+
*/
|
|
171
|
+
uniquePerAnonIdWindowSeconds?: number;
|
|
165
172
|
}
|
|
166
173
|
export interface InteractionDisplay {
|
|
167
174
|
title?: string;
|
|
@@ -203,3 +210,11 @@ export interface ListInteractionTypesQuery {
|
|
|
203
210
|
limit?: number;
|
|
204
211
|
offset?: number;
|
|
205
212
|
}
|
|
213
|
+
export interface SubmitInteractionResponse {
|
|
214
|
+
success: true;
|
|
215
|
+
eventId: string;
|
|
216
|
+
}
|
|
217
|
+
export interface SubmitInteractionError {
|
|
218
|
+
error: 'FORBIDDEN';
|
|
219
|
+
reason: 'not_public' | 'auth_required' | 'duplicate' | 'duplicate_anon' | 'disabled' | 'before_start' | 'after_end' | 'origin_forbidden';
|
|
220
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a stable anonymous device ID stored in `localStorage`.
|
|
3
|
+
*
|
|
4
|
+
* On first call a random UUID is generated and persisted. All subsequent calls
|
|
5
|
+
* from the same browser return the same value. Returns `null` during SSR or in
|
|
6
|
+
* any environment without `window` (safe to call universally).
|
|
7
|
+
*
|
|
8
|
+
* The value is scoped to the browser's `localStorage` and is cleared if the
|
|
9
|
+
* user clears site data. It does **not** create any contact or user record on
|
|
10
|
+
* the server.
|
|
11
|
+
*
|
|
12
|
+
* Pass the result in `metadata.anonId` when submitting interactions to enable
|
|
13
|
+
* device-level deduplication via `uniquePerAnonId`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { utils } from '@proveanything/smartlinks'
|
|
18
|
+
*
|
|
19
|
+
* const response = await interactions.submitPublicEvent(collectionId, {
|
|
20
|
+
* appId: 'my-app',
|
|
21
|
+
* interactionId: 'nps-score',
|
|
22
|
+
* outcome: '9',
|
|
23
|
+
* metadata: { anonId: utils.getAnonId() },
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function getAnonId(): string | null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/utils/anonId.ts
|
|
2
|
+
const ANON_ID_KEY = '_pa_anon_id';
|
|
3
|
+
/**
|
|
4
|
+
* Returns a stable anonymous device ID stored in `localStorage`.
|
|
5
|
+
*
|
|
6
|
+
* On first call a random UUID is generated and persisted. All subsequent calls
|
|
7
|
+
* from the same browser return the same value. Returns `null` during SSR or in
|
|
8
|
+
* any environment without `window` (safe to call universally).
|
|
9
|
+
*
|
|
10
|
+
* The value is scoped to the browser's `localStorage` and is cleared if the
|
|
11
|
+
* user clears site data. It does **not** create any contact or user record on
|
|
12
|
+
* the server.
|
|
13
|
+
*
|
|
14
|
+
* Pass the result in `metadata.anonId` when submitting interactions to enable
|
|
15
|
+
* device-level deduplication via `uniquePerAnonId`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { utils } from '@proveanything/smartlinks'
|
|
20
|
+
*
|
|
21
|
+
* const response = await interactions.submitPublicEvent(collectionId, {
|
|
22
|
+
* appId: 'my-app',
|
|
23
|
+
* interactionId: 'nps-score',
|
|
24
|
+
* outcome: '9',
|
|
25
|
+
* metadata: { anonId: utils.getAnonId() },
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function getAnonId() {
|
|
30
|
+
if (typeof window === 'undefined')
|
|
31
|
+
return null;
|
|
32
|
+
let id = window.localStorage.getItem(ANON_ID_KEY);
|
|
33
|
+
if (!id) {
|
|
34
|
+
id = crypto.randomUUID();
|
|
35
|
+
window.localStorage.setItem(ANON_ID_KEY, id);
|
|
36
|
+
}
|
|
37
|
+
return id;
|
|
38
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.11.
|
|
3
|
+
Version: 1.11.7 | Generated: 2026-05-02T09:06:52.034Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -5153,6 +5153,10 @@ interface InteractionPermissions {
|
|
|
5153
5153
|
* Authenticated summary visibility (counts, aggregates) when user is signed in.
|
|
5154
5154
|
allowAuthenticatedSummary?: boolean
|
|
5155
5155
|
allowOwnRead?: boolean
|
|
5156
|
+
uniquePerAnonId?: boolean
|
|
5157
|
+
* Time window in seconds for `uniquePerAnonId` enforcement.
|
|
5158
|
+
* `0` or omitted means all-time deduplication.
|
|
5159
|
+
uniquePerAnonIdWindowSeconds?: number
|
|
5156
5160
|
}
|
|
5157
5161
|
```
|
|
5158
5162
|
|
|
@@ -5220,6 +5224,30 @@ interface ListInteractionTypesQuery {
|
|
|
5220
5224
|
}
|
|
5221
5225
|
```
|
|
5222
5226
|
|
|
5227
|
+
**SubmitInteractionResponse** (interface)
|
|
5228
|
+
```typescript
|
|
5229
|
+
interface SubmitInteractionResponse {
|
|
5230
|
+
success: true
|
|
5231
|
+
eventId: string
|
|
5232
|
+
}
|
|
5233
|
+
```
|
|
5234
|
+
|
|
5235
|
+
**SubmitInteractionError** (interface)
|
|
5236
|
+
```typescript
|
|
5237
|
+
interface SubmitInteractionError {
|
|
5238
|
+
error: 'FORBIDDEN'
|
|
5239
|
+
reason:
|
|
5240
|
+
| 'not_public'
|
|
5241
|
+
| 'auth_required'
|
|
5242
|
+
| 'duplicate'
|
|
5243
|
+
| 'duplicate_anon'
|
|
5244
|
+
| 'disabled'
|
|
5245
|
+
| 'before_start'
|
|
5246
|
+
| 'after_end'
|
|
5247
|
+
| 'origin_forbidden'
|
|
5248
|
+
}
|
|
5249
|
+
```
|
|
5250
|
+
|
|
5223
5251
|
### jobs
|
|
5224
5252
|
|
|
5225
5253
|
**Job** (interface)
|
|
@@ -8333,47 +8361,47 @@ POST /admin/collection/:collectionId/interactions/append Appends one interaction
|
|
|
8333
8361
|
POST /admin/collection/:collectionId/interactions/append Appends one interaction event.
|
|
8334
8362
|
|
|
8335
8363
|
**submitPublicEvent**(collectionId: string,
|
|
8336
|
-
body: AppendInteractionBody) → `Promise
|
|
8337
|
-
|
|
8364
|
+
body: AppendInteractionBody) → `Promise<SubmitInteractionResponse | SubmitInteractionError>`
|
|
8365
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8338
8366
|
|
|
8339
8367
|
**create**(collectionId: string,
|
|
8340
8368
|
body: CreateInteractionTypeBody) → `Promise<InteractionTypeRecord>`
|
|
8341
|
-
|
|
8369
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8342
8370
|
|
|
8343
8371
|
**list**(collectionId: string,
|
|
8344
8372
|
query: ListInteractionTypesQuery = {}) → `Promise<InteractionTypeList>`
|
|
8345
|
-
|
|
8373
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8346
8374
|
|
|
8347
8375
|
**get**(collectionId: string,
|
|
8348
8376
|
id: string) → `Promise<InteractionTypeRecord>`
|
|
8349
|
-
|
|
8377
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8350
8378
|
|
|
8351
8379
|
**update**(collectionId: string,
|
|
8352
8380
|
id: string,
|
|
8353
8381
|
patchBody: UpdateInteractionTypeBody) → `Promise<InteractionTypeRecord>`
|
|
8354
|
-
|
|
8382
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8355
8383
|
|
|
8356
8384
|
**remove**(collectionId: string,
|
|
8357
8385
|
id: string) → `Promise<void>`
|
|
8358
|
-
|
|
8386
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8359
8387
|
|
|
8360
8388
|
**publicCountsByOutcome**(collectionId: string,
|
|
8361
8389
|
body: PublicInteractionsCountsByOutcomeRequest,
|
|
8362
8390
|
authToken?: string) → `Promise<OutcomeCount[]>`
|
|
8363
|
-
|
|
8391
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8364
8392
|
|
|
8365
8393
|
**publicMyInteractions**(collectionId: string,
|
|
8366
8394
|
body: PublicInteractionsByUserRequest,
|
|
8367
8395
|
authToken?: string) → `Promise<InteractionEventRow[]>`
|
|
8368
|
-
|
|
8396
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8369
8397
|
|
|
8370
8398
|
**publicList**(collectionId: string,
|
|
8371
8399
|
query: ListInteractionTypesQuery = {}) → `Promise<InteractionTypeList>`
|
|
8372
|
-
|
|
8400
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8373
8401
|
|
|
8374
8402
|
**publicGet**(collectionId: string,
|
|
8375
8403
|
id: string) → `Promise<InteractionTypeRecord>`
|
|
8376
|
-
|
|
8404
|
+
POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context. When the interaction has `allowAnonymousSubmit: true`, neither `userId` nor `contactId` is required. Pass `anonId` inside `metadata` to enable device-level deduplication via `uniquePerAnonId`.
|
|
8377
8405
|
|
|
8378
8406
|
### jobs
|
|
8379
8407
|
|
package/docs/app-objects.md
CHANGED
|
@@ -85,6 +85,7 @@ Zones are **automatically filtered** based on the caller's role:
|
|
|
85
85
|
### Zone Writing Rules
|
|
86
86
|
|
|
87
87
|
- **Non-admin callers** attempting to write to the `admin` zone are silently ignored
|
|
88
|
+
- **Authenticated record owners** can write to `data` and `owner` by default; individual keys can be restricted via the `ownerEdit` app config policy (see [Owner Edit Policy](#owner-edit-policy) below)
|
|
88
89
|
- **Public callers** can write to `data` and `owner` (if visibility allows)
|
|
89
90
|
- **Admins** can write to all three zones
|
|
90
91
|
|
|
@@ -1098,6 +1099,61 @@ The `enforce` values are **merged over** the caller's request body, so you can l
|
|
|
1098
1099
|
|
|
1099
1100
|
---
|
|
1100
1101
|
|
|
1102
|
+
## Owner Edit Policy
|
|
1103
|
+
|
|
1104
|
+
Gives per-zone, field-level control over what an **authenticated record owner** can update via `PATCH /api/v1/public/collection/:collectionId/app/:appId/records/:recordId`.
|
|
1105
|
+
|
|
1106
|
+
Set the policy in the same app config document used for `publicCreate` (stored at `sites/{collectionId}/apps/{appId}`):
|
|
1107
|
+
|
|
1108
|
+
```json
|
|
1109
|
+
{
|
|
1110
|
+
"ownerEdit": {
|
|
1111
|
+
"records": {
|
|
1112
|
+
"data": { "allow": ["paypalEmail"] },
|
|
1113
|
+
"owner": { "allow": ["paypalEmail", "paypalEmailUpdatedAt"] }
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
### Zone visibility and write access
|
|
1120
|
+
|
|
1121
|
+
| Zone | Who can read | Who can write (owner) |
|
|
1122
|
+
|---------|------------------------|----------------------------------------------------------|
|
|
1123
|
+
| `data` | public | Allow-listed keys only (if policy set); all keys if not |
|
|
1124
|
+
| `owner` | owner + admin | Allow-listed keys only (if policy set); all keys if not |
|
|
1125
|
+
| `admin` | admin | Never — admin zone is always immutable to owners |
|
|
1126
|
+
|
|
1127
|
+
### Allow-list semantics
|
|
1128
|
+
|
|
1129
|
+
| Config | Behaviour |
|
|
1130
|
+
|----------------------------|-------------------------------------------------------------------------------|
|
|
1131
|
+
| No `ownerEdit` key | Default-allow — both zones fully writable (no change to existing behaviour) |
|
|
1132
|
+
| `allow` array with keys | Only the listed keys are accepted from the PATCH body; the rest are silently ignored and their existing values preserved |
|
|
1133
|
+
| `allow: []` (empty array) | Zone is effectively read-only for the owner |
|
|
1134
|
+
|
|
1135
|
+
Accepted keys are **merged** onto the existing zone blob — you do not need to re-send unchanged values.
|
|
1136
|
+
|
|
1137
|
+
### Example: commission record with protected fields
|
|
1138
|
+
|
|
1139
|
+
An app that lets owners update their payout email but not their commission total:
|
|
1140
|
+
|
|
1141
|
+
```json
|
|
1142
|
+
{
|
|
1143
|
+
"ownerEdit": {
|
|
1144
|
+
"records": {
|
|
1145
|
+
"owner": { "allow": ["paypalEmail", "paypalEmailUpdatedAt"] }
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
A PATCH body of `{ "owner": { "paypalEmail": "x@y.com", "totalCommission": 99 } }` will update `paypalEmail` only. `totalCommission` is silently ignored and its existing value is preserved.
|
|
1152
|
+
|
|
1153
|
+
> **App design note:** If your app creates records with sensitive fields that owners should never modify (e.g. computed totals, server-assigned fields), add an `ownerEdit` policy from the start. It is significantly easier to relax restrictions later than to tighten them after data has been mutated.
|
|
1154
|
+
|
|
1155
|
+
---
|
|
1156
|
+
|
|
1101
1157
|
## Anonymous Edit Tokens
|
|
1102
1158
|
|
|
1103
1159
|
Enables an anonymous caller to amend a record they just created — without authentication — by presenting a short-lived secret token.
|
package/docs/interactions.md
CHANGED
|
@@ -119,18 +119,37 @@ await SL.interactions.appendEvent(collectionId, {
|
|
|
119
119
|
|
|
120
120
|
### Public Event Submit
|
|
121
121
|
|
|
122
|
-
Use in client-side app code.
|
|
122
|
+
Use in client-side app code. Hits the public endpoint and respects interaction permissions (`allowPublicSubmit`, `allowAnonymousSubmit`, `requireAuth`, etc.).
|
|
123
123
|
|
|
124
124
|
```typescript
|
|
125
|
+
// Authenticated submission
|
|
125
126
|
await SL.interactions.submitPublicEvent(collectionId, {
|
|
126
127
|
appId: 'my-app',
|
|
127
128
|
interactionId: 'competition-entry',
|
|
128
129
|
outcome: 'entered',
|
|
129
|
-
|
|
130
|
+
contactId: currentUser.contactId,
|
|
130
131
|
metadata: { answer: 'Paris' },
|
|
131
132
|
});
|
|
133
|
+
|
|
134
|
+
// Anonymous submission (interaction must have allowAnonymousSubmit: true)
|
|
135
|
+
const response = await SL.interactions.submitPublicEvent(collectionId, {
|
|
136
|
+
appId: 'my-app',
|
|
137
|
+
interactionId: 'nps-score',
|
|
138
|
+
outcome: '9',
|
|
139
|
+
metadata: {
|
|
140
|
+
anonId: SL.utils.getAnonId(), // device-level dedup signal
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!response.success) {
|
|
145
|
+
if (response.reason === 'duplicate_anon') {
|
|
146
|
+
// this device has already submitted
|
|
147
|
+
}
|
|
148
|
+
}
|
|
132
149
|
```
|
|
133
150
|
|
|
151
|
+
> **Anonymous submissions** — when `allowAnonymousSubmit: true` is set on the interaction, neither `userId` nor `contactId` is required. Use `utils.getAnonId()` to generate a stable browser-local UUID and pass it as `metadata.anonId`; the server will enforce `uniquePerAnonId` if configured.
|
|
152
|
+
|
|
134
153
|
### Update an Existing Event
|
|
135
154
|
|
|
136
155
|
```typescript
|
|
@@ -237,6 +256,8 @@ Set on the interaction type definition via `permissions`:
|
|
|
237
256
|
| `uniquePerUser` | boolean | Prevent duplicate submissions per user |
|
|
238
257
|
| `uniquePerUserWindowSeconds` | number | Time window for uniqueness (e.g., `86400` = 1 day) |
|
|
239
258
|
| `uniqueOutcome` | string | Outcome tag to check for duplicates (e.g., `"submitted"`) |
|
|
259
|
+
| `uniquePerAnonId` | boolean | Reject a second submission that carries the same `anonId` in metadata |
|
|
260
|
+
| `uniquePerAnonIdWindowSeconds` | number | Time window for `uniquePerAnonId` enforcement; `0` or omitted = all-time |
|
|
240
261
|
| `allowPublicSummary` | boolean | Show counts/aggregates to unauthenticated users |
|
|
241
262
|
| `allowAuthenticatedSummary` | boolean | Show counts/aggregates to authenticated users |
|
|
242
263
|
| `allowOwnRead` | boolean | Let users read their own event history via public API |
|
|
@@ -263,8 +284,10 @@ When defining a journey trigger, reference the `interactionId` that should fire
|
|
|
263
284
|
|
|
264
285
|
```typescript
|
|
265
286
|
import type {
|
|
266
|
-
AppendInteractionBody, // Event body for appendEvent
|
|
287
|
+
AppendInteractionBody, // Event body for appendEvent and submitPublicEvent
|
|
267
288
|
UpdateInteractionBody, // Event body for updateEvent
|
|
289
|
+
SubmitInteractionResponse, // { success: true; eventId: string }
|
|
290
|
+
SubmitInteractionError, // { error: 'FORBIDDEN'; reason: string }
|
|
268
291
|
InteractionEventRow, // Raw event record returned by query()
|
|
269
292
|
OutcomeCount, // { outcome: string | null; count: number }
|
|
270
293
|
InteractionPermissions, // Full permissions config shape
|
package/openapi.yaml
CHANGED
|
@@ -10355,6 +10355,40 @@ paths:
|
|
|
10355
10355
|
application/json:
|
|
10356
10356
|
schema:
|
|
10357
10357
|
$ref: "#/components/schemas/PublicInteractionsCountsByOutcomeRequest"
|
|
10358
|
+
/public/collection/{collectionId}/interactions/submit:
|
|
10359
|
+
post:
|
|
10360
|
+
tags:
|
|
10361
|
+
- interactions
|
|
10362
|
+
summary: "POST /api/v1/public/collection/:collectionId/interactions/submit Submits an interaction event from a public/client-side context."
|
|
10363
|
+
operationId: interactions_submitPublicEvent
|
|
10364
|
+
security: []
|
|
10365
|
+
parameters:
|
|
10366
|
+
- name: collectionId
|
|
10367
|
+
in: path
|
|
10368
|
+
required: true
|
|
10369
|
+
schema:
|
|
10370
|
+
type: string
|
|
10371
|
+
responses:
|
|
10372
|
+
200:
|
|
10373
|
+
description: Success
|
|
10374
|
+
content:
|
|
10375
|
+
application/json:
|
|
10376
|
+
schema:
|
|
10377
|
+
oneOf:
|
|
10378
|
+
- $ref: "#/components/schemas/SubmitInteractionResponse"
|
|
10379
|
+
- $ref: "#/components/schemas/SubmitInteractionError"
|
|
10380
|
+
400:
|
|
10381
|
+
description: Bad request
|
|
10382
|
+
401:
|
|
10383
|
+
description: Unauthorized
|
|
10384
|
+
404:
|
|
10385
|
+
description: Not found
|
|
10386
|
+
requestBody:
|
|
10387
|
+
required: true
|
|
10388
|
+
content:
|
|
10389
|
+
application/json:
|
|
10390
|
+
schema:
|
|
10391
|
+
$ref: "#/components/schemas/AppendInteractionBody"
|
|
10358
10392
|
/public/collection/{collectionId}/interactions/{id}:
|
|
10359
10393
|
get:
|
|
10360
10394
|
tags:
|
|
@@ -20187,6 +20221,10 @@ components:
|
|
|
20187
20221
|
type: boolean
|
|
20188
20222
|
allowOwnRead:
|
|
20189
20223
|
type: boolean
|
|
20224
|
+
uniquePerAnonId:
|
|
20225
|
+
type: boolean
|
|
20226
|
+
uniquePerAnonIdWindowSeconds:
|
|
20227
|
+
type: number
|
|
20190
20228
|
InteractionDisplay:
|
|
20191
20229
|
type: object
|
|
20192
20230
|
properties:
|
|
@@ -20275,6 +20313,26 @@ components:
|
|
|
20275
20313
|
type: number
|
|
20276
20314
|
offset:
|
|
20277
20315
|
type: number
|
|
20316
|
+
SubmitInteractionResponse:
|
|
20317
|
+
type: object
|
|
20318
|
+
properties:
|
|
20319
|
+
success:
|
|
20320
|
+
type: object
|
|
20321
|
+
additionalProperties: true
|
|
20322
|
+
eventId:
|
|
20323
|
+
type: string
|
|
20324
|
+
required:
|
|
20325
|
+
- success
|
|
20326
|
+
- eventId
|
|
20327
|
+
SubmitInteractionError:
|
|
20328
|
+
type: object
|
|
20329
|
+
properties:
|
|
20330
|
+
error:
|
|
20331
|
+
type: string
|
|
20332
|
+
enum:
|
|
20333
|
+
- FORBIDDEN
|
|
20334
|
+
required:
|
|
20335
|
+
- error
|
|
20278
20336
|
Job:
|
|
20279
20337
|
type: object
|
|
20280
20338
|
properties:
|