@proveanything/smartlinks 2.0.56 → 2.0.57

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.
@@ -1,4 +1,4 @@
1
- import type { IntegrationFlow, CreateFlowInput, UpdateFlowInput, ListFlowsQuery, FlowList, RunFlowInput, RunFlowResult, RunFlowSummary, RunFlowEnqueued, RecordTypesResponse, FlowRun, ListRunsQuery, RunList, ListRunItemsQuery, RunItemList, LoggingWindow, SetLoggingInput } from "../types/integrations.js";
1
+ import type { IntegrationFlow, CreateFlowInput, UpdateFlowInput, ListFlowsQuery, FlowList, RunFlowInput, RunFlowResult, RunFlowSummary, RunFlowEnqueued, RecordTypesResponse, FlowRun, ListRunsQuery, RunList, ListRunItemsQuery, RunItemList, LoggingWindow, SetLoggingInput, InstalledConnector, SaveConnectorInstallationInput, ConnectorConsoleStep } from "../types/integrations.js";
2
2
  export declare namespace integrations {
3
3
  /** List flows in a collection. GET /integrations/flows */
4
4
  function listFlows(collectionId: string, query?: ListFlowsQuery): Promise<FlowList>;
@@ -57,4 +57,24 @@ export declare namespace integrations {
57
57
  function cancelLogging(collectionId: string, connectionId: string): Promise<{
58
58
  canceled: boolean;
59
59
  }>;
60
+ /** The collection's installed connector apps. GET /integrations/connectors */
61
+ function listConnectors(collectionId: string): Promise<{
62
+ connectors: InstalledConnector[];
63
+ }>;
64
+ /** Save a connector's non-secret Connect-form values and/or settings. PUT /integrations/connectors/:appId/installation */
65
+ function saveConnectorInstallation(collectionId: string, appId: string, input: SaveConnectorInstallationInput): Promise<{
66
+ installation: {
67
+ connection: Record<string, unknown>;
68
+ settings: Record<string, unknown>;
69
+ };
70
+ }>;
71
+ /**
72
+ * Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` →
73
+ * ConnectorTargetSchema, `sample` → ConnectorRecords, `transform` (a dry run) → ConnectorTransformResult.
74
+ * The server adds the installation; pass `flowId` to layer that flow's settings.
75
+ * POST /integrations/connectors/:appId/steps/:step
76
+ */
77
+ function runConnectorStep<T = unknown>(collectionId: string, appId: string, step: ConnectorConsoleStep, input?: Record<string, unknown> & {
78
+ flowId?: string;
79
+ }): Promise<T>;
60
80
  }
@@ -135,4 +135,29 @@ export var integrations;
135
135
  return del(`/admin/collection/${enc(collectionId)}/integrations/logging/${enc(connectionId)}`);
136
136
  }
137
137
  integrations.cancelLogging = cancelLogging;
