@proveanything/smartlinks 2.1.14 → 2.1.15
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/integrations.d.ts +28 -0
- package/dist/api/integrations.js +28 -0
- package/dist/docs/API_SUMMARY.md +37 -2
- package/dist/docs/connectors.md +56 -0
- package/dist/docs/integrations.md +8 -0
- package/dist/docs/server-functions.md +1 -0
- package/dist/openapi.yaml +47 -0
- package/dist/types/appManifest.d.ts +23 -0
- package/dist/types/integrations.d.ts +20 -0
- package/docs/API_SUMMARY.md +37 -2
- package/docs/connectors.md +56 -0
- package/docs/integrations.md +8 -0
- package/docs/server-functions.md +1 -0
- package/openapi.yaml +47 -0
- package/package.json +1 -1
|
@@ -105,4 +105,32 @@ export declare namespace integrations {
|
|
|
105
105
|
function runConnectorStep<T = unknown>(collectionId: string, appId: string, step: ConnectorConsoleStep, input?: Record<string, unknown> & {
|
|
106
106
|
flowId?: string;
|
|
107
107
|
}): Promise<T>;
|
|
108
|
+
/**
|
|
109
|
+
* Invoke a connector ACTION from the console (any visibility): redeem, enrol, adjust points, issue a
|
|
110
|
+
* coupon… The connector's function runs with its own credentials and is told who asked.
|
|
111
|
+
* POST /integrations/connectors/:appId/actions/:action { input }
|
|
112
|
+
*/
|
|
113
|
+
function runConnectorAction<T = unknown>(collectionId: string, appId: string, action: string, input?: Record<string, unknown>): Promise<T>;
|
|
114
|
+
/**
|
|
115
|
+
* PUBLIC (the Hub, a site): the connector actions the caller may invoke — those declared `public`,
|
|
116
|
+
* plus `member` ones when signed in.
|
|
117
|
+
* GET /public/collection/:collectionId/connectors/:appId/actions
|
|
118
|
+
*/
|
|
119
|
+
function listConnectorActions(collectionId: string, appId: string): Promise<{
|
|
120
|
+
actions: Array<{
|
|
121
|
+
name: string;
|
|
122
|
+
title: string;
|
|
123
|
+
description?: string;
|
|
124
|
+
visibility: 'public' | 'member' | 'admin';
|
|
125
|
+
input?: Record<string, unknown>;
|
|
126
|
+
output?: Record<string, unknown>;
|
|
127
|
+
}>;
|
|
128
|
+
}>;
|
|
129
|
+
/**
|
|
130
|
+
* PUBLIC (the Hub, a site): invoke a connector action — e.g. redeem a reward for the signed-in member.
|
|
131
|
+
* 401 SIGN_IN_REQUIRED for a `member` action without a session; 403 for an admin-only one;
|
|
132
|
+
* 400 with `missing[]` when a required input is absent.
|
|
133
|
+
* POST /public/collection/:collectionId/connectors/:appId/actions/:action { input }
|
|
134
|
+
*/
|
|
135
|
+
function callConnectorAction<T = unknown>(collectionId: string, appId: string, action: string, input?: Record<string, unknown>): Promise<T>;
|
|
108
136
|
}
|
package/dist/api/integrations.js
CHANGED
|
@@ -200,4 +200,32 @@ export var integrations;
|
|
|
200
200
|
return post(`${connectorsBase(collectionId)}/${enc(appId)}/steps/${enc(step)}`, input);
|
|
201
201
|
}
|
|
202
202
|
integrations.runConnectorStep = runConnectorStep;
|
|
203
|
+
/**
|
|
204
|
+
* Invoke a connector ACTION from the console (any visibility): redeem, enrol, adjust points, issue a
|
|
205
|
+
* coupon… The connector's function runs with its own credentials and is told who asked.
|
|
206
|
+
* POST /integrations/connectors/:appId/actions/:action { input }
|
|
207
|
+
*/
|
|
208
|
+
async function runConnectorAction(collectionId, appId, action, input = {}) {
|
|
209
|
+
return post(`${connectorsBase(collectionId)}/${enc(appId)}/actions/${enc(action)}`, { input });
|
|
210
|
+
}
|
|
211
|
+
integrations.runConnectorAction = runConnectorAction;
|
|
212
|
+
/**
|
|
213
|
+
* PUBLIC (the Hub, a site): the connector actions the caller may invoke — those declared `public`,
|
|
214
|
+
* plus `member` ones when signed in.
|
|
215
|
+
* GET /public/collection/:collectionId/connectors/:appId/actions
|
|
216
|
+
*/
|
|
217
|
+
async function listConnectorActions(collectionId, appId) {
|
|
218
|
+
return request(`/public/collection/${enc(collectionId)}/connectors/${enc(appId)}/actions`);
|
|
219
|
+
}
|
|
220
|
+
integrations.listConnectorActions = listConnectorActions;
|
|
221
|
+
/**
|
|
222
|
+
* PUBLIC (the Hub, a site): invoke a connector action — e.g. redeem a reward for the signed-in member.
|
|
223
|
+
* 401 SIGN_IN_REQUIRED for a `member` action without a session; 403 for an admin-only one;
|
|
224
|
+
* 400 with `missing[]` when a required input is absent.
|
|
225
|
+
* POST /public/collection/:collectionId/connectors/:appId/actions/:action { input }
|
|
226
|
+
*/
|
|
227
|
+
async function callConnectorAction(collectionId, appId, action, input = {}) {
|
|
228
|
+
return post(`/public/collection/${enc(collectionId)}/connectors/${enc(appId)}/actions/${enc(action)}`, { input });
|
|
229
|
+
}
|
|
230
|
+
integrations.callConnectorAction = callConnectorAction;
|
|
203
231
|
})(integrations || (integrations = {}));
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.15 | Generated: 2026-11-09T15:17:42.472Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -65,7 +65,7 @@ For detailed guides on specific features:
|
|
|
65
65
|
- **[Analytics Metadata Conventions](analytics-metadata-conventions.md)** - Standard recommended keys and conventions for analytics metadata fields
|
|
66
66
|
- **[Loyalty: Points, Members & Earning Rules](loyalty.md)** - Loyalty schemes, automatic point earning via interaction rules, member balances, transaction history, and manual adjustments
|
|
67
67
|
- **[Integrations](integrations.md)** - Inbound/outbound integration flows + the sealed-secret store; triggers (manual/event/schedule), event patterns, the event envelope (interaction/contact/event sources), identity policy, event mapping, signed deliveries, contact as a target, field mappings
|
|
68
|
-
- **[Connectors](connectors.md)** - Connector apps (adapters for one external provider): a manifest `connector` block + server functions (testConnection/describeTarget/sample/fetch/shape/serialize/send/describeEvents/lookup); loyalty and CRM connectors; core does the mapping
|
|
68
|
+
- **[Connectors](connectors.md)** - Connector apps (adapters for one external provider): a manifest `connector` block + server functions (testConnection/describeTarget/sample/fetch/shape/serialize/send/describeEvents/lookup); loyalty and CRM connectors; actions (provider operations on demand); core does the mapping
|
|
69
69
|
- **[Entitlements](entitlements.md)** - "Is this person gold or above?" answered on demand from signed claims, a connector lookup (cached), or the contact — never a mirrored member base; gates on interaction types and in server functions
|
|
70
70
|
- **[AI Tools & Skills](ai-tools-and-skills.md)** - The AI capability catalog: skills apps invoke by name (e.g. research.brand), the tools the AI reaches for (web fetch/screenshot/brand assets/image gen), the agent loop, and how apps discover them
|
|
71
71
|
- **[Deep Link Discovery](deep-link-discovery.md)** - Registering and discovering navigable app states for portal menus and AI orchestration
|
|
@@ -3112,6 +3112,19 @@ interface ConnectorPanel {
|
|
|
3112
3112
|
}
|
|
3113
3113
|
```
|
|
3114
3114
|
|
|
3115
|
+
**ConnectorAction** (interface)
|
|
3116
|
+
```typescript
|
|
3117
|
+
interface ConnectorAction {
|
|
3118
|
+
name: string
|
|
3119
|
+
title?: string
|
|
3120
|
+
description?: string
|
|
3121
|
+
function: string
|
|
3122
|
+
visibility?: 'admin' | 'member' | 'public'
|
|
3123
|
+
input?: Record<string, unknown>
|
|
3124
|
+
output?: Record<string, unknown>
|
|
3125
|
+
}
|
|
3126
|
+
```
|
|
3127
|
+
|
|
3115
3128
|
**AppConnectorDeclaration** (interface)
|
|
3116
3129
|
```typescript
|
|
3117
3130
|
interface AppConnectorDeclaration {
|
|
@@ -3134,6 +3147,7 @@ interface AppConnectorDeclaration {
|
|
|
3134
3147
|
lookup?: string
|
|
3135
3148
|
}
|
|
3136
3149
|
panels?: ConnectorPanel[]
|
|
3150
|
+
actions?: ConnectorAction[]
|
|
3137
3151
|
aiGuide?: string
|
|
3138
3152
|
recordId?: string
|
|
3139
3153
|
limits?: { requestsPerSecond?: number; maxEvents?: number; maxAttributes?: number }
|
|
@@ -7980,6 +7994,7 @@ interface IntegrationFlowConfig {
|
|
|
7980
7994
|
signing?: { credentialRef: string; previousCredentialRef?: string }
|
|
7981
7995
|
identity?: { policy?: IdentityPolicy; hashKeyCredentialRef?: string }
|
|
7982
7996
|
eventMapping?: EventMapping
|
|
7997
|
+
envelopeFormat?: 'cloudevents'
|
|
7983
7998
|
connection?: FlowConnection
|
|
7984
7999
|
fieldMappings?: FieldMapping[]
|
|
7985
8000
|
connector?: FlowConnectorRef
|
|
@@ -8222,6 +8237,7 @@ interface ConnectorLookupResult {
|
|
|
8222
8237
|
**EventEnvelope** (interface)
|
|
8223
8238
|
```typescript
|
|
8224
8239
|
interface EventEnvelope {
|
|
8240
|
+
specVersion: number
|
|
8225
8241
|
id: string
|
|
8226
8242
|
type: string
|
|
8227
8243
|
entityType: string | null
|
|
@@ -8234,6 +8250,8 @@ interface EventEnvelope {
|
|
|
8234
8250
|
contactId: string | null
|
|
8235
8251
|
userId: string | null
|
|
8236
8252
|
externalIds: Record<string, string>
|
|
8253
|
+
anonymousId: string | null
|
|
8254
|
+
sessionId: string | null
|
|
8237
8255
|
emailHash?: string | null
|
|
8238
8256
|
phoneHash?: string | null
|
|
8239
8257
|
hashAlgorithm?: 'hmac-sha256' | null
|
|
@@ -8243,6 +8261,7 @@ interface EventEnvelope {
|
|
|
8243
8261
|
identityPolicy: IdentityPolicy
|
|
8244
8262
|
product: { id: string; name?: string | null; sku?: string | null; gtin?: string | null; variantId?: string | null; batchId?: string | null } | null
|
|
8245
8263
|
proof: { id: string } | null
|
|
8264
|
+
consent: { channels: Record<'email' | 'sms' | 'push' | 'whatsapp', 'opted_in' | 'opted_out' | 'suppressed' | 'unknown'>; topics: Record<string, boolean>; updatedAt: string | null } | null
|
|
8246
8265
|
subject: Record<string, unknown>
|
|
8247
8266
|
event?: { name: string; attributes: Record<string, unknown>; rule: string | null }
|
|
8248
8267
|
attributes: Record<string, unknown>
|
|
@@ -12984,6 +13003,22 @@ Save a connector's non-secret Connect-form values and/or settings. PUT /integrat
|
|
|
12984
13003
|
input: Record<string, unknown> & { flowId?: string } = {}) → `Promise<T>`
|
|
12985
13004
|
Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` → ConnectorTargetSchema, `sample` → ConnectorRecords, and dry runs of `shape` ({ records }) → ConnectorShapeResult and `serialize` ({ documents }) → ConnectorSerializeResult. The server adds the installation; pass `flowId` to layer that flow's settings. POST /integrations/connectors/:appId/steps/:step
|
|
12986
13005
|
|
|
13006
|
+
**runConnectorAction**(collectionId: string,
|
|
13007
|
+
appId: string,
|
|
13008
|
+
action: string,
|
|
13009
|
+
input: Record<string, unknown> = {}) → `Promise<T>`
|
|
13010
|
+
Invoke a connector ACTION from the console (any visibility): redeem, enrol, adjust points, issue a coupon… The connector's function runs with its own credentials and is told who asked. POST /integrations/connectors/:appId/actions/:action { input }
|
|
13011
|
+
|
|
13012
|
+
**listConnectorActions**(collectionId: string,
|
|
13013
|
+
appId: string) → `Promise<`
|
|
13014
|
+
PUBLIC (the Hub, a site): the connector actions the caller may invoke — those declared `public`, plus `member` ones when signed in. GET /public/collection/:collectionId/connectors/:appId/actions
|
|
13015
|
+
|
|
13016
|
+
**callConnectorAction**(collectionId: string,
|
|
13017
|
+
appId: string,
|
|
13018
|
+
action: string,
|
|
13019
|
+
input: Record<string, unknown> = {}) → `Promise<T>`
|
|
13020
|
+
PUBLIC (the Hub, a site): invoke a connector action — e.g. redeem a reward for the signed-in member. 401 SIGN_IN_REQUIRED for a `member` action without a session; 403 for an admin-only one; 400 with `missing[]` when a required input is absent. POST /public/collection/:collectionId/connectors/:appId/actions/:action { input }
|
|
13021
|
+
|
|
12987
13022
|
### interactions
|
|
12988
13023
|
|
|
12989
13024
|
**query**(collectionId: string,
|
package/dist/docs/connectors.md
CHANGED
|
@@ -92,6 +92,7 @@ Types: `AppConnectorDeclaration` and friends in `@proveanything/smartlinks` (`sr
|
|
|
92
92
|
| `aiGuide` | A guide the mapping assistant reads: the provider's quirks, required fields, good defaults. |
|
|
93
93
|
| `recordId` | Path of the provider's own id in a fetched record (e.g. `gtin`). Inbound records are landed and matched by it, so a re-import updates the same product instead of creating another. Default `id`; a flow can override it (`config.upsert.externalIdPath`). |
|
|
94
94
|
| `limits` | `{ requestsPerSecond }` toward the provider, enforced across every SmartLinks worker; `{ maxEvents, maxAttributes }` when the provider caps registered events — a flow's `eventMapping` is checked against them when saved. |
|
|
95
|
+
| `actions[]` | Provider operations invoked on demand (redeem, enrol, issue…): name, title, function, `visibility`, input schema. See [Actions](#actions-provider-operations-on-demand). |
|
|
95
96
|
|
|
96
97
|
---
|
|
97
98
|
|
|
@@ -219,6 +220,61 @@ A connector should ship an `eventMapping` **preset** with the provider's standar
|
|
|
219
220
|
|
|
220
221
|
---
|
|
221
222
|
|
|
223
|
+
## Actions: provider operations on demand
|
|
224
|
+
|
|
225
|
+
Steps are called by the engine. **Actions** are called when someone asks: redeem a reward, enrol a
|
|
226
|
+
member, issue a coupon, raise a ticket, send a receipt, check a balance. A connector declares them in
|
|
227
|
+
the manifest and implements each as a server function; the platform decides who may call them and
|
|
228
|
+
runs them with the connector's credentials, telling the function who asked.
|
|
229
|
+
|
|
230
|
+
```jsonc
|
|
231
|
+
"connector": {
|
|
232
|
+
"actions": [
|
|
233
|
+
{ "name": "redeem", "title": "Redeem a reward", "function": "redeemReward", "visibility": "member",
|
|
234
|
+
"input": { "type": "object", "required": ["rewardId"], "properties": { "rewardId": { "type": "string" } } } },
|
|
235
|
+
{ "name": "enrol", "title": "Join the programme", "function": "enrolMember", "visibility": "member" },
|
|
236
|
+
{ "name": "coupon-status", "title": "Coupon status", "function": "couponStatus", "visibility": "public" },
|
|
237
|
+
{ "name": "adjust-points", "title": "Adjust points", "function": "adjustPoints" } // admin only (the default)
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
| `visibility` | Who may invoke it |
|
|
243
|
+
|---|---|
|
|
244
|
+
| `admin` (default) | the console and admin API; any app function |
|
|
245
|
+
| `member` | also a signed-in person through the Hub / a site |
|
|
246
|
+
| `public` | also anyone |
|
|
247
|
+
|
|
248
|
+
The function receives `event.body = { input, installation, caller: { userId, contactId, role, surface } }`
|
|
249
|
+
and returns JSON (or a `Response`). `input.required` fields from the declared schema are checked before
|
|
250
|
+
the function runs (400 with `missing[]`); fuller validation is the connector's job.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
export async function redeemReward(ctx: any, event: any) {
|
|
254
|
+
const { input, caller } = event.body
|
|
255
|
+
if (!caller.contactId) return new Response(JSON.stringify({ error: 'no_contact' }), { status: 400 })
|
|
256
|
+
const memberId = (await ctx.sl.contacts.forCaller())?.customFields?.loyaltyMemberId // written by Profile
|
|
257
|
+
const res = await ctx.fetch(`${api}/members/${memberId}/redemptions`, { method: 'POST', headers: auth(await ctx.secrets.get('apiKey')), body: JSON.stringify({ reward: input.rewardId }) })
|
|
258
|
+
return res.json()
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Calling an action:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// From the Hub or a site (visibility public / member):
|
|
266
|
+
const { actions } = await integrations.listConnectorActions(collectionId, 'acme-loyalty')
|
|
267
|
+
const out = await integrations.callConnectorAction(collectionId, 'acme-loyalty', 'redeem', { rewardId: 'R-5' })
|
|
268
|
+
|
|
269
|
+
// From another app's server function (any visibility; needs sl:connectors:write):
|
|
270
|
+
const out = await ctx.sl.connectors.call('acme-loyalty', 'redeem', { rewardId: 'R-5' })
|
|
271
|
+
|
|
272
|
+
// From the console (any visibility):
|
|
273
|
+
await integrations.runConnectorAction(collectionId, 'acme-loyalty', 'adjust-points', { memberId, delta: 100 })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
222
278
|
## Where configuration lives
|
|
223
279
|
|
|
224
280
|
| What | Where |
|
|
@@ -118,6 +118,7 @@ mapped with the flow's field mappings (or handed to a connector's `serialize`):
|
|
|
118
118
|
|
|
119
119
|
```jsonc
|
|
120
120
|
{
|
|
121
|
+
"specVersion": 1, // bumped only when a consumer could break
|
|
121
122
|
"id": "0b2b…", // the event's id — stable across retries; use it as the destination's idempotency / transaction id
|
|
122
123
|
"type": "interaction.submitted:quiz-1",
|
|
123
124
|
"entityType": "interaction", "entityId": "0b2b…",
|
|
@@ -126,12 +127,14 @@ mapped with the flow's field mappings (or handed to a connector's `serialize`):
|
|
|
126
127
|
"identity": {
|
|
127
128
|
"contactId": "…", "userId": "…",
|
|
128
129
|
"externalIds": { "acme-loyalty": "M-9", "crm": "C-1" }, // ids other systems know this person by
|
|
130
|
+
"anonymousId": "v-77", "sessionId": "s-1", // the visitor / device and session, before anyone signed in (every policy)
|
|
129
131
|
"emailHash": "…", "phoneHash": "…", "hashAlgorithm": "hmac-sha256" // policy 'hashed' (default)
|
|
130
132
|
// "email", "phone": only under policy 'raw'
|
|
131
133
|
},
|
|
132
134
|
"identityPolicy": "hashed",
|
|
133
135
|
"product": { "id": "P1", "name": "Widget", "sku": "W-1", "gtin": "0500…", "variantId": null, "batchId": null },
|
|
134
136
|
"proof": { "id": "…" },
|
|
137
|
+
"consent": { "channels": { "email": "opted_in", "sms": "suppressed", "push": "unknown", "whatsapp": "unknown" }, "topics": { "offers": true }, "updatedAt": "…" }, // null when about nobody we hold
|
|
135
138
|
"subject": { "interactionId": "quiz-1", "outcome": "completed", "metadata": { "score": 9 }, "source": "hub" },
|
|
136
139
|
"event": { "name": "engagement_completed", "attributes": { "kind": "quiz", "score": 9 }, "rule": "quiz" }, // from eventMapping
|
|
137
140
|
"attributes": {}
|
|
@@ -142,6 +145,11 @@ Field mappings read envelope paths (`identity.externalIds.acme-loyalty`, `subjec
|
|
|
142
145
|
`event.name`, `product.sku`). With no mappings the envelope itself is sent. A failed event delivery
|
|
143
146
|
keeps its envelope and is retried from it; a product is re-read instead.
|
|
144
147
|
|
|
148
|
+
**CloudEvents.** `config.envelopeFormat: 'cloudevents'` wraps each delivered body (mapped or not) as a
|
|
149
|
+
CloudEvents 1.0 structured event — `{ specversion: '1.0', id, type, source: 'urn:smartlinks:collection:<id>',
|
|
150
|
+
subject, time, datacontenttype, dataschema: 'urn:smartlinks:envelope:v1', data }` — the shape every event
|
|
151
|
+
bus, lake and serverless tool already consumes.
|
|
152
|
+
|
|
145
153
|
```ts
|
|
146
154
|
const flow = await integrations.createFlow(collectionId, {
|
|
147
155
|
appId: 'core-integrations', direction: 'outbound', name: 'Engagement → loyalty', status: 'active',
|
|
@@ -518,6 +518,7 @@ Prefer the platform primitives above over a dependency; when you do need one, pi
|
|
|
518
518
|
| Put a visitor's details on a contact | `ctx.sl.contacts.upsert({ email, firstName, … })` → `{ contactId, created }` (fills empty fields only) | `sl:contacts:write` |
|
|
519
519
|
| Record "they did X" | `ctx.sl.interactions.record({ interactionId, contactId, outcome, metadata })` | `sl:interactions:write` |
|
|
520
520
|
| Land a loyalty / CRM provider's view of a person (member id, tier, points, tags, external ids) | `ctx.sl.members.apply({ identity, contact?, loyalty?, tags?, externalIds? }, { provider })` → `{ contactId, op }` — matches by id, external id, email or phone; profile fields fill-empty only; see [connectors.md](connectors.md#loyalty-and-crm-connectors) | `sl:members:write` |
|
|
521
|
+
| Invoke a connector's action — redeem, enrol, issue a coupon, raise a ticket — with the connector's credentials | `ctx.sl.connectors.call('acme-loyalty', 'redeem', { rewardId })` → the action's result; `ctx.sl.connectors.actions(appId)` lists them; see [connectors.md](connectors.md#actions-provider-operations-on-demand) | `sl:connectors:write` |
|
|
521
522
|
| Ask "is this person gold or above?" without holding their member record | `ctx.sl.entitlements.check('tier>=gold')` → `{ allowed, basis, asOf, attributes }` for the signed-in caller (or `{ identity, token, fallback }`); see [entitlements.md](entitlements.md) | `sl:entitlements:read` |
|
|
522
523
|
| Take a booking with your own checks (hours, queue token, payment) | `ctx.sl.bookings.create({ slotId \| resource + time, contactId, status, idempotencyKey })` — never oversold, never double-booked; also `confirm` / `cancel` / `move` / `mine` / `freebusy` / `message` (everyone booked, hydrated) / `slots.*` / `blocks.*` (see [bookings.md](bookings.md)) | `sl:bookings:write` (`sl:bookings:read` to read) |
|
|
523
524
|
|
package/dist/openapi.yaml
CHANGED
|
@@ -21235,6 +21235,32 @@ components:
|
|
|
21235
21235
|
- id
|
|
21236
21236
|
- title
|
|
21237
21237
|
- component
|
|
21238
|
+
ConnectorAction:
|
|
21239
|
+
type: object
|
|
21240
|
+
properties:
|
|
21241
|
+
name:
|
|
21242
|
+
type: string
|
|
21243
|
+
title:
|
|
21244
|
+
type: string
|
|
21245
|
+
description:
|
|
21246
|
+
type: string
|
|
21247
|
+
function:
|
|
21248
|
+
type: string
|
|
21249
|
+
visibility:
|
|
21250
|
+
type: string
|
|
21251
|
+
enum:
|
|
21252
|
+
- admin
|
|
21253
|
+
- member
|
|
21254
|
+
- public
|
|
21255
|
+
input:
|
|
21256
|
+
type: object
|
|
21257
|
+
additionalProperties: true
|
|
21258
|
+
output:
|
|
21259
|
+
type: object
|
|
21260
|
+
additionalProperties: true
|
|
21261
|
+
required:
|
|
21262
|
+
- name
|
|
21263
|
+
- function
|
|
21238
21264
|
AppConnectorDeclaration:
|
|
21239
21265
|
type: object
|
|
21240
21266
|
properties:
|
|
@@ -21288,6 +21314,10 @@ components:
|
|
|
21288
21314
|
type: array
|
|
21289
21315
|
items:
|
|
21290
21316
|
$ref: "#/components/schemas/ConnectorPanel"
|
|
21317
|
+
actions:
|
|
21318
|
+
type: array
|
|
21319
|
+
items:
|
|
21320
|
+
$ref: "#/components/schemas/ConnectorAction"
|
|
21291
21321
|
aiGuide:
|
|
21292
21322
|
type: string
|
|
21293
21323
|
recordId:
|
|
@@ -29060,6 +29090,10 @@ components:
|
|
|
29060
29090
|
additionalProperties: true
|
|
29061
29091
|
eventMapping:
|
|
29062
29092
|
$ref: "#/components/schemas/EventMapping"
|
|
29093
|
+
envelopeFormat:
|
|
29094
|
+
type: string
|
|
29095
|
+
enum:
|
|
29096
|
+
- cloudevents
|
|
29063
29097
|
connection:
|
|
29064
29098
|
$ref: "#/components/schemas/FlowConnection"
|
|
29065
29099
|
fieldMappings:
|
|
@@ -29520,6 +29554,8 @@ components:
|
|
|
29520
29554
|
EventEnvelope:
|
|
29521
29555
|
type: object
|
|
29522
29556
|
properties:
|
|
29557
|
+
specVersion:
|
|
29558
|
+
type: number
|
|
29523
29559
|
id:
|
|
29524
29560
|
type: string
|
|
29525
29561
|
type:
|
|
@@ -29547,6 +29583,10 @@ components:
|
|
|
29547
29583
|
type: object
|
|
29548
29584
|
additionalProperties:
|
|
29549
29585
|
type: string
|
|
29586
|
+
anonymousId:
|
|
29587
|
+
type: string
|
|
29588
|
+
sessionId:
|
|
29589
|
+
type: string
|
|
29550
29590
|
emailHash:
|
|
29551
29591
|
type: string
|
|
29552
29592
|
phoneHash:
|
|
@@ -29567,6 +29607,9 @@ components:
|
|
|
29567
29607
|
proof:
|
|
29568
29608
|
type: object
|
|
29569
29609
|
additionalProperties: true
|
|
29610
|
+
consent:
|
|
29611
|
+
type: object
|
|
29612
|
+
additionalProperties: true
|
|
29570
29613
|
subject:
|
|
29571
29614
|
type: object
|
|
29572
29615
|
additionalProperties: true
|
|
@@ -29577,6 +29620,7 @@ components:
|
|
|
29577
29620
|
type: object
|
|
29578
29621
|
additionalProperties: true
|
|
29579
29622
|
required:
|
|
29623
|
+
- specVersion
|
|
29580
29624
|
- id
|
|
29581
29625
|
- type
|
|
29582
29626
|
- entityType
|
|
@@ -29589,9 +29633,12 @@ components:
|
|
|
29589
29633
|
- contactId
|
|
29590
29634
|
- userId
|
|
29591
29635
|
- externalIds
|
|
29636
|
+
- anonymousId
|
|
29637
|
+
- sessionId
|
|
29592
29638
|
- identityPolicy
|
|
29593
29639
|
- product
|
|
29594
29640
|
- proof
|
|
29641
|
+
- consent
|
|
29595
29642
|
- subject
|
|
29596
29643
|
- attributes
|
|
29597
29644
|
CapturedRequest:
|
|
@@ -1030,6 +1030,27 @@ export interface ConnectorPanel {
|
|
|
1030
1030
|
/** Exported component name in the app's widgets bundle (manifest `widgets.files`). */
|
|
1031
1031
|
component: string;
|
|
1032
1032
|
}
|
|
1033
|
+
/**
|
|
1034
|
+
* A named provider operation invoked ON DEMAND (not by the engine): redeem a reward, enrol a member,
|
|
1035
|
+
* issue a coupon, raise a ticket, send a receipt. Called by an app function (`ctx.sl.connectors.call`),
|
|
1036
|
+
* by the Hub (public route — only `public`, or `member` for a signed-in person) or by the console. The
|
|
1037
|
+
* function receives `{ input, installation, caller }` and returns JSON; it runs with the connector's
|
|
1038
|
+
* own credentials and is told who asked.
|
|
1039
|
+
*/
|
|
1040
|
+
export interface ConnectorAction {
|
|
1041
|
+
/** `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$` */
|
|
1042
|
+
name: string;
|
|
1043
|
+
title?: string;
|
|
1044
|
+
description?: string;
|
|
1045
|
+
/** This app's server function implementing it. */
|
|
1046
|
+
function: string;
|
|
1047
|
+
/** Who may invoke it outside functions and the console. Default 'admin'. */
|
|
1048
|
+
visibility?: 'admin' | 'member' | 'public';
|
|
1049
|
+
/** JSON Schema for `input`; `required` fields are checked before the function runs. */
|
|
1050
|
+
input?: Record<string, unknown>;
|
|
1051
|
+
/** JSON Schema for the result (documentation; not enforced). */
|
|
1052
|
+
output?: Record<string, unknown>;
|
|
1053
|
+
}
|
|
1033
1054
|
/**
|
|
1034
1055
|
* The connector contract. Every function named here is one of this app's server functions
|
|
1035
1056
|
* (manifest `functions.definitions`); the engine calls it with collection authority, capped by the
|
|
@@ -1092,6 +1113,8 @@ export interface AppConnectorDeclaration {
|
|
|
1092
1113
|
lookup?: string;
|
|
1093
1114
|
};
|
|
1094
1115
|
panels?: ConnectorPanel[];
|
|
1116
|
+
/** On-demand operations the connector offers (see ConnectorAction). */
|
|
1117
|
+
actions?: ConnectorAction[];
|
|
1095
1118
|
/** Path (under the app's public files) of a guide the AI mapping assistant reads. */
|
|
1096
1119
|
aiGuide?: string;
|
|
1097
1120
|
/**
|
|
@@ -92,6 +92,12 @@ export interface IntegrationFlowConfig {
|
|
|
92
92
|
};
|
|
93
93
|
/** Event-sourced flows: fold SmartLinks events into the provider's registered events. */
|
|
94
94
|
eventMapping?: EventMapping;
|
|
95
|
+
/**
|
|
96
|
+
* Wrap each delivered body as a CloudEvents 1.0 structured event
|
|
97
|
+
* ({ specversion, id, type, source, subject, time, datacontenttype, dataschema, data }) — the shape
|
|
98
|
+
* event buses, lakes and serverless tools consume without custom code.
|
|
99
|
+
*/
|
|
100
|
+
envelopeFormat?: 'cloudevents';
|
|
95
101
|
connection?: FlowConnection;
|
|
96
102
|
fieldMappings?: FieldMapping[];
|
|
97
103
|
/** Run this flow through a connector app's functions instead of the built-in mapper/transport. */
|
|
@@ -369,6 +375,8 @@ export interface ConnectorLookupResult {
|
|
|
369
375
|
* connector's `serialize` / `send`, after the flow's identity policy and event mapping.
|
|
370
376
|
*/
|
|
371
377
|
export interface EventEnvelope {
|
|
378
|
+
/** Schema version of this envelope. Bumped only when a consumer could break (a field removed or changed). */
|
|
379
|
+
specVersion: number;
|
|
372
380
|
/** The event's id: stable across retries; use it as the provider's transaction / idempotency id. */
|
|
373
381
|
id: string;
|
|
374
382
|
/** Spine event name, e.g. 'interaction.submitted:<typeId>', 'contact.updated', 'tag.scanned:scan_redirect'. */
|
|
@@ -384,6 +392,9 @@ export interface EventEnvelope {
|
|
|
384
392
|
userId: string | null;
|
|
385
393
|
/** Ids other systems know this person by, keyed by system (a connector's `provider`). */
|
|
386
394
|
externalIds: Record<string, string>;
|
|
395
|
+
/** The visitor / device id before anyone signed in (an analytics destination stitches on it later). Under every policy. */
|
|
396
|
+
anonymousId: string | null;
|
|
397
|
+
sessionId: string | null;
|
|
387
398
|
emailHash?: string | null;
|
|
388
399
|
phoneHash?: string | null;
|
|
389
400
|
hashAlgorithm?: 'hmac-sha256' | null;
|
|
@@ -403,6 +414,15 @@ export interface EventEnvelope {
|
|
|
403
414
|
proof: {
|
|
404
415
|
id: string;
|
|
405
416
|
} | null;
|
|
417
|
+
/**
|
|
418
|
+
* The person's communication consent, summarised per channel from the contact's comms state; null
|
|
419
|
+
* when the event is about nobody we hold. A CRM or CDP must not act against it.
|
|
420
|
+
*/
|
|
421
|
+
consent: {
|
|
422
|
+
channels: Record<'email' | 'sms' | 'push' | 'whatsapp', 'opted_in' | 'opted_out' | 'suppressed' | 'unknown'>;
|
|
423
|
+
topics: Record<string, boolean>;
|
|
424
|
+
updatedAt: string | null;
|
|
425
|
+
} | null;
|
|
406
426
|
/** The event's own fields: an interaction's interactionId / outcome / metadata, a scan's codeId / country, a contact's profile. */
|
|
407
427
|
subject: Record<string, unknown>;
|
|
408
428
|
/** From the flow's event mapping, when it has one. */
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.15 | Generated: 2026-11-09T15:17:42.472Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -65,7 +65,7 @@ For detailed guides on specific features:
|
|
|
65
65
|
- **[Analytics Metadata Conventions](analytics-metadata-conventions.md)** - Standard recommended keys and conventions for analytics metadata fields
|
|
66
66
|
- **[Loyalty: Points, Members & Earning Rules](loyalty.md)** - Loyalty schemes, automatic point earning via interaction rules, member balances, transaction history, and manual adjustments
|
|
67
67
|
- **[Integrations](integrations.md)** - Inbound/outbound integration flows + the sealed-secret store; triggers (manual/event/schedule), event patterns, the event envelope (interaction/contact/event sources), identity policy, event mapping, signed deliveries, contact as a target, field mappings
|
|
68
|
-
- **[Connectors](connectors.md)** - Connector apps (adapters for one external provider): a manifest `connector` block + server functions (testConnection/describeTarget/sample/fetch/shape/serialize/send/describeEvents/lookup); loyalty and CRM connectors; core does the mapping
|
|
68
|
+
- **[Connectors](connectors.md)** - Connector apps (adapters for one external provider): a manifest `connector` block + server functions (testConnection/describeTarget/sample/fetch/shape/serialize/send/describeEvents/lookup); loyalty and CRM connectors; actions (provider operations on demand); core does the mapping
|
|
69
69
|
- **[Entitlements](entitlements.md)** - "Is this person gold or above?" answered on demand from signed claims, a connector lookup (cached), or the contact — never a mirrored member base; gates on interaction types and in server functions
|
|
70
70
|
- **[AI Tools & Skills](ai-tools-and-skills.md)** - The AI capability catalog: skills apps invoke by name (e.g. research.brand), the tools the AI reaches for (web fetch/screenshot/brand assets/image gen), the agent loop, and how apps discover them
|
|
71
71
|
- **[Deep Link Discovery](deep-link-discovery.md)** - Registering and discovering navigable app states for portal menus and AI orchestration
|
|
@@ -3112,6 +3112,19 @@ interface ConnectorPanel {
|
|
|
3112
3112
|
}
|
|
3113
3113
|
```
|
|
3114
3114
|
|
|
3115
|
+
**ConnectorAction** (interface)
|
|
3116
|
+
```typescript
|
|
3117
|
+
interface ConnectorAction {
|
|
3118
|
+
name: string
|
|
3119
|
+
title?: string
|
|
3120
|
+
description?: string
|
|
3121
|
+
function: string
|
|
3122
|
+
visibility?: 'admin' | 'member' | 'public'
|
|
3123
|
+
input?: Record<string, unknown>
|
|
3124
|
+
output?: Record<string, unknown>
|
|
3125
|
+
}
|
|
3126
|
+
```
|
|
3127
|
+
|
|
3115
3128
|
**AppConnectorDeclaration** (interface)
|
|
3116
3129
|
```typescript
|
|
3117
3130
|
interface AppConnectorDeclaration {
|
|
@@ -3134,6 +3147,7 @@ interface AppConnectorDeclaration {
|
|
|
3134
3147
|
lookup?: string
|
|
3135
3148
|
}
|
|
3136
3149
|
panels?: ConnectorPanel[]
|
|
3150
|
+
actions?: ConnectorAction[]
|
|
3137
3151
|
aiGuide?: string
|
|
3138
3152
|
recordId?: string
|
|
3139
3153
|
limits?: { requestsPerSecond?: number; maxEvents?: number; maxAttributes?: number }
|
|
@@ -7980,6 +7994,7 @@ interface IntegrationFlowConfig {
|
|
|
7980
7994
|
signing?: { credentialRef: string; previousCredentialRef?: string }
|
|
7981
7995
|
identity?: { policy?: IdentityPolicy; hashKeyCredentialRef?: string }
|
|
7982
7996
|
eventMapping?: EventMapping
|
|
7997
|
+
envelopeFormat?: 'cloudevents'
|
|
7983
7998
|
connection?: FlowConnection
|
|
7984
7999
|
fieldMappings?: FieldMapping[]
|
|
7985
8000
|
connector?: FlowConnectorRef
|
|
@@ -8222,6 +8237,7 @@ interface ConnectorLookupResult {
|
|
|
8222
8237
|
**EventEnvelope** (interface)
|
|
8223
8238
|
```typescript
|
|
8224
8239
|
interface EventEnvelope {
|
|
8240
|
+
specVersion: number
|
|
8225
8241
|
id: string
|
|
8226
8242
|
type: string
|
|
8227
8243
|
entityType: string | null
|
|
@@ -8234,6 +8250,8 @@ interface EventEnvelope {
|
|
|
8234
8250
|
contactId: string | null
|
|
8235
8251
|
userId: string | null
|
|
8236
8252
|
externalIds: Record<string, string>
|
|
8253
|
+
anonymousId: string | null
|
|
8254
|
+
sessionId: string | null
|
|
8237
8255
|
emailHash?: string | null
|
|
8238
8256
|
phoneHash?: string | null
|
|
8239
8257
|
hashAlgorithm?: 'hmac-sha256' | null
|
|
@@ -8243,6 +8261,7 @@ interface EventEnvelope {
|
|
|
8243
8261
|
identityPolicy: IdentityPolicy
|
|
8244
8262
|
product: { id: string; name?: string | null; sku?: string | null; gtin?: string | null; variantId?: string | null; batchId?: string | null } | null
|
|
8245
8263
|
proof: { id: string } | null
|
|
8264
|
+
consent: { channels: Record<'email' | 'sms' | 'push' | 'whatsapp', 'opted_in' | 'opted_out' | 'suppressed' | 'unknown'>; topics: Record<string, boolean>; updatedAt: string | null } | null
|
|
8246
8265
|
subject: Record<string, unknown>
|
|
8247
8266
|
event?: { name: string; attributes: Record<string, unknown>; rule: string | null }
|
|
8248
8267
|
attributes: Record<string, unknown>
|
|
@@ -12984,6 +13003,22 @@ Save a connector's non-secret Connect-form values and/or settings. PUT /integrat
|
|
|
12984
13003
|
input: Record<string, unknown> & { flowId?: string } = {}) → `Promise<T>`
|
|
12985
13004
|
Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` → ConnectorTargetSchema, `sample` → ConnectorRecords, and dry runs of `shape` ({ records }) → ConnectorShapeResult and `serialize` ({ documents }) → ConnectorSerializeResult. The server adds the installation; pass `flowId` to layer that flow's settings. POST /integrations/connectors/:appId/steps/:step
|
|
12986
13005
|
|
|
13006
|
+
**runConnectorAction**(collectionId: string,
|
|
13007
|
+
appId: string,
|
|
13008
|
+
action: string,
|
|
13009
|
+
input: Record<string, unknown> = {}) → `Promise<T>`
|
|
13010
|
+
Invoke a connector ACTION from the console (any visibility): redeem, enrol, adjust points, issue a coupon… The connector's function runs with its own credentials and is told who asked. POST /integrations/connectors/:appId/actions/:action { input }
|
|
13011
|
+
|
|
13012
|
+
**listConnectorActions**(collectionId: string,
|
|
13013
|
+
appId: string) → `Promise<`
|
|
13014
|
+
PUBLIC (the Hub, a site): the connector actions the caller may invoke — those declared `public`, plus `member` ones when signed in. GET /public/collection/:collectionId/connectors/:appId/actions
|
|
13015
|
+
|
|
13016
|
+
**callConnectorAction**(collectionId: string,
|
|
13017
|
+
appId: string,
|
|
13018
|
+
action: string,
|
|
13019
|
+
input: Record<string, unknown> = {}) → `Promise<T>`
|
|
13020
|
+
PUBLIC (the Hub, a site): invoke a connector action — e.g. redeem a reward for the signed-in member. 401 SIGN_IN_REQUIRED for a `member` action without a session; 403 for an admin-only one; 400 with `missing[]` when a required input is absent. POST /public/collection/:collectionId/connectors/:appId/actions/:action { input }
|
|
13021
|
+
|
|
12987
13022
|
### interactions
|
|
12988
13023
|
|
|
12989
13024
|
**query**(collectionId: string,
|
package/docs/connectors.md
CHANGED
|
@@ -92,6 +92,7 @@ Types: `AppConnectorDeclaration` and friends in `@proveanything/smartlinks` (`sr
|
|
|
92
92
|
| `aiGuide` | A guide the mapping assistant reads: the provider's quirks, required fields, good defaults. |
|
|
93
93
|
| `recordId` | Path of the provider's own id in a fetched record (e.g. `gtin`). Inbound records are landed and matched by it, so a re-import updates the same product instead of creating another. Default `id`; a flow can override it (`config.upsert.externalIdPath`). |
|
|
94
94
|
| `limits` | `{ requestsPerSecond }` toward the provider, enforced across every SmartLinks worker; `{ maxEvents, maxAttributes }` when the provider caps registered events — a flow's `eventMapping` is checked against them when saved. |
|
|
95
|
+
| `actions[]` | Provider operations invoked on demand (redeem, enrol, issue…): name, title, function, `visibility`, input schema. See [Actions](#actions-provider-operations-on-demand). |
|
|
95
96
|
|
|
96
97
|
---
|
|
97
98
|
|
|
@@ -219,6 +220,61 @@ A connector should ship an `eventMapping` **preset** with the provider's standar
|
|
|
219
220
|
|
|
220
221
|
---
|
|
221
222
|
|
|
223
|
+
## Actions: provider operations on demand
|
|
224
|
+
|
|
225
|
+
Steps are called by the engine. **Actions** are called when someone asks: redeem a reward, enrol a
|
|
226
|
+
member, issue a coupon, raise a ticket, send a receipt, check a balance. A connector declares them in
|
|
227
|
+
the manifest and implements each as a server function; the platform decides who may call them and
|
|
228
|
+
runs them with the connector's credentials, telling the function who asked.
|
|
229
|
+
|
|
230
|
+
```jsonc
|
|
231
|
+
"connector": {
|
|
232
|
+
"actions": [
|
|
233
|
+
{ "name": "redeem", "title": "Redeem a reward", "function": "redeemReward", "visibility": "member",
|
|
234
|
+
"input": { "type": "object", "required": ["rewardId"], "properties": { "rewardId": { "type": "string" } } } },
|
|
235
|
+
{ "name": "enrol", "title": "Join the programme", "function": "enrolMember", "visibility": "member" },
|
|
236
|
+
{ "name": "coupon-status", "title": "Coupon status", "function": "couponStatus", "visibility": "public" },
|
|
237
|
+
{ "name": "adjust-points", "title": "Adjust points", "function": "adjustPoints" } // admin only (the default)
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
| `visibility` | Who may invoke it |
|
|
243
|
+
|---|---|
|
|
244
|
+
| `admin` (default) | the console and admin API; any app function |
|
|
245
|
+
| `member` | also a signed-in person through the Hub / a site |
|
|
246
|
+
| `public` | also anyone |
|
|
247
|
+
|
|
248
|
+
The function receives `event.body = { input, installation, caller: { userId, contactId, role, surface } }`
|
|
249
|
+
and returns JSON (or a `Response`). `input.required` fields from the declared schema are checked before
|
|
250
|
+
the function runs (400 with `missing[]`); fuller validation is the connector's job.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
export async function redeemReward(ctx: any, event: any) {
|
|
254
|
+
const { input, caller } = event.body
|
|
255
|
+
if (!caller.contactId) return new Response(JSON.stringify({ error: 'no_contact' }), { status: 400 })
|
|
256
|
+
const memberId = (await ctx.sl.contacts.forCaller())?.customFields?.loyaltyMemberId // written by Profile
|
|
257
|
+
const res = await ctx.fetch(`${api}/members/${memberId}/redemptions`, { method: 'POST', headers: auth(await ctx.secrets.get('apiKey')), body: JSON.stringify({ reward: input.rewardId }) })
|
|
258
|
+
return res.json()
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Calling an action:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// From the Hub or a site (visibility public / member):
|
|
266
|
+
const { actions } = await integrations.listConnectorActions(collectionId, 'acme-loyalty')
|
|
267
|
+
const out = await integrations.callConnectorAction(collectionId, 'acme-loyalty', 'redeem', { rewardId: 'R-5' })
|
|
268
|
+
|
|
269
|
+
// From another app's server function (any visibility; needs sl:connectors:write):
|
|
270
|
+
const out = await ctx.sl.connectors.call('acme-loyalty', 'redeem', { rewardId: 'R-5' })
|
|
271
|
+
|
|
272
|
+
// From the console (any visibility):
|
|
273
|
+
await integrations.runConnectorAction(collectionId, 'acme-loyalty', 'adjust-points', { memberId, delta: 100 })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
222
278
|
## Where configuration lives
|
|
223
279
|
|
|
224
280
|
| What | Where |
|
package/docs/integrations.md
CHANGED
|
@@ -118,6 +118,7 @@ mapped with the flow's field mappings (or handed to a connector's `serialize`):
|
|
|
118
118
|
|
|
119
119
|
```jsonc
|
|
120
120
|
{
|
|
121
|
+
"specVersion": 1, // bumped only when a consumer could break
|
|
121
122
|
"id": "0b2b…", // the event's id — stable across retries; use it as the destination's idempotency / transaction id
|
|
122
123
|
"type": "interaction.submitted:quiz-1",
|
|
123
124
|
"entityType": "interaction", "entityId": "0b2b…",
|
|
@@ -126,12 +127,14 @@ mapped with the flow's field mappings (or handed to a connector's `serialize`):
|
|
|
126
127
|
"identity": {
|
|
127
128
|
"contactId": "…", "userId": "…",
|
|
128
129
|
"externalIds": { "acme-loyalty": "M-9", "crm": "C-1" }, // ids other systems know this person by
|
|
130
|
+
"anonymousId": "v-77", "sessionId": "s-1", // the visitor / device and session, before anyone signed in (every policy)
|
|
129
131
|
"emailHash": "…", "phoneHash": "…", "hashAlgorithm": "hmac-sha256" // policy 'hashed' (default)
|
|
130
132
|
// "email", "phone": only under policy 'raw'
|
|
131
133
|
},
|
|
132
134
|
"identityPolicy": "hashed",
|
|
133
135
|
"product": { "id": "P1", "name": "Widget", "sku": "W-1", "gtin": "0500…", "variantId": null, "batchId": null },
|
|
134
136
|
"proof": { "id": "…" },
|
|
137
|
+
"consent": { "channels": { "email": "opted_in", "sms": "suppressed", "push": "unknown", "whatsapp": "unknown" }, "topics": { "offers": true }, "updatedAt": "…" }, // null when about nobody we hold
|
|
135
138
|
"subject": { "interactionId": "quiz-1", "outcome": "completed", "metadata": { "score": 9 }, "source": "hub" },
|
|
136
139
|
"event": { "name": "engagement_completed", "attributes": { "kind": "quiz", "score": 9 }, "rule": "quiz" }, // from eventMapping
|
|
137
140
|
"attributes": {}
|
|
@@ -142,6 +145,11 @@ Field mappings read envelope paths (`identity.externalIds.acme-loyalty`, `subjec
|
|
|
142
145
|
`event.name`, `product.sku`). With no mappings the envelope itself is sent. A failed event delivery
|
|
143
146
|
keeps its envelope and is retried from it; a product is re-read instead.
|
|
144
147
|
|
|
148
|
+
**CloudEvents.** `config.envelopeFormat: 'cloudevents'` wraps each delivered body (mapped or not) as a
|
|
149
|
+
CloudEvents 1.0 structured event — `{ specversion: '1.0', id, type, source: 'urn:smartlinks:collection:<id>',
|
|
150
|
+
subject, time, datacontenttype, dataschema: 'urn:smartlinks:envelope:v1', data }` — the shape every event
|
|
151
|
+
bus, lake and serverless tool already consumes.
|
|
152
|
+
|
|
145
153
|
```ts
|
|
146
154
|
const flow = await integrations.createFlow(collectionId, {
|
|
147
155
|
appId: 'core-integrations', direction: 'outbound', name: 'Engagement → loyalty', status: 'active',
|
package/docs/server-functions.md
CHANGED
|
@@ -518,6 +518,7 @@ Prefer the platform primitives above over a dependency; when you do need one, pi
|
|
|
518
518
|
| Put a visitor's details on a contact | `ctx.sl.contacts.upsert({ email, firstName, … })` → `{ contactId, created }` (fills empty fields only) | `sl:contacts:write` |
|
|
519
519
|
| Record "they did X" | `ctx.sl.interactions.record({ interactionId, contactId, outcome, metadata })` | `sl:interactions:write` |
|
|
520
520
|
| Land a loyalty / CRM provider's view of a person (member id, tier, points, tags, external ids) | `ctx.sl.members.apply({ identity, contact?, loyalty?, tags?, externalIds? }, { provider })` → `{ contactId, op }` — matches by id, external id, email or phone; profile fields fill-empty only; see [connectors.md](connectors.md#loyalty-and-crm-connectors) | `sl:members:write` |
|
|
521
|
+
| Invoke a connector's action — redeem, enrol, issue a coupon, raise a ticket — with the connector's credentials | `ctx.sl.connectors.call('acme-loyalty', 'redeem', { rewardId })` → the action's result; `ctx.sl.connectors.actions(appId)` lists them; see [connectors.md](connectors.md#actions-provider-operations-on-demand) | `sl:connectors:write` |
|
|
521
522
|
| Ask "is this person gold or above?" without holding their member record | `ctx.sl.entitlements.check('tier>=gold')` → `{ allowed, basis, asOf, attributes }` for the signed-in caller (or `{ identity, token, fallback }`); see [entitlements.md](entitlements.md) | `sl:entitlements:read` |
|
|
522
523
|
| Take a booking with your own checks (hours, queue token, payment) | `ctx.sl.bookings.create({ slotId \| resource + time, contactId, status, idempotencyKey })` — never oversold, never double-booked; also `confirm` / `cancel` / `move` / `mine` / `freebusy` / `message` (everyone booked, hydrated) / `slots.*` / `blocks.*` (see [bookings.md](bookings.md)) | `sl:bookings:write` (`sl:bookings:read` to read) |
|
|
523
524
|
|
package/openapi.yaml
CHANGED
|
@@ -21235,6 +21235,32 @@ components:
|
|
|
21235
21235
|
- id
|
|
21236
21236
|
- title
|
|
21237
21237
|
- component
|
|
21238
|
+
ConnectorAction:
|
|
21239
|
+
type: object
|
|
21240
|
+
properties:
|
|
21241
|
+
name:
|
|
21242
|
+
type: string
|
|
21243
|
+
title:
|
|
21244
|
+
type: string
|
|
21245
|
+
description:
|
|
21246
|
+
type: string
|
|
21247
|
+
function:
|
|
21248
|
+
type: string
|
|
21249
|
+
visibility:
|
|
21250
|
+
type: string
|
|
21251
|
+
enum:
|
|
21252
|
+
- admin
|
|
21253
|
+
- member
|
|
21254
|
+
- public
|
|
21255
|
+
input:
|
|
21256
|
+
type: object
|
|
21257
|
+
additionalProperties: true
|
|
21258
|
+
output:
|
|
21259
|
+
type: object
|
|
21260
|
+
additionalProperties: true
|
|
21261
|
+
required:
|
|
21262
|
+
- name
|
|
21263
|
+
- function
|
|
21238
21264
|
AppConnectorDeclaration:
|
|
21239
21265
|
type: object
|
|
21240
21266
|
properties:
|
|
@@ -21288,6 +21314,10 @@ components:
|
|
|
21288
21314
|
type: array
|
|
21289
21315
|
items:
|
|
21290
21316
|
$ref: "#/components/schemas/ConnectorPanel"
|
|
21317
|
+
actions:
|
|
21318
|
+
type: array
|
|
21319
|
+
items:
|
|
21320
|
+
$ref: "#/components/schemas/ConnectorAction"
|
|
21291
21321
|
aiGuide:
|
|
21292
21322
|
type: string
|
|
21293
21323
|
recordId:
|
|
@@ -29060,6 +29090,10 @@ components:
|
|
|
29060
29090
|
additionalProperties: true
|
|
29061
29091
|
eventMapping:
|
|
29062
29092
|
$ref: "#/components/schemas/EventMapping"
|
|
29093
|
+
envelopeFormat:
|
|
29094
|
+
type: string
|
|
29095
|
+
enum:
|
|
29096
|
+
- cloudevents
|
|
29063
29097
|
connection:
|
|
29064
29098
|
$ref: "#/components/schemas/FlowConnection"
|
|
29065
29099
|
fieldMappings:
|
|
@@ -29520,6 +29554,8 @@ components:
|
|
|
29520
29554
|
EventEnvelope:
|
|
29521
29555
|
type: object
|
|
29522
29556
|
properties:
|
|
29557
|
+
specVersion:
|
|
29558
|
+
type: number
|
|
29523
29559
|
id:
|
|
29524
29560
|
type: string
|
|
29525
29561
|
type:
|
|
@@ -29547,6 +29583,10 @@ components:
|
|
|
29547
29583
|
type: object
|
|
29548
29584
|
additionalProperties:
|
|
29549
29585
|
type: string
|
|
29586
|
+
anonymousId:
|
|
29587
|
+
type: string
|
|
29588
|
+
sessionId:
|
|
29589
|
+
type: string
|
|
29550
29590
|
emailHash:
|
|
29551
29591
|
type: string
|
|
29552
29592
|
phoneHash:
|
|
@@ -29567,6 +29607,9 @@ components:
|
|
|
29567
29607
|
proof:
|
|
29568
29608
|
type: object
|
|
29569
29609
|
additionalProperties: true
|
|
29610
|
+
consent:
|
|
29611
|
+
type: object
|
|
29612
|
+
additionalProperties: true
|
|
29570
29613
|
subject:
|
|
29571
29614
|
type: object
|
|
29572
29615
|
additionalProperties: true
|
|
@@ -29577,6 +29620,7 @@ components:
|
|
|
29577
29620
|
type: object
|
|
29578
29621
|
additionalProperties: true
|
|
29579
29622
|
required:
|
|
29623
|
+
- specVersion
|
|
29580
29624
|
- id
|
|
29581
29625
|
- type
|
|
29582
29626
|
- entityType
|
|
@@ -29589,9 +29633,12 @@ components:
|
|
|
29589
29633
|
- contactId
|
|
29590
29634
|
- userId
|
|
29591
29635
|
- externalIds
|
|
29636
|
+
- anonymousId
|
|
29637
|
+
- sessionId
|
|
29592
29638
|
- identityPolicy
|
|
29593
29639
|
- product
|
|
29594
29640
|
- proof
|
|
29641
|
+
- consent
|
|
29595
29642
|
- subject
|
|
29596
29643
|
- attributes
|
|
29597
29644
|
CapturedRequest:
|