138
+ // --- Connectors (docs/connectors.md) ------------------------------------
139
+ // Connector apps installed on the collection: their declared contract, the installation's
140
+ // non-secret settings, and which secrets are set. Secrets themselves go through
141
+ // `secrets.put(collectionId, name, value, { appId })` for the connector's appId.
142
+ const connectorsBase = (collectionId) => `/admin/collection/${enc(collectionId)}/integrations/connectors`;
143
+ /** The collection's installed connector apps. GET /integrations/connectors */
144
+ async function listConnectors(collectionId) {
145
+ return request(connectorsBase(collectionId));
146
+ }
147
+ integrations.listConnectors = listConnectors;
148
+ /** Save a connector's non-secret Connect-form values and/or settings. PUT /integrations/connectors/:appId/installation */
149
+ async function saveConnectorInstallation(collectionId, appId, input) {
150
+ return put(`${connectorsBase(collectionId)}/${enc(appId)}/installation`, input);
151
+ }
152
+ integrations.saveConnectorInstallation = saveConnectorInstallation;
153
+ /**
154
+ * Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` →
155
+ * ConnectorTargetSchema, `sample` → ConnectorRecords, `transform` (a dry run) → ConnectorTransformResult.
156
+ * The server adds the installation; pass `flowId` to layer that flow's settings.
157
+ * POST /integrations/connectors/:appId/steps/:step
158
+ */
159
+ async function runConnectorStep(collectionId, appId, step, input = {}) {
160
+ return post(`${connectorsBase(collectionId)}/${enc(appId)}/steps/${enc(step)}`, input);
161
+ }
162
+ integrations.runConnectorStep = runConnectorStep;
138
163
  })(integrations || (integrations = {}));
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.0.56 | Generated: 2026-10-12T20:58:59.335Z
3
+ Version: 2.0.57 | Generated: 2026-10-13T11:42:19.247Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -65,6 +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), field mappings, and the Syndigo/Event Hub outbound path
68
+ - **[Connectors](connectors.md)** - Connector apps: a manifest `connector` block + server functions (testConnection/describeTarget/sample/transform/send/fetch) that the integration engine and console call for one external provider
68
69
  - **[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
69
70
  - **[Deep Link Discovery](deep-link-discovery.md)** - Registering and discovering navigable app states for portal menus and AI orchestration
70
71
  - **[AI Guide Template](ai-guide-template.md)** - A sample for an app on how to build an AI setup guide
@@ -2853,10 +2854,156 @@ interface AppManifest {
2853
2854
  realtime?: { channels: RealtimeChannelDeclaration[] };
2854
2855
  data?: AppDataDeclaration;
2855
2856
  headless?: AppHeadlessDeclaration;
2857
+ connector?: AppConnectorDeclaration;
2856
2858
  [key: string]: any;
2857
2859
  }
2858
2860
  ```
2859
2861
 
2862
+ **ConnectorConnectionField** (interface)
2863
+ ```typescript
2864
+ interface ConnectorConnectionField {
2865
+ name: string
2866
+ label: string
2867
+ description?: string
2868
+ secret?: string
2869
+ required?: boolean
2870
+ type?: 'text' | 'url' | 'select' | 'number' | 'boolean'
2871
+ options?: Array<{ value: string; label: string }>
2872
+ placeholder?: string
2873
+ }
2874
+ ```
2875
+
2876
+ **ConnectorPreset** (interface)
2877
+ ```typescript
2878
+ interface ConnectorPreset {
2879
+ id: string
2880
+ title: string
2881
+ description?: string
2882
+ direction: 'inbound' | 'outbound'
2883
+ entity: string
2884
+ flow?: Record<string, unknown>
2885
+ }
2886
+ ```
2887
+
2888
+ **ConnectorPanel** (interface)
2889
+ ```typescript
2890
+ interface ConnectorPanel {
2891
+ id: string
2892
+ title: string
2893
+ component: string
2894
+ }
2895
+ ```
2896
+
2897
+ **AppConnectorDeclaration** (interface)
2898
+ ```typescript
2899
+ interface AppConnectorDeclaration {
2900
+ provider: string
2901
+ title?: string
2902
+ directions: Array<'inbound' | 'outbound'>
2903
+ entities: string[]
2904
+ connection?: { fields: ConnectorConnectionField[] }
2905
+ settings?: Record<string, unknown>
2906
+ presets?: ConnectorPreset[]
2907
+ functions: {
2908
+ testConnection?: string
2909
+ describeTarget?: string
2910
+ sample?: string
2911
+ transform?: string
2912
+ send?: string
2913
+ fetch?: string
2914
+ }
2915
+ panels?: ConnectorPanel[]
2916
+ aiGuide?: string
2917
+ }
2918
+ ```
2919
+
2920
+ **ConnectorInstallation** (interface)
2921
+ ```typescript
2922
+ interface ConnectorInstallation {
2923
+ collectionId: string
2924
+ appId: string
2925
+ flowId?: string | null
2926
+ connection: Record<string, unknown>
2927
+ settings: Record<string, unknown>
2928
+ }
2929
+ ```
2930
+
2931
+ **ConnectorTestResult** (interface)
2932
+ ```typescript
2933
+ interface ConnectorTestResult {
2934
+ ok: boolean
2935
+ message: string
2936
+ }
2937
+ ```
2938
+
2939
+ **ConnectorTargetField** (interface)
2940
+ ```typescript
2941
+ interface ConnectorTargetField {
2942
+ path: string
2943
+ label?: string
2944
+ type?: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array'
2945
+ required?: boolean
2946
+ description?: string
2947
+ enum?: string[]
2948
+ }
2949
+ ```
2950
+
2951
+ **ConnectorTargetSchema** (interface)
2952
+ ```typescript
2953
+ interface ConnectorTargetSchema {
2954
+ fields: ConnectorTargetField[]
2955
+ }
2956
+ ```
2957
+
2958
+ **ConnectorRecords** (interface)
2959
+ ```typescript
2960
+ interface ConnectorRecords {
2961
+ records: Array<Record<string, unknown>>
2962
+ nextCursor?: string | null
2963
+ }
2964
+ ```
2965
+
2966
+ **ConnectorTransformInput** (interface)
2967
+ ```typescript
2968
+ interface ConnectorTransformInput {
2969
+ installation: ConnectorInstallation
2970
+ records: Array<Record<string, unknown>>
2971
+ mapping: FieldMapping[]
2972
+ }
2973
+ ```
2974
+
2975
+ **ConnectorTransformResult** (interface)
2976
+ ```typescript
2977
+ interface ConnectorTransformResult {
2978
+ payloads: Array<unknown | null>
2979
+ skipped?: Array<{ index: number; reason: string }>
2980
+ }
2981
+ ```
2982
+
2983
+ **ConnectorSendInput** (interface)
2984
+ ```typescript
2985
+ interface ConnectorSendInput {
2986
+ installation: ConnectorInstallation
2987
+ payloads: unknown[]
2988
+ }
2989
+ ```
2990
+
2991
+ **ConnectorSendResult** (interface)
2992
+ ```typescript
2993
+ interface ConnectorSendResult {
2994
+ results: Array<{ index: number; ok: boolean; status?: number; error?: string; externalId?: string }>
2995
+ }
2996
+ ```
2997
+
2998
+ **ConnectorFetchInput** (interface)
2999
+ ```typescript
3000
+ interface ConnectorFetchInput {
3001
+ installation: ConnectorInstallation
3002
+ cursor?: string | null
3003
+ limit?: number
3004
+ }
3005
+ ```
3006
+
2860
3007
  **CollectionAppWidget** (interface)
2861
3008
  ```typescript
2862
3009
  interface CollectionAppWidget {
@@ -6899,11 +7046,21 @@ interface FlowConnection {
6899
7046
  }
6900
7047
  ```
6901
7048
 
7049
+ **FlowConnectorRef** (interface)
7050
+ ```typescript
7051
+ interface FlowConnectorRef {
7052
+ appId: string
7053
+ preset?: string
7054
+ settings?: Record<string, unknown>
7055
+ }
7056
+ ```
7057
+
6902
7058
  **IntegrationFlowConfig** (interface)
6903
7059
  ```typescript
6904
7060
  interface IntegrationFlowConfig {
6905
7061
  connection?: FlowConnection
6906
7062
  fieldMappings?: FieldMapping[]
7063
+ connector?: FlowConnectorRef
6907
7064
  [key: string]: any
6908
7065
  }
6909
7066
  ```
@@ -6982,13 +7139,37 @@ interface RunFlowSummary {
6982
7139
  flowId: string
6983
7140
  direction: FlowDirection
6984
7141
  records: number
6985
- sent: number
7142
+ sent?: number
6986
7143
  failed: number
7144
+ created?: number
7145
+ updated?: number
7146
+ skipped?: number
6987
7147
  status: RunStatus
6988
7148
  runId?: string
6989
7149
  }
6990
7150
  ```
6991
7151
 
7152
+ **InstalledConnector** (interface)
7153
+ ```typescript
7154
+ interface InstalledConnector {
7155
+ appId: string
7156
+ name: string
7157
+ channel: string
7158
+ version: string | null
7159
+ connector: import('./appManifest').AppConnectorDeclaration
7160
+ installation: { connection: Record<string, unknown>; settings: Record<string, unknown> }
7161
+ secrets: Record<string, { set: boolean; hint?: string | null }>
7162
+ }
7163
+ ```
7164
+
7165
+ **SaveConnectorInstallationInput** (interface)
7166
+ ```typescript
7167
+ interface SaveConnectorInstallationInput {
7168
+ connection?: Record<string, unknown>
7169
+ settings?: Record<string, unknown>
7170
+ }
7171
+ ```
7172
+
6992
7173
  **CapturedRequest** (interface)
6993
7174
  ```typescript
6994
7175
  interface CapturedRequest {
@@ -7193,6 +7374,8 @@ interface RecordTypesResponse {
7193
7374
 
7194
7375
  **UpdateFlowInput** = `Partial<Omit<CreateFlowInput, 'direction'>> & {`
7195
7376
 
7377
+ **ConnectorConsoleStep** = `'testConnection' | 'describeTarget' | 'sample' | 'transform'`
7378
+
7196
7379
  **RunTrigger** = `'manual' | 'event' | 'schedule' | 'test'`
7197
7380
 
7198
7381
  **FlowRunStatus** = `'running' | 'success' | 'partial' | 'failed' | 'skipped'`
@@ -11507,6 +11690,20 @@ Enable enhanced logging. POST /integrations/logging/:connectionId (omit ttlMinut
11507
11690
  **cancelLogging**(collectionId: string, connectionId: string) → `Promise<`
11508
11691
  Cancel enhanced logging now. DELETE /integrations/logging/:connectionId
11509
11692
 
11693
+ **listConnectors**(collectionId: string) → `Promise<`
11694
+ The collection's installed connector apps. GET /integrations/connectors
11695
+
11696
+ **saveConnectorInstallation**(collectionId: string,
11697
+ appId: string,
11698
+ input: SaveConnectorInstallationInput) → `Promise<`
11699
+ Save a connector's non-secret Connect-form values and/or settings. PUT /integrations/connectors/:appId/installation
11700
+
11701
+ **runConnectorStep**(collectionId: string,
11702
+ appId: string,
11703
+ step: ConnectorConsoleStep,
11704
+ input: Record<string, unknown> & { flowId?: string } = {}) → `Promise<T>`
11705
+ Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` → ConnectorTargetSchema, `sample` → ConnectorRecords, `transform` (a dry run) → ConnectorTransformResult. The server adds the installation; pass `flowId` to layer that flow's settings. POST /integrations/connectors/:appId/steps/:step
11706
+
11510
11707
  ### interactions
11511
11708
 
11512
11709
  **query**(collectionId: string,
@@ -436,6 +436,13 @@ this is just the manifest shape.
436
436
  | `definitions[].authority` | string | `caller` \| `collection` — whose authority it runs as |
437
437
  | `definitions[].capabilities` | string[] | Least-privilege grants (`sl:<res>:<read\|write>`, `network[:<host>]`, `secrets:<ref>`) |
438
438
 
439
+ #### `connector`
440
+
441
+ Marks the app as an integration **connector** for one external provider: the integrations console
442
+ lists it and draws its connect form and settings, and the integration engine calls its server
443
+ functions (`testConnection`, `describeTarget`, `sample`, `transform`, `send`, `fetch`). The full
444
+ contract is in the **[Connectors guide](connectors.md)**.
445
+
439
446
  ---
440
447
 
441
448
  ## `app.admin.json`
@@ -0,0 +1,204 @@
1
+ # Integration connectors
2
+
3
+ A **connector** is an app that knows one external provider (a PIM, a content network, a shop
4
+ platform…). It is installed on a collection like any app, versioned and released like any app, and
5
+ it adds a manifest `connector` block plus a handful of [server functions](server-functions.md) the
6
+ integration engine and the integrations console call by convention.
7
+
8
+ Three layers, each with one job:
9
+
10
+ | Layer | Holds |
11
+ |---|---|
12
+ | **Engine** (the API) | [Integration flows](integrations.md), runs and their logs, triggers (events, schedule, Run now), the secret store, the deterministic field mapper, generic transports (HTTP, Event Hub). No provider code. |
13
+ | **Integrations console** (a core app) | The one admin screen: installed connectors, connect forms, settings, the mapper with AI assist, flows, runs. |
14
+ | **Connector apps** | One per provider: its connect form, settings, target schema, transform, send/fetch, and any provider-specific panels. |
15
+
16
+ A flow that only posts JSON to a webhook or an Event Hub needs no connector; it uses the engine's
17
+ generic transports (see [integrations.md](integrations.md)).
18
+
19
+ **No AI at run time.** The AI helps an admin *author* a mapping in the console (from the connector's
20
+ target schema plus real sample records); a person approves it, and it is saved as data and runs
21
+ deterministically. A connector's `transform` must be deterministic too.
22
+
23
+ ---
24
+
25
+ ## The manifest block
26
+
27
+ ```jsonc
28
+ // app.manifest.json
29
+ "connector": {
30
+ "provider": "oneworldsync",
31
+ "title": "1WorldSync",
32
+ "directions": ["inbound"],
33
+ "entities": ["product"],
34
+ "connection": {
35
+ "fields": [
36
+ { "name": "appId", "label": "App ID", "secret": "oneworldsync-app-id", "required": true },
37
+ { "name": "secretKey", "label": "Secret key", "secret": "oneworldsync-secret-key", "required": true },
38
+ { "name": "environment", "label": "Environment", "type": "select",
39
+ "options": [ { "value": "preprod", "label": "Pre-production" }, { "value": "production", "label": "Production" } ] }
40
+ ]
41
+ },
42
+ "settings": { // JSON Schema → a form in the console
43
+ "type": "object",
44
+ "properties": { "targetMarket": { "type": "string", "default": "GB" } }
45
+ },
46
+ "presets": [
47
+ { "id": "import-products", "title": "Import products", "direction": "inbound", "entity": "product" }
48
+ ],
49
+ "functions": { // names of this app's server functions
50
+ "testConnection": "testConnection",
51
+ "describeTarget": "describeTarget",
52
+ "sample": "sample",
53
+ "fetch": "fetch"
54
+ },
55
+ "panels": [ { "id": "explorer", "title": "Explorer", "component": "Explorer" } ],
56
+ "aiGuide": "connector-ai-guide.md"
57
+ },
58
+ "functions": {
59
+ "files": { "js": { "umd": "dist/functions.umd.js" } },
60
+ "definitions": [
61
+ { "name": "testConnection", "trigger": { "type": "http", "method": "POST" },
62
+ "visibility": "admin", "authority": "collection",
63
+ "capabilities": ["network:content1-api.1worldsync.com", "secrets:oneworldsync-app-id", "secrets:oneworldsync-secret-key"] }
64
+ // …one definition per function named above
65
+ ]
66
+ }
67
+ ```
68
+
69
+ Types: `AppConnectorDeclaration` and friends in `@proveanything/smartlinks` (`src/types/appManifest.ts`).
70
+
71
+ | Field | Meaning |
72
+ |---|---|
73
+ | `provider` | Stable provider key (`syndigo`, `oneworldsync`). |
74
+ | `title` | Provider display name; defaults to the app name. |
75
+ | `directions` | `inbound` (provider → SmartLinks), `outbound` (SmartLinks → provider), or both. |
76
+ | `entities` | SmartLinks entities it maps, e.g. `["product"]`. |
77
+ | `connection.fields[]` | The console's **Connect** form. A field with `secret` is stored as this app's secret of that name (write-only; never returned to the browser). Other fields are saved in the installation's connection settings. |
78
+ | `settings` | JSON Schema for installation settings; the console renders it as a form. |
79
+ | `presets[]` | Ready-made flows ("Products → provider") with flow defaults. |
80
+ | `functions` | Which of this app's server functions implement each step (below). Fill in only what applies. |
81
+ | `panels[]` | Provider-specific screens the console mounts (an API explorer, a status page). |
82
+ | `aiGuide` | A guide the mapping assistant reads: the provider's quirks, required fields, good defaults. |
83
+
84
+ ---
85
+
86
+ ## The functions
87
+
88
+ Each step names one of the app's server functions. Declare them as **http POST, `visibility:
89
+ "admin"`, `authority: "collection"`**: the engine calls them with no signed-in user (a product edit, a
90
+ schedule), and the console calls the same functions while an admin configures the flow
91
+ (`functions.callAdmin(collectionId, name, input, { appId })`). The input arrives as `event.body`;
92
+ return the result as JSON. Capabilities still cap what each one can do: declare the provider's hosts
93
+ (`network:<host>`) and the secrets it reads (`secrets:<name>`).
94
+
95
+ | Step | Called by | Input (`event.body`) | Returns |
96
+ |---|---|---|---|
97
+ | `testConnection` | Connect form, **Test** | `{ installation }` | `ConnectorTestResult` `{ ok, message }` |
98
+ | `describeTarget` | Mapper opens | `{ installation }` | `ConnectorTargetSchema` `{ fields: [{ path, label, type, required, enum }] }`: the provider's fields *for this customer* |
99
+ | `sample` | Mapper, AI assist, dry run | `{ installation, limit }` | `ConnectorRecords` `{ records }`: real provider records |
100
+ | `transform` | Every run | `ConnectorTransformInput` `{ installation, records, mapping }` | `ConnectorTransformResult` `{ payloads, skipped }`: one payload per record, same order; `null` skips one |
101
+ | `send` | Every outbound run | `ConnectorSendInput` `{ installation, payloads }` | `ConnectorSendResult` `{ results: [{ index, ok, status, error, externalId }] }` |
102
+ | `fetch` | Every inbound run | `ConnectorFetchInput` `{ installation, cursor, limit }` | `ConnectorRecords` `{ records, nextCursor }`: one page per call |
103
+
104
+ What happens when a step is left out:
105
+
106
+ - **No `transform`:** the engine's own mapper runs the flow's `fieldMappings` (direct, static, Liquid
107
+ template).
108
+ - **No `send`:** the flow's own transport: HTTP (`config.connection`), or Azure Event Hub with
109
+ `config.transport: 'eventhub'` and `config.eventHub`.
110
+ - **No `describeTarget`:** the mapper offers free-text target paths.
111
+
112
+ ### `installation`
113
+
114
+ Every step receives the installation it serves:
115
+
116
+ ```ts
117
+ interface ConnectorInstallation {
118
+ collectionId: string
119
+ appId: string // the connector app
120
+ flowId?: string | null // when called for a flow
121
+ connection: Record<string, unknown> // non-secret Connect-form fields
122
+ settings: Record<string, unknown> // installation settings, with the flow's overrides on top
123
+ }
124
+ ```
125
+
126
+ Secrets are never in it. Read them in the function with `ctx.secrets.get('<name>')`, which needs the
127
+ matching `secrets:<name>` capability.
128
+
129
+ ### Example: `fetch`
130
+
131
+ ```ts
132
+ // src/functions/index.ts (the app's functions bundle)
133
+ import type { ConnectorFetchInput, ConnectorRecords } from '@proveanything/smartlinks'
134
+
135
+ export async function fetch(ctx: any, event: any): Promise<ConnectorRecords> {
136
+ const { installation, cursor, limit = 50 } = event.body as ConnectorFetchInput
137
+ const appId = await ctx.secrets.get('provider-app-id')
138
+ const key = await ctx.secrets.get('provider-secret-key')
139
+ const res = await ctx.fetch(buildSignedUrl(installation, { cursor, limit }, appId, key))
140
+ const page = await res.json()
141
+ return { records: page.items, nextCursor: page.next ?? null }
142
+ }
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Where configuration lives
148
+
149
+ | What | Where |
150
+ |---|---|
151
+ | Connect-form values (non-secret), installation settings | The connector app's config on the collection: `appConfiguration` for `appId`, under `connector.connection` and `connector.settings`. |
152
+ | Credentials | The connector app's secrets, by the names its fields declare. |
153
+ | One flow's use of the connector | The flow: `config.connector = { appId, preset?, settings? }` (per-flow settings override the installation's), plus `config.fieldMappings`. |
154
+
155
+ ```ts
156
+ await integrations.createFlow(collectionId, {
157
+ appId: 'core-integrations',
158
+ direction: 'inbound',
159
+ name: 'Import products from 1WorldSync',
160
+ status: 'active',
161
+ schedule: '0 * * * *',
162
+ targetEntity: 'product',
163
+ config: {
164
+ connector: { appId: 'oneworldsync', preset: 'import-products' },
165
+ fieldMappings: [ { targetPath: 'name', sourcePath: 'item.tradeItemDescription', transformType: 'direct' } ],
166
+ },
167
+ })
168
+ ```
169
+
170
+ ---
171
+
172
+ ## From the console
173
+
174
+ ```ts
175
+ import { integrations, secrets } from '@proveanything/smartlinks'
176
+
177
+ const { connectors } = await integrations.listConnectors(collectionId)
178
+ // → [{ appId, name, version, connector, installation: { connection, settings }, secrets: { 'provider-key': { set, hint } } }]
179
+
180
+ await secrets.put(collectionId, 'provider-key', value, { appId }) // a secret field
181
+ await integrations.saveConnectorInstallation(collectionId, appId, { connection: { environment: 'production' } })
182
+ const test = await integrations.runConnectorStep(collectionId, appId, 'testConnection')
183
+ const target = await integrations.runConnectorStep(collectionId, appId, 'describeTarget')
184
+ const dryRun = await integrations.runConnectorStep(collectionId, appId, 'transform', { records, mapping })
185
+ ```
186
+
187
+ `send` and `fetch` only run inside a flow. A flow run's summary carries `sent` (outbound) or
188
+ `created` / `updated` / `skipped` (inbound), and every run, inbound or outbound, is in the run history.
189
+
190
+ ---
191
+
192
+ ## Panels
193
+
194
+ A connector can contribute screens to the integrations console (an API explorer, say). Each
195
+ `panels[]` entry names a component exported from the app's widgets bundle; the console mounts it host-side and passes it the
196
+ collection and the installation. Panels are admin-only and call the connector's own functions.
197
+
198
+ ---
199
+
200
+ ## Who can build connectors
201
+
202
+ First-party connectors run in-process, like any app server function. Third-party connectors will run
203
+ in the isolated runner (see [server-functions.md](server-functions.md)) once that opens up; the contract
204
+ does not change.
@@ -15,6 +15,10 @@ Flows are triggered three ways, all converging on the same executor:
15
15
  automatically when that entity changes.
16
16
  - **schedule** — a flow carrying a cron/interval `schedule` is run by the scan job. *(next)*
17
17
 
18
+ Provider-specific logic (a provider's payload format, its API, its connect form) lives in a
19
+ **connector app**, not in the engine: see **[Connectors](connectors.md)**. A flow uses one with
20
+ `config.connector = { appId }`.
21
+
18
22
  Credentials are **never** stored on the flow. The connection holds an opaque
19
23
  `credentialRef` into the **sealed-secret store** (`secrets` namespace); the value is sealed
20
24
  at rest and resolved server-side only, at execution.
@@ -94,6 +94,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
94
94
  | **Lots** | `docs/lots.md` | Cross-SKU production "Lot" entity |
95
95
  | **Loyalty** | `docs/loyalty.md` | Points, members, earning rules |
96
96
  | **Integrations** | `docs/integrations.md` | Inbound/outbound external-system integration flows |
97
+ | **Connectors** | `docs/connectors.md` | Connector apps: the manifest `connector` block + the functions the integration engine calls |
97
98
  | **Item Context** | `docs/item-context.md` | The `itemContext` container prop (serial / NFC authenticity context) |
98
99
  | **Native Facade** | `docs/native-facade.md` | `host.native` / `SL.native` device-capability facade |
99
100
  | **Product Facets** | `docs/PRODUCT_FACETS_SDK.md` | Facet API reference (admin + public endpoints, types) |