@fluentcommerce/fluent-mcp-extn 0.1.0 → 0.2.1
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/README.md +823 -818
- package/dist/fluent-client.js +12 -12
- package/dist/tools.js +17 -0
- package/docs/CONTRIBUTING.md +100 -100
- package/docs/E2E_TESTING.md +739 -739
- package/docs/HANDOVER_ENV.example +29 -29
- package/docs/HANDOVER_GITHUB_COPILOT.md +165 -165
- package/docs/RUNBOOK.md +312 -312
- package/docs/TOOL_REFERENCE.md +1810 -1810
- package/package.json +68 -68
- package/docs/IMPLEMENTATION_GUIDE.md +0 -299
package/docs/TOOL_REFERENCE.md
CHANGED
|
@@ -1,1810 +1,1810 @@
|
|
|
1
|
-
# Tool Reference
|
|
2
|
-
|
|
3
|
-
This document describes every MCP tool exposed by `fluent-mcp-extn`, including
|
|
4
|
-
purpose, required inputs, and example requests.
|
|
5
|
-
|
|
6
|
-
## Response envelope conventions
|
|
7
|
-
|
|
8
|
-
### Success
|
|
9
|
-
|
|
10
|
-
```json
|
|
11
|
-
{
|
|
12
|
-
"ok": true
|
|
13
|
-
}
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Most tools include extra fields like `event`, `response`, `job`, or `status`.
|
|
17
|
-
|
|
18
|
-
### Failure
|
|
19
|
-
|
|
20
|
-
```json
|
|
21
|
-
{
|
|
22
|
-
"ok": false,
|
|
23
|
-
"error": {
|
|
24
|
-
"code": "VALIDATION_ERROR",
|
|
25
|
-
"message": "Invalid tool arguments.",
|
|
26
|
-
"retryable": false,
|
|
27
|
-
"details": {}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
`error.retryable` indicates whether a caller can safely retry the same request. Non-idempotent writes intentionally avoid automatic retry behavior.
|
|
33
|
-
|
|
34
|
-
## `config.validate`
|
|
35
|
-
|
|
36
|
-
Validates configuration and auth readiness without calling Fluent APIs.
|
|
37
|
-
|
|
38
|
-
- **Input**: no fields
|
|
39
|
-
- **Use when**: startup checks, CI preflight, environment validation
|
|
40
|
-
|
|
41
|
-
Example:
|
|
42
|
-
|
|
43
|
-
```json
|
|
44
|
-
{}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## `health.ping`
|
|
48
|
-
|
|
49
|
-
Returns server health, SDK availability, and safe config summary.
|
|
50
|
-
|
|
51
|
-
- **Input**: no fields
|
|
52
|
-
- **Use when**: quick readiness checks in clients/agents
|
|
53
|
-
|
|
54
|
-
Example:
|
|
55
|
-
|
|
56
|
-
```json
|
|
57
|
-
{}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## `event.build`
|
|
61
|
-
|
|
62
|
-
Builds a Fluent event payload only (no API call).
|
|
63
|
-
|
|
64
|
-
- **Required**: `name`, `entityRef`, `entityType`
|
|
65
|
-
- **Optional**: `entityId`, `rootEntityId`, `rootEntityType`, `source`, `type`, `attributes`, etc.
|
|
66
|
-
- **Note**: if available in your flow, provide both `entityRef` and `entityId`.
|
|
67
|
-
|
|
68
|
-
Example:
|
|
69
|
-
|
|
70
|
-
```json
|
|
71
|
-
{
|
|
72
|
-
"name": "ORDER_CREATED",
|
|
73
|
-
"entityRef": "ORD-1001",
|
|
74
|
-
"entityId": "12345",
|
|
75
|
-
"entityType": "ORDER",
|
|
76
|
-
"attributes": {
|
|
77
|
-
"channel": "web"
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## `event.send`
|
|
83
|
-
|
|
84
|
-
Builds and sends an event via SDK adapter.
|
|
85
|
-
|
|
86
|
-
- **Required**: `name`, `entityRef`, `entityType`
|
|
87
|
-
- **Optional**: all `event.build` fields plus:
|
|
88
|
-
- `mode`: `async` or `sync`
|
|
89
|
-
- `dryRun`: boolean
|
|
90
|
-
- **Important**: `retailerId` must match the target entity's retailer for real sends.
|
|
91
|
-
|
|
92
|
-
Example dry-run:
|
|
93
|
-
|
|
94
|
-
```json
|
|
95
|
-
{
|
|
96
|
-
"name": "ORDER_CREATED",
|
|
97
|
-
"entityRef": "ORD-1001",
|
|
98
|
-
"entityId": "12345",
|
|
99
|
-
"entityType": "ORDER",
|
|
100
|
-
"dryRun": true
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Example send:
|
|
105
|
-
|
|
106
|
-
```json
|
|
107
|
-
{
|
|
108
|
-
"name": "ORDER_CREATED",
|
|
109
|
-
"entityRef": "ORD-1001",
|
|
110
|
-
"entityId": "12345",
|
|
111
|
-
"entityType": "ORDER",
|
|
112
|
-
"mode": "async",
|
|
113
|
-
"dryRun": false
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## `event.get`
|
|
118
|
-
|
|
119
|
-
Gets one event by ID via SDK-native `getEventById(eventId)`:
|
|
120
|
-
|
|
121
|
-
- endpoint: `/api/v4.1/event/{id}`
|
|
122
|
-
- **Required**: `eventId`
|
|
123
|
-
|
|
124
|
-
Example:
|
|
125
|
-
|
|
126
|
-
```json
|
|
127
|
-
{
|
|
128
|
-
"eventId": "7815ee32-5985-485f-863a-2643e57b64a2"
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Typical success shape (truncated):
|
|
133
|
-
|
|
134
|
-
```json
|
|
135
|
-
{
|
|
136
|
-
"ok": true,
|
|
137
|
-
"event": {
|
|
138
|
-
"id": "7815ee32-5985-485f-863a-2643e57b64a2",
|
|
139
|
-
"name": "ORDER_CREATED",
|
|
140
|
-
"entityRef": "ORD-1001",
|
|
141
|
-
"entityType": "ORDER",
|
|
142
|
-
"retailerId": "5"
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## `event.list`
|
|
148
|
-
|
|
149
|
-
Lists/filter events via SDK-native `getEvents(params)`:
|
|
150
|
-
|
|
151
|
-
- endpoint: `/api/v4.1/event`
|
|
152
|
-
- **Canonical filters**:
|
|
153
|
-
- `eventId`, `name`, `category`, `retailerId`
|
|
154
|
-
- `eventType`, `eventStatus`, `from`, `to`
|
|
155
|
-
- `start`, `count`
|
|
156
|
-
- `context.rootEntityType`, `context.rootEntityId`, `context.rootEntityRef`
|
|
157
|
-
- `context.entityType`, `context.entityId`, `context.entityRef`, `context.sourceEvents`
|
|
158
|
-
- **Alias filters** (mapped internally):
|
|
159
|
-
- `id` -> `eventId`
|
|
160
|
-
- `entityRef` -> `context.entityRef`
|
|
161
|
-
- `entityType` -> `context.entityType`
|
|
162
|
-
- `type` -> `eventType`
|
|
163
|
-
- **`context.sourceEvents` handling**:
|
|
164
|
-
- accepts array input in tool arguments
|
|
165
|
-
- serialized for query-string delivery to Event API
|
|
166
|
-
|
|
167
|
-
Example:
|
|
168
|
-
|
|
169
|
-
```json
|
|
170
|
-
{
|
|
171
|
-
"eventType": "ORCHESTRATION_AUDIT",
|
|
172
|
-
"eventStatus": "FAILED",
|
|
173
|
-
"context.rootEntityType": "ORDER",
|
|
174
|
-
"context.rootEntityRef": "ORD-1001",
|
|
175
|
-
"retailerId": "5",
|
|
176
|
-
"from": "2026-02-01T00:00:00.000Z",
|
|
177
|
-
"to": "2026-02-15T23:59:59.999Z",
|
|
178
|
-
"count": 25,
|
|
179
|
-
"start": 1
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Typical success shape (truncated):
|
|
184
|
-
|
|
185
|
-
```json
|
|
186
|
-
{
|
|
187
|
-
"ok": true,
|
|
188
|
-
"events": {
|
|
189
|
-
"start": 1,
|
|
190
|
-
"count": 25,
|
|
191
|
-
"hasMore": true,
|
|
192
|
-
"results": [
|
|
193
|
-
{ "id": "event-id-1", "name": "ORDER_CREATED" }
|
|
194
|
-
]
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
If `event.send` does not return an `id`, use `event.list` with `name`,
|
|
200
|
-
`entityRef`, and `retailerId` to resolve the latest event ID, then call
|
|
201
|
-
`event.get`.
|
|
202
|
-
|
|
203
|
-
## `event.flowInspect`
|
|
204
|
-
|
|
205
|
-
One-call runtime forensics for any root entity.
|
|
206
|
-
|
|
207
|
-
- **Required**: `rootEntityRef`
|
|
208
|
-
- **Optional**:
|
|
209
|
-
- `rootEntityType`: optional entity type filter (for example `ORDER`, `FULFILMENT`, `LOCATION`, `WAVE`, `PRODUCT`)
|
|
210
|
-
- `rootEntityId`: optional root entity ID (helps disambiguate reused refs)
|
|
211
|
-
- `compact`: return pre-analyzed summary instead of raw arrays (default `true`). Compact mode returns ~2-3k tokens with an `analysis` section; set to `false` for full ~24k raw data.
|
|
212
|
-
- `from`, `to`: optional time window
|
|
213
|
-
- `maxPages`: page cap per event type (`1..50`, default `10`)
|
|
214
|
-
- `includeEventDetails`: include compact orchestration event rows (default `true`, full mode only)
|
|
215
|
-
- `includeAuditDetails`: include compact audit action samples (default `false`, full mode only)
|
|
216
|
-
- `includeScheduled`: fetch SCHEDULED events separately (default `true`)
|
|
217
|
-
- `inspectStatuses`: statuses to drill via `event.get` (defaults: `["NO_MATCH", "PENDING", "FAILED"]`)
|
|
218
|
-
- `maxDrilldowns`: cap on individual event.get calls (`0..100`, default `50`)
|
|
219
|
-
- `actionSampleLimit`: max rows for action-derived sections (`1..200`, default `100`)
|
|
220
|
-
- **Default-on flags:**
|
|
221
|
-
- `compact`: pre-analyzed summary response (default `true`)
|
|
222
|
-
- `includeExceptions`: rule exceptions with class, message, ruleset context (default `true`)
|
|
223
|
-
- `includeNoMatchDetails`: enhanced NO_MATCH diagnostics from ruleSet audit events with closeMatches (default `true`)
|
|
224
|
-
- **Opt-in flags (default false):**
|
|
225
|
-
- `includeRuleDetails`: per-rule execution trace with class name, props, and timing (durationMs)
|
|
226
|
-
- `includeCustomLogs`: custom plugin log messages (LogCollection from CUSTOM category)
|
|
227
|
-
- `includeSnapshots`: entity state snapshots at each processing point
|
|
228
|
-
- `includeCrossEntity`: fetch child entity events (FULFILMENT_CHOICE, FULFILMENT) using rootEntityRef
|
|
229
|
-
|
|
230
|
-
**Compact mode (`compact: true`, the default):**
|
|
231
|
-
|
|
232
|
-
Returns a pre-analyzed summary with:
|
|
233
|
-
- `analysis.statusFlow`: ordered unique statuses seen (e.g., `["CREATED", "BOOKED", "SHIPPED"]`)
|
|
234
|
-
- `analysis.findings[]`: anomaly detection — NO_MATCH (CRITICAL), FAILED/webhook errors/exceptions (HIGH), PENDING/slow rulesets (MEDIUM)
|
|
235
|
-
- `analysis.failedWebhookEndpoints`: URLs that returned >= 400
|
|
236
|
-
- `analysis.slowestRulesets`: top 3 by duration
|
|
237
|
-
- `analysis.timespan`: first/last timestamps with durationMs
|
|
238
|
-
- `audit.webhookActions`: only failures (responseCode >= 400)
|
|
239
|
-
- `audit.mutationActions`: top 5 by queryName (name + count only)
|
|
240
|
-
- `audit.sendEventActions`: count + scheduledCount only
|
|
241
|
-
- `diagnostics.inspectedEvents`: summary (id, name, status, closeMatchCount)
|
|
242
|
-
|
|
243
|
-
**Full mode (`compact: false`):**
|
|
244
|
-
|
|
245
|
-
Returns complete raw arrays:
|
|
246
|
-
- Orchestration timeline counts (`status`, `entityType`, top names)
|
|
247
|
-
- Audit category/action breakdown with ruleset durations
|
|
248
|
-
- Mutation payload evidence from ACTION audit (`DynamicUpdateMutation` request blocks)
|
|
249
|
-
- Webhook diagnostics (`Request Endpoint`, `Response code`, `Response Body`, headers)
|
|
250
|
-
- SendEvent payloads and future-dated scheduling evidence
|
|
251
|
-
- Rule exceptions (when `includeExceptions: true`)
|
|
252
|
-
- NO_MATCH closeMatches with mismatch reasons (when `includeNoMatchDetails: true`)
|
|
253
|
-
- Per-rule execution with props/timing (when `includeRuleDetails: true`)
|
|
254
|
-
- Custom plugin logs (when `includeCustomLogs: true`)
|
|
255
|
-
- Entity snapshots (when `includeSnapshots: true`)
|
|
256
|
-
- Cross-entity events with full events array (when `includeCrossEntity: true`)
|
|
257
|
-
- `NO_MATCH`/`PENDING`/`FAILED` drilldown with full event payload via `event.get`
|
|
258
|
-
- Auto-recommendations based on findings
|
|
259
|
-
|
|
260
|
-
Example (compact — recommended first call):
|
|
261
|
-
|
|
262
|
-
```json
|
|
263
|
-
{
|
|
264
|
-
"rootEntityRef": "E2E_MULTI_202602221343",
|
|
265
|
-
"rootEntityType": "ORDER"
|
|
266
|
-
}
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
Example (full data with all sections):
|
|
270
|
-
|
|
271
|
-
```json
|
|
272
|
-
{
|
|
273
|
-
"rootEntityRef": "ORD-001",
|
|
274
|
-
"rootEntityType": "ORDER",
|
|
275
|
-
"compact": false,
|
|
276
|
-
"includeRuleDetails": true,
|
|
277
|
-
"includeCustomLogs": true,
|
|
278
|
-
"includeSnapshots": true,
|
|
279
|
-
"includeCrossEntity": true
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Example (lightweight):
|
|
284
|
-
|
|
285
|
-
```json
|
|
286
|
-
{
|
|
287
|
-
"rootEntityRef": "ORD-001",
|
|
288
|
-
"rootEntityType": "ORDER",
|
|
289
|
-
"includeEventDetails": false,
|
|
290
|
-
"includeExceptions": false,
|
|
291
|
-
"includeNoMatchDetails": false,
|
|
292
|
-
"maxDrilldowns": 0
|
|
293
|
-
}
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
## `metrics.query`
|
|
297
|
-
|
|
298
|
-
Queries Prometheus metrics for Fluent runtime telemetry.
|
|
299
|
-
|
|
300
|
-
- **Required**: `query` (PromQL expression)
|
|
301
|
-
- **Optional**:
|
|
302
|
-
- `type`: `instant` (default) or `range`
|
|
303
|
-
- `time`: instant query timestamp
|
|
304
|
-
- `start`, `end`, `step`: required together for range queries
|
|
305
|
-
- `timeout`: query timeout in seconds (`1..120`)
|
|
306
|
-
- **Execution path**:
|
|
307
|
-
- instant -> GraphQL `metricInstant(query, time?)`
|
|
308
|
-
- range -> GraphQL `metricRange(query, start, end, step)`
|
|
309
|
-
- **Note**: `metrics.query` routes PromQL through Fluent's GraphQL proxy. Raw Prometheus REST endpoints are not relied on directly by this tool.
|
|
310
|
-
|
|
311
|
-
Label hygiene reminder:
|
|
312
|
-
- `core_event_received_total` does **not** include `status`
|
|
313
|
-
- `rubix_event_runtime_seconds_*` does include `status`
|
|
314
|
-
- histogram bucket series add `le`
|
|
315
|
-
|
|
316
|
-
Accurate counter delta pattern for wider windows:
|
|
317
|
-
|
|
318
|
-
```promql
|
|
319
|
-
(last_over_time(metric[window]) - metric offset <period>)
|
|
320
|
-
or
|
|
321
|
-
last_over_time(metric[window])
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
Use the fallback `or` arm when offset samples are missing.
|
|
325
|
-
|
|
326
|
-
Instant example:
|
|
327
|
-
|
|
328
|
-
```json
|
|
329
|
-
{
|
|
330
|
-
"query": "sum(rate(rubix_event_runtime_seconds_count[5m]))",
|
|
331
|
-
"type": "instant"
|
|
332
|
-
}
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
Range example:
|
|
336
|
-
|
|
337
|
-
```json
|
|
338
|
-
{
|
|
339
|
-
"query": "rate(rubix_event_runtime_seconds_count{status=\"FAILED\"}[5m])",
|
|
340
|
-
"type": "range",
|
|
341
|
-
"start": "2026-02-20T00:00:00Z",
|
|
342
|
-
"end": "2026-02-20T01:00:00Z",
|
|
343
|
-
"step": "1m"
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
## `metrics.topEvents`
|
|
348
|
-
|
|
349
|
-
Returns ranked event analytics for a time window by aggregating Event API results.
|
|
350
|
-
|
|
351
|
-
- **Required**: `from`
|
|
352
|
-
- **Optional**:
|
|
353
|
-
- `to`: end timestamp (defaults to now)
|
|
354
|
-
- `entityType`: filter to one entity type
|
|
355
|
-
- `eventType`: defaults to `ORCHESTRATION`
|
|
356
|
-
- `topN`: ranked rows to return (`1..100`, default `20`)
|
|
357
|
-
- `maxPages`: Event API pagination cap (`1..50`, default `10`)
|
|
358
|
-
- **Output summary**:
|
|
359
|
-
- `totalEvents`, `failureRate`, `statusBreakdown`
|
|
360
|
-
- `topEvents[]` grouped by `name + entityType + status`
|
|
361
|
-
- `uniqueEventNames`, `uniqueEntityTypes`
|
|
362
|
-
|
|
363
|
-
Example:
|
|
364
|
-
|
|
365
|
-
```json
|
|
366
|
-
{
|
|
367
|
-
"from": "2026-02-22T00:00:00Z",
|
|
368
|
-
"to": "2026-02-22T12:00:00Z",
|
|
369
|
-
"topN": 20,
|
|
370
|
-
"eventType": "ORCHESTRATION"
|
|
371
|
-
}
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
## `metrics.healthCheck`
|
|
375
|
-
|
|
376
|
-
Runs a compact, one-call anomaly assessment.
|
|
377
|
-
|
|
378
|
-
- **Optional**:
|
|
379
|
-
- `window`: Prometheus/Event API analysis window (`1h`, `6h`, `24h`, `7d`; default `1h`)
|
|
380
|
-
- `includeTopEvents`: include ranked event list in response (default `true`)
|
|
381
|
-
- `topN`: max top event rows (`1..100`, default `10`)
|
|
382
|
-
- `thresholds`: override defaults
|
|
383
|
-
- `failureRate` (default `5`)
|
|
384
|
-
- `pendingRate` (default `10`)
|
|
385
|
-
- `dominanceRate` (default `50`)
|
|
386
|
-
- **Behavior**:
|
|
387
|
-
- Prometheus-first (`metrics.query` equivalent calls under the hood)
|
|
388
|
-
- Falls back to Event API aggregation if Prometheus is unavailable
|
|
389
|
-
|
|
390
|
-
Example:
|
|
391
|
-
|
|
392
|
-
```json
|
|
393
|
-
{
|
|
394
|
-
"window": "1h",
|
|
395
|
-
"includeTopEvents": true,
|
|
396
|
-
"topN": 10
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
## `metrics.sloReport`
|
|
401
|
-
|
|
402
|
-
Returns a managed-services SLO snapshot with explicit KPI fields and threshold findings.
|
|
403
|
-
|
|
404
|
-
- **Optional**:
|
|
405
|
-
- `window`: KPI window (`30m`, `1h`, `24h`, `7d`; default `1h`)
|
|
406
|
-
- `includeTopFailingEvents`: include top failed events from Event API (default `true`)
|
|
407
|
-
- `topN`: top failed rows (`1..100`, default `10`)
|
|
408
|
-
- `maxPages`: Event API pagination cap for fallback/failed ranking (`1..50`, default `10`)
|
|
409
|
-
- `thresholds`: override defaults
|
|
410
|
-
- `failureRate` (default `5`)
|
|
411
|
-
- `noMatchRate` (default `0`)
|
|
412
|
-
- `pendingRate` (default `10`)
|
|
413
|
-
- `runtimeP95Seconds` (default `5`)
|
|
414
|
-
- `inflightP95Seconds` (default `60`)
|
|
415
|
-
- **Output highlights**:
|
|
416
|
-
- total/failed/no-match/pending event counts
|
|
417
|
-
- failure/no-match/pending rates
|
|
418
|
-
- p95 runtime and inflight latency (null when Prometheus fallback path is used)
|
|
419
|
-
- findings + recommendations + source (`prometheus` or `event_api`)
|
|
420
|
-
|
|
421
|
-
Example:
|
|
422
|
-
|
|
423
|
-
```json
|
|
424
|
-
{
|
|
425
|
-
"window": "1h",
|
|
426
|
-
"includeTopFailingEvents": true,
|
|
427
|
-
"topN": 10
|
|
428
|
-
}
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
## `metrics.labelCatalog`
|
|
432
|
-
|
|
433
|
-
Discovers what labels a metric supports so PromQL groupings are correct.
|
|
434
|
-
|
|
435
|
-
- **Required**: `metric`
|
|
436
|
-
- **Optional**:
|
|
437
|
-
- `window`: live series sampling window (`30m`, `1h`, `24h`, `7d`; default `24h`)
|
|
438
|
-
- `includeKnownLabels`: include Fluent-known label hints (default `true`)
|
|
439
|
-
- `maxValuesPerLabel`: max sample values returned per label (`1..50`, default `10`)
|
|
440
|
-
- **Behavior**:
|
|
441
|
-
- runs `last_over_time(<metric>[window])` as an instant query
|
|
442
|
-
- extracts label keys from returned vector series
|
|
443
|
-
- reports presence/cardinality/sample values per label
|
|
444
|
-
- merges known Fluent label hints when available
|
|
445
|
-
|
|
446
|
-
Example:
|
|
447
|
-
|
|
448
|
-
```json
|
|
449
|
-
{
|
|
450
|
-
"metric": "core_event_received_total",
|
|
451
|
-
"window": "1h",
|
|
452
|
-
"maxValuesPerLabel": 10
|
|
453
|
-
}
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## `workflow.transitions`
|
|
457
|
-
|
|
458
|
-
Query available user actions (transitions) for entities at a given workflow state.
|
|
459
|
-
|
|
460
|
-
Calls `POST /api/v4.1/transition` to discover what events can be fired, what attributes they require, and how they appear in the UI.
|
|
461
|
-
|
|
462
|
-
- **Required**: `triggers` (array), each trigger requires `retailerId`
|
|
463
|
-
- **Optional per trigger**: `type`, `subtype`, `status`, `module`, `flexType`, `flexVersion`, `name`
|
|
464
|
-
- **Retailer behavior**: `retailerId` is required per trigger. Falls back to `FLUENT_RETAILER_ID` when omitted.
|
|
465
|
-
|
|
466
|
-
### Use cases
|
|
467
|
-
|
|
468
|
-
- Discover available actions at any workflow status without reading workflow JSON
|
|
469
|
-
- Build dynamic E2E test sequences that adapt to workflow changes
|
|
470
|
-
- Validate that expected user actions are available after deployment
|
|
471
|
-
- Get required event attributes for each action (avoids missing-attribute errors)
|
|
472
|
-
|
|
473
|
-
### Integration with event.send
|
|
474
|
-
|
|
475
|
-
The `eventName` from each `userAction` maps directly to `event.send`'s `name` parameter. The `attributes[]` tell you what to include in `event.send`'s `attributes` parameter.
|
|
476
|
-
|
|
477
|
-
### Example: ORDER at CREATED status
|
|
478
|
-
|
|
479
|
-
```json
|
|
480
|
-
{
|
|
481
|
-
"triggers": [
|
|
482
|
-
{
|
|
483
|
-
"type": "ORDER",
|
|
484
|
-
"subtype": "HD",
|
|
485
|
-
"status": "CREATED",
|
|
486
|
-
"retailerId": "5",
|
|
487
|
-
"flexType": "ORDER::HD"
|
|
488
|
-
}
|
|
489
|
-
]
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### Example: MANIFEST with module filter
|
|
494
|
-
|
|
495
|
-
```json
|
|
496
|
-
{
|
|
497
|
-
"triggers": [
|
|
498
|
-
{
|
|
499
|
-
"type": "MANIFEST",
|
|
500
|
-
"subtype": "DEFAULT",
|
|
501
|
-
"status": "PENDING",
|
|
502
|
-
"module": "servicepoint",
|
|
503
|
-
"flexType": "CARRIER::DEFAULT",
|
|
504
|
-
"retailerId": "2"
|
|
505
|
-
}
|
|
506
|
-
]
|
|
507
|
-
}
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
Typical success shape (truncated):
|
|
511
|
-
|
|
512
|
-
```json
|
|
513
|
-
{
|
|
514
|
-
"ok": true,
|
|
515
|
-
"response": {
|
|
516
|
-
"response": [
|
|
517
|
-
{
|
|
518
|
-
"trigger": {
|
|
519
|
-
"type": "MANIFEST",
|
|
520
|
-
"subtype": "DEFAULT",
|
|
521
|
-
"status": "PENDING",
|
|
522
|
-
"module": "servicepoint",
|
|
523
|
-
"flexType": "CARRIER::DEFAULT",
|
|
524
|
-
"retailerId": "2"
|
|
525
|
-
},
|
|
526
|
-
"userActions": [
|
|
527
|
-
{
|
|
528
|
-
"eventName": "UPDATE",
|
|
529
|
-
"context": [
|
|
530
|
-
{ "label": "SUBMIT", "type": "PRIMARY", "modules": ["servicepoint"], "confirm": false }
|
|
531
|
-
],
|
|
532
|
-
"attributes": []
|
|
533
|
-
}
|
|
534
|
-
]
|
|
535
|
-
}
|
|
536
|
-
]
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
### Dynamic test sequence pattern
|
|
542
|
-
|
|
543
|
-
```
|
|
544
|
-
1. Create entity via graphql.query (mutation)
|
|
545
|
-
2. workflow.transitions → get available actions at current status
|
|
546
|
-
3. For each action:
|
|
547
|
-
a. event.send with eventName and required attributes from response
|
|
548
|
-
b. Poll entity status until transition completes
|
|
549
|
-
c. workflow.transitions → get next available actions
|
|
550
|
-
4. Repeat until no more userActions (terminal state)
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
## `plugin.list`
|
|
554
|
-
|
|
555
|
-
List all registered orchestration rules (standard + custom) with metadata.
|
|
556
|
-
|
|
557
|
-
Calls `GET /orchestration/rest/v1/plugin` and returns a map keyed by fully-qualified rule name (e.g. `ACCOUNT.context.RuleName`).
|
|
558
|
-
|
|
559
|
-
- **Optional**: `name` (string) — case-insensitive substring filter on rule keys
|
|
560
|
-
- **Requires**: SDK client with `request` method
|
|
561
|
-
|
|
562
|
-
### Each entry contains
|
|
563
|
-
|
|
564
|
-
- `ruleInfo`: name, description, accepted entity types, produced events
|
|
565
|
-
- `eventAttributes`: attributes the rule reads from events
|
|
566
|
-
- `parameters`: configurable rule parameters (with types and descriptions)
|
|
567
|
-
|
|
568
|
-
### Use cases
|
|
569
|
-
|
|
570
|
-
- Understand what rules do when analyzing workflows
|
|
571
|
-
- Discover all registered rules (standard + custom) for a given account
|
|
572
|
-
- Find rules by name pattern (e.g. all "SendEvent" variants)
|
|
573
|
-
- Cross-reference deployed rules with local source code for module validation
|
|
574
|
-
|
|
575
|
-
### Example: list all rules
|
|
576
|
-
|
|
577
|
-
```json
|
|
578
|
-
{}
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
### Example: filter by name
|
|
582
|
-
|
|
583
|
-
```json
|
|
584
|
-
{
|
|
585
|
-
"name": "SendEvent"
|
|
586
|
-
}
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
Typical success shape (truncated):
|
|
590
|
-
|
|
591
|
-
```json
|
|
592
|
-
{
|
|
593
|
-
"ok": true,
|
|
594
|
-
"response": {
|
|
595
|
-
"FLUENTRETAIL.base.SendEvent": {
|
|
596
|
-
"ruleInfo": {
|
|
597
|
-
"name": "SendEvent",
|
|
598
|
-
"description": "Sends an event to a specified entity",
|
|
599
|
-
"accepts": ["ORDER", "FULFILMENT", "ARTICLE"],
|
|
600
|
-
"produces": ["SendEvent"]
|
|
601
|
-
},
|
|
602
|
-
"eventAttributes": [
|
|
603
|
-
{ "name": "eventName", "type": "STRING", "description": "Name of the event to send" }
|
|
604
|
-
],
|
|
605
|
-
"parameters": [
|
|
606
|
-
{ "name": "eventName", "type": "STRING", "description": "Event name to dispatch" }
|
|
607
|
-
]
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
## `graphql.query`
|
|
614
|
-
|
|
615
|
-
Executes a Fluent Commerce GraphQL query or mutation through the SDK.
|
|
616
|
-
|
|
617
|
-
- **Required**: `query`
|
|
618
|
-
- **Optional**: `variables`
|
|
619
|
-
- **Retry behavior**:
|
|
620
|
-
- query operations use configured retry/backoff
|
|
621
|
-
- mutation operations disable retries automatically to avoid duplicate writes
|
|
622
|
-
|
|
623
|
-
### Basic Query
|
|
624
|
-
|
|
625
|
-
```json
|
|
626
|
-
{
|
|
627
|
-
"query": "query { orders(first: 5) { edges { cursor node { id ref status } } pageInfo { hasNextPage } } }"
|
|
628
|
-
}
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
### Pagination
|
|
632
|
-
|
|
633
|
-
Fluent uses Relay-style connections. Cursors live on each **edge**, not on `pageInfo`. There is no `endCursor` or `startCursor`.
|
|
634
|
-
|
|
635
|
-
```json
|
|
636
|
-
{
|
|
637
|
-
"query": "query($cursor: String) { orders(first: 50, after: $cursor) { edges { cursor node { id ref status } } pageInfo { hasNextPage } } }",
|
|
638
|
-
"variables": { "cursor": "Y3Vyc29yOi0tLTM2X18xNzcxMTc2MzMyMTc0" }
|
|
639
|
-
}
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
To paginate: take the `cursor` from the **last edge** in the response, pass it as the `after` variable. Repeat while `hasNextPage` is `true`.
|
|
643
|
-
|
|
644
|
-
Pagination args: `first`/`after` (forward), `last`/`before` (backward).
|
|
645
|
-
|
|
646
|
-
### Mutation
|
|
647
|
-
|
|
648
|
-
```json
|
|
649
|
-
{
|
|
650
|
-
"query": "mutation($input: UpdateOrderInput!) { updateOrder(input: $input) { id ref status } }",
|
|
651
|
-
"variables": { "input": { "id": "36", "status": "RECEIVED" } }
|
|
652
|
-
}
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
### Synchronous Fulfilment Options (Live Checkout)
|
|
656
|
-
|
|
657
|
-
`graphql.query` supports synchronous fulfilment options orchestration calls,
|
|
658
|
-
commonly used by checkout journeys to get plans in the same response.
|
|
659
|
-
|
|
660
|
-
```json
|
|
661
|
-
{
|
|
662
|
-
"query": "mutation CreateFulfilmentOption($retailerId: Int!, $type: String!, $orderType: String!, $ref: String!, $products: [CreateFulfilmentOptionProductInput!], $longitude: Float!, $latitude: Float!, $radius: Json!) { createFulfilmentOption(input: { retailerId: $retailerId, type: $type, orderType: $orderType, ref: $ref, products: $products, address: { city: \"Kellyville Ridge\", state: \"New South Wales\", country: \"Australia\", postcode: \"2155\", addressLine1: \"Kellyville Ridge NSW, Australia\", latitude: $latitude, longitude: $longitude }, attributes: { name: \"radius\", type: \"DOUBLE\", value: $radius } }, executionMode: AWAIT_ORCHESTRATION) { id createdOn plans { edges { node { ref status eta splitCount fulfilments { locationRef eta items { productRef availableQuantity requestedQuantity } } } } } } }",
|
|
663
|
-
"variables": {
|
|
664
|
-
"retailerId": 5,
|
|
665
|
-
"type": "CC",
|
|
666
|
-
"orderType": "CC",
|
|
667
|
-
"ref": "CHECKOUT-FO-12345",
|
|
668
|
-
"products": [
|
|
669
|
-
{ "productRef": "MP09-34-Blue", "requestedQuantity": 1, "catalogueRef": "" }
|
|
670
|
-
],
|
|
671
|
-
"longitude": 150.9193671,
|
|
672
|
-
"latitude": -33.7068442,
|
|
673
|
-
"radius": 50
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
Notes:
|
|
679
|
-
- `executionMode: AWAIT_ORCHESTRATION` makes the call synchronous.
|
|
680
|
-
- The environment must contain a matching workflow for `type` (for example `FULFILMENT_OPTIONS::CC`).
|
|
681
|
-
- If no matching workflow exists, Fluent returns a backend workflow-not-found error.
|
|
682
|
-
|
|
683
|
-
### Connection Shape
|
|
684
|
-
|
|
685
|
-
All list queries return connections:
|
|
686
|
-
|
|
687
|
-
```graphql
|
|
688
|
-
{
|
|
689
|
-
edges {
|
|
690
|
-
cursor
|
|
691
|
-
node { ...fields }
|
|
692
|
-
}
|
|
693
|
-
pageInfo { hasNextPage }
|
|
694
|
-
}
|
|
695
|
-
```
|
|
696
|
-
|
|
697
|
-
Common query roots: `orders`, `fulfilments`, `fulfilmentChoices`, `locations`, `inventoryPositions`, `products`, `categories`, `settings`, `waves`, `articles`.
|
|
698
|
-
|
|
699
|
-
## `batch.create`
|
|
700
|
-
|
|
701
|
-
Creates a Fluent ingestion job.
|
|
702
|
-
|
|
703
|
-
- **Required**: `name`
|
|
704
|
-
- **Optional**: `retailerId`, `entityType`, `action`
|
|
705
|
-
- **Note**: `retailerId` falls back to `FLUENT_RETAILER_ID` if omitted
|
|
706
|
-
|
|
707
|
-
Example:
|
|
708
|
-
|
|
709
|
-
```json
|
|
710
|
-
{
|
|
711
|
-
"name": "inventory-load-2026-02-06",
|
|
712
|
-
"entityType": "INVENTORY_POSITION",
|
|
713
|
-
"action": "UPSERT"
|
|
714
|
-
}
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
## `batch.send`
|
|
718
|
-
|
|
719
|
-
Sends payload records to an existing batch job.
|
|
720
|
-
|
|
721
|
-
- **Required**:
|
|
722
|
-
- `jobId`
|
|
723
|
-
- `payload.action`
|
|
724
|
-
- `payload.entityType`
|
|
725
|
-
- `payload.entities` (non-empty array)
|
|
726
|
-
|
|
727
|
-
Example:
|
|
728
|
-
|
|
729
|
-
```json
|
|
730
|
-
{
|
|
731
|
-
"jobId": "JOB-123",
|
|
732
|
-
"payload": {
|
|
733
|
-
"action": "UPSERT",
|
|
734
|
-
"entityType": "INVENTORY_POSITION",
|
|
735
|
-
"entities": [
|
|
736
|
-
{
|
|
737
|
-
"locationRef": "LOC-1",
|
|
738
|
-
"skuRef": "SKU-1",
|
|
739
|
-
"qty": 10,
|
|
740
|
-
"type": "LAST_ON_HAND",
|
|
741
|
-
"status": "ACTIVE"
|
|
742
|
-
}
|
|
743
|
-
]
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
## `batch.status`
|
|
749
|
-
|
|
750
|
-
Reads status for a previously created batch job.
|
|
751
|
-
|
|
752
|
-
- **Required**: `jobId`
|
|
753
|
-
|
|
754
|
-
Example:
|
|
755
|
-
|
|
756
|
-
```json
|
|
757
|
-
{
|
|
758
|
-
"jobId": "JOB-123"
|
|
759
|
-
}
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
## `batch.batchStatus`
|
|
763
|
-
|
|
764
|
-
Gets the status of a specific batch within a job. Useful for troubleshooting partial failures in multi-batch jobs.
|
|
765
|
-
|
|
766
|
-
- **Required**: `jobId`, `batchId`
|
|
767
|
-
|
|
768
|
-
Example:
|
|
769
|
-
|
|
770
|
-
```json
|
|
771
|
-
{
|
|
772
|
-
"jobId": "JOB-123",
|
|
773
|
-
"batchId": "BATCH-456"
|
|
774
|
-
}
|
|
775
|
-
```
|
|
776
|
-
|
|
777
|
-
## `batch.results`
|
|
778
|
-
|
|
779
|
-
Gets per-record outcomes for a completed job. Call after `batch.status` reports a terminal state.
|
|
780
|
-
|
|
781
|
-
- **Required**: `jobId`
|
|
782
|
-
|
|
783
|
-
Example:
|
|
784
|
-
|
|
785
|
-
```json
|
|
786
|
-
{
|
|
787
|
-
"jobId": "JOB-123"
|
|
788
|
-
}
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
---
|
|
792
|
-
|
|
793
|
-
## `graphql.queryAll`
|
|
794
|
-
|
|
795
|
-
Executes a GraphQL query with SDK auto-pagination. Automatically follows cursors, merges all edges, and deduplicates by node ID. Use instead of `graphql.query` when you need ALL records from a connection.
|
|
796
|
-
|
|
797
|
-
- **Required**: `query`
|
|
798
|
-
- **Optional**:
|
|
799
|
-
- `variables`: query variables (include cursor variable, usually `null` for first page)
|
|
800
|
-
- `maxPages`: max pages to fetch (default: 100, max: 500)
|
|
801
|
-
- `maxRecords`: max total records to accumulate (default: 10000, max: 50000)
|
|
802
|
-
- `timeoutMs`: hard timeout for entire pagination run (default: 300000ms = 5 min)
|
|
803
|
-
- `direction`: `forward` (first/after) or `backward` (last/before)
|
|
804
|
-
- `errorHandling`: `throw` (fail on errors) or `partial` (return partial data + errors)
|
|
805
|
-
- **Timeout behavior**:
|
|
806
|
-
- uses `timeoutMs` for the whole pagination run
|
|
807
|
-
- independent from `FLUENT_REQUEST_TIMEOUT_MS` single-call timeout
|
|
808
|
-
|
|
809
|
-
### How it works
|
|
810
|
-
|
|
811
|
-
1. SDK detects pagination variables (`first`/`after` or `last`/`before`) in your query
|
|
812
|
-
2. Executes the first page
|
|
813
|
-
3. Extracts cursor from the last edge, follows `hasNextPage`
|
|
814
|
-
4. Merges edges across pages, deduplicates by node ID
|
|
815
|
-
5. Stops when: no more pages, maxPages reached, maxRecords reached, or timeout
|
|
816
|
-
|
|
817
|
-
### Response
|
|
818
|
-
|
|
819
|
-
Includes `extensions.autoPagination`:
|
|
820
|
-
|
|
821
|
-
```json
|
|
822
|
-
{
|
|
823
|
-
"totalPages": 5,
|
|
824
|
-
"totalRecords": 487,
|
|
825
|
-
"truncated": false,
|
|
826
|
-
"direction": "forward"
|
|
827
|
-
}
|
|
828
|
-
```
|
|
829
|
-
|
|
830
|
-
If truncated: `truncationReason` will be `"maxPages"`, `"maxRecords"`, or `"timeout"`.
|
|
831
|
-
|
|
832
|
-
### Example: Fetch all active orders
|
|
833
|
-
|
|
834
|
-
```json
|
|
835
|
-
{
|
|
836
|
-
"query": "query($cursor: String) { orders(first: 100, after: $cursor, status: \"ACTIVE\") { edges { cursor node { id ref status createdOn } } pageInfo { hasNextPage } } }",
|
|
837
|
-
"variables": { "cursor": null },
|
|
838
|
-
"maxRecords": 5000
|
|
839
|
-
}
|
|
840
|
-
```
|
|
841
|
-
|
|
842
|
-
### Example: Fetch all locations (backward)
|
|
843
|
-
|
|
844
|
-
```json
|
|
845
|
-
{
|
|
846
|
-
"query": "query($cursor: String) { locations(last: 50, before: $cursor) { edges { cursor node { id ref name type } } pageInfo { hasPreviousPage } } }",
|
|
847
|
-
"variables": { "cursor": null },
|
|
848
|
-
"direction": "backward"
|
|
849
|
-
}
|
|
850
|
-
```
|
|
851
|
-
|
|
852
|
-
## `graphql.batchMutate`
|
|
853
|
-
|
|
854
|
-
Executes multiple GraphQL mutations in a single request using aliased mutations. Sends up to 50 mutations at once, each with its own input.
|
|
855
|
-
|
|
856
|
-
- **Required**: `mutation`, `inputs`
|
|
857
|
-
- **Optional**: `returnFields`, `operationName`
|
|
858
|
-
- **Retry behavior**: disabled (non-idempotent write operation)
|
|
859
|
-
|
|
860
|
-
### How it works
|
|
861
|
-
|
|
862
|
-
1. Builds an aliased mutation query:
|
|
863
|
-
```graphql
|
|
864
|
-
mutation BatchUpdateOrders($input1: UpdateOrderInput!, $input2: UpdateOrderInput!) {
|
|
865
|
-
updateOrder1: updateOrder(input: $input1) { id ref status }
|
|
866
|
-
updateOrder2: updateOrder(input: $input2) { id ref status }
|
|
867
|
-
}
|
|
868
|
-
```
|
|
869
|
-
2. Sends as a single GraphQL request
|
|
870
|
-
3. Parses per-mutation results (success/failure for each input)
|
|
871
|
-
|
|
872
|
-
### Response
|
|
873
|
-
|
|
874
|
-
```json
|
|
875
|
-
{
|
|
876
|
-
"ok": true,
|
|
877
|
-
"summary": "All 3 mutations succeeded",
|
|
878
|
-
"executed": 3,
|
|
879
|
-
"failed": 0,
|
|
880
|
-
"allSucceeded": true,
|
|
881
|
-
"allFailed": false,
|
|
882
|
-
"results": [
|
|
883
|
-
{ "alias": "updateOrder1", "index": 0, "success": true, "data": { "id": "1", "ref": "ORD-001", "status": "SHIPPED" } },
|
|
884
|
-
{ "alias": "updateOrder2", "index": 1, "success": true, "data": { "id": "2", "ref": "ORD-002", "status": "SHIPPED" } }
|
|
885
|
-
]
|
|
886
|
-
}
|
|
887
|
-
```
|
|
888
|
-
|
|
889
|
-
On partial failure, `errors` array includes per-mutation error details with `alias`, `index`, `message`, and `inputRef`.
|
|
890
|
-
|
|
891
|
-
### Example: Bulk update order statuses
|
|
892
|
-
|
|
893
|
-
```json
|
|
894
|
-
{
|
|
895
|
-
"mutation": "updateOrder",
|
|
896
|
-
"inputs": [
|
|
897
|
-
{ "id": "36", "status": "SHIPPED" },
|
|
898
|
-
{ "id": "37", "status": "SHIPPED" },
|
|
899
|
-
{ "id": "38", "status": "SHIPPED" }
|
|
900
|
-
],
|
|
901
|
-
"returnFields": ["id", "ref", "status"]
|
|
902
|
-
}
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
### Example: Batch create inventory positions
|
|
906
|
-
|
|
907
|
-
```json
|
|
908
|
-
{
|
|
909
|
-
"mutation": "updateInventoryPosition",
|
|
910
|
-
"inputs": [
|
|
911
|
-
{ "id": "100", "status": "ACTIVE" },
|
|
912
|
-
{ "id": "101", "status": "ACTIVE" }
|
|
913
|
-
],
|
|
914
|
-
"returnFields": ["id", "ref", "status", "onHand"]
|
|
915
|
-
}
|
|
916
|
-
```
|
|
917
|
-
|
|
918
|
-
## `graphql.introspect`
|
|
919
|
-
|
|
920
|
-
Inspects the Fluent Commerce GraphQL schema via introspection. Fetches the full schema and caches it for 1 hour. Use to discover mutations, input types, and field requirements at runtime.
|
|
921
|
-
|
|
922
|
-
- **Optional** (specify one mode):
|
|
923
|
-
- `type`: name of an INPUT_OBJECT type to inspect (e.g., `UpdateOrderInput`)
|
|
924
|
-
- `mutation`: name of a mutation to inspect (e.g., `updateOrder`)
|
|
925
|
-
- `listMutations`: `true` to list all available mutation names
|
|
926
|
-
- `listQueries`: `true` to list all available query root field names
|
|
927
|
-
|
|
928
|
-
### Example: List all mutations
|
|
929
|
-
|
|
930
|
-
```json
|
|
931
|
-
{
|
|
932
|
-
"listMutations": true
|
|
933
|
-
}
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
Response:
|
|
937
|
-
|
|
938
|
-
```json
|
|
939
|
-
{
|
|
940
|
-
"ok": true,
|
|
941
|
-
"mutations": ["createOrder", "updateOrder", "createFulfilment", "..."],
|
|
942
|
-
"count": 142
|
|
943
|
-
}
|
|
944
|
-
```
|
|
945
|
-
|
|
946
|
-
### Example: Inspect a mutation
|
|
947
|
-
|
|
948
|
-
```json
|
|
949
|
-
{
|
|
950
|
-
"mutation": "updateOrder"
|
|
951
|
-
}
|
|
952
|
-
```
|
|
953
|
-
|
|
954
|
-
Response:
|
|
955
|
-
|
|
956
|
-
```json
|
|
957
|
-
{
|
|
958
|
-
"ok": true,
|
|
959
|
-
"mutation": {
|
|
960
|
-
"name": "updateOrder",
|
|
961
|
-
"description": "Updates an existing order",
|
|
962
|
-
"args": [
|
|
963
|
-
{
|
|
964
|
-
"name": "input",
|
|
965
|
-
"type": "UpdateOrderInput!",
|
|
966
|
-
"required": true
|
|
967
|
-
}
|
|
968
|
-
],
|
|
969
|
-
"returnType": "Order"
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
```
|
|
973
|
-
|
|
974
|
-
### Example: Inspect an input type
|
|
975
|
-
|
|
976
|
-
```json
|
|
977
|
-
{
|
|
978
|
-
"type": "UpdateOrderInput"
|
|
979
|
-
}
|
|
980
|
-
```
|
|
981
|
-
|
|
982
|
-
Response:
|
|
983
|
-
|
|
984
|
-
```json
|
|
985
|
-
{
|
|
986
|
-
"ok": true,
|
|
987
|
-
"inputType": {
|
|
988
|
-
"name": "UpdateOrderInput",
|
|
989
|
-
"fields": [
|
|
990
|
-
{ "name": "id", "type": "ID!", "required": true },
|
|
991
|
-
{ "name": "ref", "type": "String", "required": false },
|
|
992
|
-
{ "name": "status", "type": "String", "required": false },
|
|
993
|
-
{ "name": "attributes", "type": "[AttributeInput]", "required": false, "isArray": true }
|
|
994
|
-
]
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
```
|
|
998
|
-
|
|
999
|
-
### Chaining example
|
|
1000
|
-
|
|
1001
|
-
1. `{ "listMutations": true }` → find `updateOrder`
|
|
1002
|
-
2. `{ "mutation": "updateOrder" }` → see it takes `UpdateOrderInput!`
|
|
1003
|
-
3. `{ "type": "UpdateOrderInput" }` → see all updatable fields
|
|
1004
|
-
4. Use `graphql.query` or `graphql.batchMutate` with the discovered schema
|
|
1005
|
-
|
|
1006
|
-
## `connection.test`
|
|
1007
|
-
|
|
1008
|
-
Comprehensive Fluent Commerce connectivity test. Authenticates, executes a `me` query, and returns the authenticated user's details.
|
|
1009
|
-
|
|
1010
|
-
- **Input**: no fields
|
|
1011
|
-
|
|
1012
|
-
### Response (success)
|
|
1013
|
-
|
|
1014
|
-
```json
|
|
1015
|
-
{
|
|
1016
|
-
"ok": true,
|
|
1017
|
-
"duration": 342,
|
|
1018
|
-
"details": {
|
|
1019
|
-
"userId": "12",
|
|
1020
|
-
"username": "admin@hmtest.com",
|
|
1021
|
-
"userType": "RETAILER",
|
|
1022
|
-
"userStatus": "ACTIVE",
|
|
1023
|
-
"retailerId": "5",
|
|
1024
|
-
"retailerRef": "HMTEST",
|
|
1025
|
-
"retailerName": "HM Test",
|
|
1026
|
-
"locationId": "10",
|
|
1027
|
-
"locationRef": "W252",
|
|
1028
|
-
"locationName": "Warehouse 252"
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
```
|
|
1032
|
-
|
|
1033
|
-
### Response (failure)
|
|
1034
|
-
|
|
1035
|
-
```json
|
|
1036
|
-
{
|
|
1037
|
-
"ok": false,
|
|
1038
|
-
"duration": 1500,
|
|
1039
|
-
"error": "Authentication failed after 3 retries: 401 Unauthorized"
|
|
1040
|
-
}
|
|
1041
|
-
```
|
|
1042
|
-
|
|
1043
|
-
More thorough than `health.ping` — actually verifies the GraphQL endpoint works end-to-end. Use when first connecting, debugging auth issues, or verifying retailer/location context.
|
|
1044
|
-
|
|
1045
|
-
## `webhook.validate`
|
|
1046
|
-
|
|
1047
|
-
Validates a Fluent Commerce webhook payload and optionally verifies its signature.
|
|
1048
|
-
|
|
1049
|
-
- **Required**: `payload`
|
|
1050
|
-
- **Optional**:
|
|
1051
|
-
- `rawBody`: exact original HTTP request body string for signature checks
|
|
1052
|
-
- `signature`: the `X-Fluent-Signature` header value (base64-encoded)
|
|
1053
|
-
- `publicKey`: the Fluent public key (PEM format) for signature verification
|
|
1054
|
-
- `algorithm`: `SHA512withRSA` (default) or `MD5withRSA`
|
|
1055
|
-
|
|
1056
|
-
### Mode 1: Basic validation
|
|
1057
|
-
|
|
1058
|
-
Checks that required fields (`name`, `id`, `retailerId`) are present in the payload.
|
|
1059
|
-
|
|
1060
|
-
```json
|
|
1061
|
-
{
|
|
1062
|
-
"payload": {
|
|
1063
|
-
"name": "OrderCreated",
|
|
1064
|
-
"id": "event-123",
|
|
1065
|
-
"retailerId": "5",
|
|
1066
|
-
"entityType": "ORDER",
|
|
1067
|
-
"entityRef": "ORD-001"
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
```
|
|
1071
|
-
|
|
1072
|
-
Response:
|
|
1073
|
-
|
|
1074
|
-
```json
|
|
1075
|
-
{
|
|
1076
|
-
"ok": true,
|
|
1077
|
-
"basicValidation": { "valid": true, "missingFields": [] },
|
|
1078
|
-
"note": "Basic field validation passed. Provide signature + publicKey for signature verification."
|
|
1079
|
-
}
|
|
1080
|
-
```
|
|
1081
|
-
|
|
1082
|
-
### Mode 2: Signature validation
|
|
1083
|
-
|
|
1084
|
-
Verifies the webhook body against the `X-Fluent-Signature` header using the provided public key.
|
|
1085
|
-
Use `rawBody` whenever possible because signature verification is byte-sensitive.
|
|
1086
|
-
|
|
1087
|
-
```json
|
|
1088
|
-
{
|
|
1089
|
-
"payload": {
|
|
1090
|
-
"name": "OrderCreated",
|
|
1091
|
-
"id": "event-123",
|
|
1092
|
-
"retailerId": "5"
|
|
1093
|
-
},
|
|
1094
|
-
"rawBody": "{\"name\":\"OrderCreated\",\"id\":\"event-123\",\"retailerId\":\"5\"}",
|
|
1095
|
-
"signature": "base64-encoded-signature-from-header",
|
|
1096
|
-
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----",
|
|
1097
|
-
"algorithm": "SHA512withRSA"
|
|
1098
|
-
}
|
|
1099
|
-
```
|
|
1100
|
-
|
|
1101
|
-
Response:
|
|
1102
|
-
|
|
1103
|
-
```json
|
|
1104
|
-
{
|
|
1105
|
-
"ok": true,
|
|
1106
|
-
"basicValidation": { "valid": true, "missingFields": [] },
|
|
1107
|
-
"signatureValidation": {
|
|
1108
|
-
"valid": true,
|
|
1109
|
-
"algorithm": "SHA512withRSA",
|
|
1110
|
-
"payloadSource": "rawBody"
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
```
|
|
1114
|
-
|
|
1115
|
-
---
|
|
1116
|
-
|
|
1117
|
-
## `entity.create`
|
|
1118
|
-
|
|
1119
|
-
Type-safe entity creation with built-in validation and gotcha knowledge.
|
|
1120
|
-
|
|
1121
|
-
**Supported entity types:** ORDER, FULFILMENT, LOCATION, NETWORK, CUSTOMER, PRODUCT, INVENTORY_POSITION, VIRTUAL_CATALOGUE, VIRTUAL_POSITION, CATEGORY, CARRIER, SETTING
|
|
1122
|
-
|
|
1123
|
-
- **Required**:
|
|
1124
|
-
- `entityType`: entity type string (see supported list above)
|
|
1125
|
-
- `data`: creation input fields matching the GraphQL create input type
|
|
1126
|
-
- **Optional**:
|
|
1127
|
-
- `returnFields`: array of field names to return (defaults to entity type defaults)
|
|
1128
|
-
- `dryRun`: if `true`, builds and validates the mutation without executing (default `false`)
|
|
1129
|
-
|
|
1130
|
-
**Key behaviors:**
|
|
1131
|
-
|
|
1132
|
-
- Validates required fields BEFORE sending (e.g., Location requires `openingSchedule`)
|
|
1133
|
-
- Encodes compound key rules (ProductKey needs `ref` + `catalogue.ref`)
|
|
1134
|
-
- Auto-resolves `retailerId` from config for retailer-scoped entities
|
|
1135
|
-
- Returns the executed mutation string for audit trail
|
|
1136
|
-
|
|
1137
|
-
**Known gotchas (encoded in validation):**
|
|
1138
|
-
|
|
1139
|
-
- No create inputs have a `status` field — status is auto-set to `CREATED`
|
|
1140
|
-
- `Customer` has NO `ref` field; `username` is the identifier
|
|
1141
|
-
- `Network` uses `name` (not `ref`) in create; `retailers` is a plural array
|
|
1142
|
-
- `Location` requires `openingSchedule` even for 24/7 (use `allHours: true`)
|
|
1143
|
-
- `Product` `gtin` has a 20-character max
|
|
1144
|
-
- `Setting` `context` is a plain String, `contextId` is a separate Int
|
|
1145
|
-
|
|
1146
|
-
**Example** — create a location (dry run):
|
|
1147
|
-
|
|
1148
|
-
```json
|
|
1149
|
-
{
|
|
1150
|
-
"entityType": "LOCATION",
|
|
1151
|
-
"data": {
|
|
1152
|
-
"ref": "LOC_WH_01",
|
|
1153
|
-
"type": "WAREHOUSE",
|
|
1154
|
-
"name": "Main Warehouse",
|
|
1155
|
-
"openingSchedule": { "allHours": true }
|
|
1156
|
-
},
|
|
1157
|
-
"dryRun": true
|
|
1158
|
-
}
|
|
1159
|
-
```
|
|
1160
|
-
|
|
1161
|
-
Response (dry run):
|
|
1162
|
-
|
|
1163
|
-
```json
|
|
1164
|
-
{
|
|
1165
|
-
"ok": true,
|
|
1166
|
-
"dryRun": true,
|
|
1167
|
-
"entityType": "LOCATION",
|
|
1168
|
-
"mutation": "mutation CreateLocation($input: CreateLocationInput!) { createLocation(input: $input) { id ref status type name } }",
|
|
1169
|
-
"inputType": "CreateLocationInput",
|
|
1170
|
-
"variables": { "input": { "ref": "LOC_WH_01", "type": "WAREHOUSE", "name": "Main Warehouse", "openingSchedule": { "allHours": true } } },
|
|
1171
|
-
"requiredFields": ["ref", "type", "name", "openingSchedule"],
|
|
1172
|
-
"gotchas": ["Location requires openingSchedule — use { allHours: true } for 24/7"],
|
|
1173
|
-
"note": "No API call made. Set dryRun=false to execute."
|
|
1174
|
-
}
|
|
1175
|
-
```
|
|
1176
|
-
|
|
1177
|
-
**Example** — create a location (real):
|
|
1178
|
-
|
|
1179
|
-
```json
|
|
1180
|
-
{
|
|
1181
|
-
"entityType": "LOCATION",
|
|
1182
|
-
"data": {
|
|
1183
|
-
"ref": "LOC_WH_01",
|
|
1184
|
-
"type": "WAREHOUSE",
|
|
1185
|
-
"name": "Main Warehouse",
|
|
1186
|
-
"openingSchedule": { "allHours": true }
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
```
|
|
1190
|
-
|
|
1191
|
-
Response:
|
|
1192
|
-
|
|
1193
|
-
```json
|
|
1194
|
-
{
|
|
1195
|
-
"ok": true,
|
|
1196
|
-
"entityType": "LOCATION",
|
|
1197
|
-
"entity": { "id": "42", "ref": "LOC_WH_01", "status": "CREATED", "type": "WAREHOUSE", "name": "Main Warehouse" },
|
|
1198
|
-
"mutation": "mutation CreateLocation($input: CreateLocationInput!) { ... }"
|
|
1199
|
-
}
|
|
1200
|
-
```
|
|
1201
|
-
|
|
1202
|
-
---
|
|
1203
|
-
|
|
1204
|
-
## `entity.update`
|
|
1205
|
-
|
|
1206
|
-
Status-aware entity updates with optional transition validation.
|
|
1207
|
-
|
|
1208
|
-
- **Required**:
|
|
1209
|
-
- `entityType`: entity type string
|
|
1210
|
-
- `id`: entity ID (required for updates)
|
|
1211
|
-
- `fields`: object of fields to update (matches GraphQL update input type)
|
|
1212
|
-
- **Optional**:
|
|
1213
|
-
- `returnFields`: array of field names to return
|
|
1214
|
-
- `validateTransition`: if `true` and `status` is being changed, queries `workflow.transitions` to verify the transition is allowed before executing (default `false`)
|
|
1215
|
-
|
|
1216
|
-
**Key behaviors:**
|
|
1217
|
-
|
|
1218
|
-
- Auto-detects the correct GraphQL mutation name from entity type
|
|
1219
|
-
- When `validateTransition` is `true`: fetches current entity state, queries transition API, warns if no workflow transitions exist from the current status
|
|
1220
|
-
- Returns the previous status for audit trail when transition validation is used
|
|
1221
|
-
- Uses no-retry semantics (write operation)
|
|
1222
|
-
|
|
1223
|
-
**Example** — update order status with transition validation:
|
|
1224
|
-
|
|
1225
|
-
```json
|
|
1226
|
-
{
|
|
1227
|
-
"entityType": "ORDER",
|
|
1228
|
-
"id": "12345",
|
|
1229
|
-
"fields": { "status": "COMPLETE" },
|
|
1230
|
-
"validateTransition": true
|
|
1231
|
-
}
|
|
1232
|
-
```
|
|
1233
|
-
|
|
1234
|
-
Response:
|
|
1235
|
-
|
|
1236
|
-
```json
|
|
1237
|
-
{
|
|
1238
|
-
"ok": true,
|
|
1239
|
-
"entityType": "ORDER",
|
|
1240
|
-
"entity": { "id": "12345", "ref": "HD-001", "status": "COMPLETE" },
|
|
1241
|
-
"mutation": "mutation UpdateOrder($input: UpdateOrderInput!) { ... }"
|
|
1242
|
-
}
|
|
1243
|
-
```
|
|
1244
|
-
|
|
1245
|
-
If no workflow transition exists:
|
|
1246
|
-
|
|
1247
|
-
```json
|
|
1248
|
-
{
|
|
1249
|
-
"ok": true,
|
|
1250
|
-
"entityType": "ORDER",
|
|
1251
|
-
"entity": { "id": "12345", "ref": "HD-001", "status": "COMPLETE" },
|
|
1252
|
-
"mutation": "...",
|
|
1253
|
-
"transitionWarning": "No workflow transitions available from status \"BOOKED\" for ORDER. The status update may succeed as a direct mutation but will not trigger workflow orchestration."
|
|
1254
|
-
}
|
|
1255
|
-
```
|
|
1256
|
-
|
|
1257
|
-
---
|
|
1258
|
-
|
|
1259
|
-
## `entity.get`
|
|
1260
|
-
|
|
1261
|
-
Unified entity lookup by ID or ref with optional edge inclusion.
|
|
1262
|
-
|
|
1263
|
-
- **Required**:
|
|
1264
|
-
- `entityType`: entity type string
|
|
1265
|
-
- At least one of `id` or `ref`
|
|
1266
|
-
- **Optional**:
|
|
1267
|
-
- `id`: entity ID (preferred lookup method)
|
|
1268
|
-
- `ref`: entity ref (fallback — not available for CUSTOMER)
|
|
1269
|
-
- `fields`: array of field names to return (defaults to entity type defaults: id, ref, status, type, createdOn, etc.)
|
|
1270
|
-
- `includeEdges`: array of related entity edges to fetch (e.g., `["fulfilments", "items", "attributes"]`)
|
|
1271
|
-
|
|
1272
|
-
**Key behaviors:**
|
|
1273
|
-
|
|
1274
|
-
- Prefers ID-based lookup (single entity query) over ref-based lookup (connection query)
|
|
1275
|
-
- Ref-based lookup uses the connection query pattern (edges/node)
|
|
1276
|
-
- Returns `{ found: false }` when entity is not found (not an error)
|
|
1277
|
-
- CUSTOMER entity type does not support ref-based lookup
|
|
1278
|
-
|
|
1279
|
-
**Example** — get order by ref with fulfilments:
|
|
1280
|
-
|
|
1281
|
-
```json
|
|
1282
|
-
{
|
|
1283
|
-
"entityType": "ORDER",
|
|
1284
|
-
"ref": "HD-001",
|
|
1285
|
-
"includeEdges": ["fulfilments"]
|
|
1286
|
-
}
|
|
1287
|
-
```
|
|
1288
|
-
|
|
1289
|
-
Response:
|
|
1290
|
-
|
|
1291
|
-
```json
|
|
1292
|
-
{
|
|
1293
|
-
"ok": true,
|
|
1294
|
-
"found": true,
|
|
1295
|
-
"entityType": "ORDER",
|
|
1296
|
-
"entity": {
|
|
1297
|
-
"id": "12345",
|
|
1298
|
-
"ref": "HD-001",
|
|
1299
|
-
"status": "BOOKED",
|
|
1300
|
-
"type": "HD",
|
|
1301
|
-
"fulfilments": {
|
|
1302
|
-
"edges": [
|
|
1303
|
-
{ "node": { "id": "67890", "ref": "FUL-001", "status": "CREATED" } }
|
|
1304
|
-
]
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
```
|
|
1309
|
-
|
|
1310
|
-
---
|
|
1311
|
-
|
|
1312
|
-
## `workflow.upload`
|
|
1313
|
-
|
|
1314
|
-
Deploy a workflow JSON definition to the Fluent environment.
|
|
1315
|
-
|
|
1316
|
-
Uploads via REST API `POST /api/v4.1/workflow/{retailerId}` — the same endpoint the Fluent CLI uses.
|
|
1317
|
-
|
|
1318
|
-
- **Required**:
|
|
1319
|
-
- `workflow`: workflow JSON definition (as object or JSON string)
|
|
1320
|
-
- **Optional**:
|
|
1321
|
-
- `retailerId`: target retailer ID (falls back to `FLUENT_RETAILER_ID`)
|
|
1322
|
-
- `validate`: validate structure before uploading (default `true`)
|
|
1323
|
-
- `dryRun`: validate only, do not deploy (default `false`)
|
|
1324
|
-
|
|
1325
|
-
**Validation checks:**
|
|
1326
|
-
|
|
1327
|
-
- `name` field present
|
|
1328
|
-
- At least one status defined in `statuses` array
|
|
1329
|
-
- All rulesets have `name`, `triggers`, and `rules` (warns if empty)
|
|
1330
|
-
|
|
1331
|
-
**Important:** For production deployments, prefer `fluent module install` via CLI which bundles workflows with settings, rules, and data in a versioned module. Use this tool for interactive editing, hotfixes, or when CLI is unavailable.
|
|
1332
|
-
|
|
1333
|
-
**Example** — dry-run validation:
|
|
1334
|
-
|
|
1335
|
-
```json
|
|
1336
|
-
{
|
|
1337
|
-
"workflow": {
|
|
1338
|
-
"name": "ORDER::HD",
|
|
1339
|
-
"type": "ORDER",
|
|
1340
|
-
"subtype": "HD",
|
|
1341
|
-
"statuses": [{ "name": "CREATED" }, { "name": "BOOKED" }],
|
|
1342
|
-
"rulesets": [
|
|
1343
|
-
{
|
|
1344
|
-
"name": "BookOrder",
|
|
1345
|
-
"triggers": [{ "status": "CREATED" }],
|
|
1346
|
-
"rules": [{ "name": "com.fluentretail.rubix.rule.order.SendEventOnVerifyingState", "props": { "status": "BOOKED" } }]
|
|
1347
|
-
}
|
|
1348
|
-
]
|
|
1349
|
-
},
|
|
1350
|
-
"dryRun": true
|
|
1351
|
-
}
|
|
1352
|
-
```
|
|
1353
|
-
|
|
1354
|
-
Response (dry run — valid):
|
|
1355
|
-
|
|
1356
|
-
```json
|
|
1357
|
-
{
|
|
1358
|
-
"ok": true,
|
|
1359
|
-
"dryRun": true,
|
|
1360
|
-
"valid": true,
|
|
1361
|
-
"workflowName": "ORDER::HD",
|
|
1362
|
-
"retailerId": "5",
|
|
1363
|
-
"note": "Validation passed. Set dryRun=false to deploy."
|
|
1364
|
-
}
|
|
1365
|
-
```
|
|
1366
|
-
|
|
1367
|
-
---
|
|
1368
|
-
|
|
1369
|
-
## `workflow.diff`
|
|
1370
|
-
|
|
1371
|
-
Compare two workflow JSON definitions and identify changes.
|
|
1372
|
-
|
|
1373
|
-
Pure local computation — no API calls.
|
|
1374
|
-
|
|
1375
|
-
- **Required**:
|
|
1376
|
-
- `base`: base workflow JSON (before changes)
|
|
1377
|
-
- `target`: target workflow JSON (after changes)
|
|
1378
|
-
- **Optional**:
|
|
1379
|
-
- `format`: output format — `summary` (default), `detailed`, or `mermaid`
|
|
1380
|
-
|
|
1381
|
-
**Comparison scope:**
|
|
1382
|
-
|
|
1383
|
-
- Rulesets: added, removed, modified (trigger changes, rule additions/removals, prop changes)
|
|
1384
|
-
- Statuses: added, removed
|
|
1385
|
-
- Risk assessment: removing rulesets or statuses = HIGH, modifying props = MEDIUM, adding = LOW
|
|
1386
|
-
|
|
1387
|
-
**Formats:**
|
|
1388
|
-
|
|
1389
|
-
| Format | Returns |
|
|
1390
|
-
|---|---|
|
|
1391
|
-
| `summary` | Change counts, status changes, risk level |
|
|
1392
|
-
| `detailed` | Full `WorkflowDiffResult` object with per-ruleset breakdown |
|
|
1393
|
-
| `mermaid` | `stateDiagram-v2` with color-coded added/removed/modified states and transitions |
|
|
1394
|
-
|
|
1395
|
-
**Example** — summary diff:
|
|
1396
|
-
|
|
1397
|
-
```json
|
|
1398
|
-
{
|
|
1399
|
-
"base": { "name": "ORDER::HD", "statuses": [...], "rulesets": [...] },
|
|
1400
|
-
"target": { "name": "ORDER::HD", "statuses": [...], "rulesets": [...] },
|
|
1401
|
-
"format": "summary"
|
|
1402
|
-
}
|
|
1403
|
-
```
|
|
1404
|
-
|
|
1405
|
-
Response:
|
|
1406
|
-
|
|
1407
|
-
```json
|
|
1408
|
-
{
|
|
1409
|
-
"ok": true,
|
|
1410
|
-
"summary": "3 ruleset change(s): 1 added, 0 removed, 2 modified. 1 status change(s): 1 added, 0 removed. Risk level: MEDIUM.",
|
|
1411
|
-
"riskLevel": "medium",
|
|
1412
|
-
"rulesetChanges": { "added": 1, "removed": 0, "modified": 2 },
|
|
1413
|
-
"statusChanges": { "added": ["AWAITING_PICKUP"], "removed": [] }
|
|
1414
|
-
}
|
|
1415
|
-
```
|
|
1416
|
-
|
|
1417
|
-
---
|
|
1418
|
-
|
|
1419
|
-
## `workflow.simulate`
|
|
1420
|
-
|
|
1421
|
-
Static prediction of workflow event outcomes from workflow JSON.
|
|
1422
|
-
|
|
1423
|
-
Pure local computation — no API calls. Parses workflow JSON to find matching rulesets and predict state transitions, follow-on events, and webhook side effects.
|
|
1424
|
-
|
|
1425
|
-
- **Required**:
|
|
1426
|
-
- `workflow`: workflow JSON definition (as object or JSON string)
|
|
1427
|
-
- `currentStatus`: entity status to simulate from (e.g., `CREATED`, `BOOKED`)
|
|
1428
|
-
- **Optional**:
|
|
1429
|
-
- `eventName`: event name to match against ruleset names (if omitted, returns all rulesets triggered by `currentStatus`)
|
|
1430
|
-
- `entityType`: entity type filter for trigger matching
|
|
1431
|
-
- `entitySubtype`: entity subtype filter for trigger matching
|
|
1432
|
-
|
|
1433
|
-
**What it does:**
|
|
1434
|
-
|
|
1435
|
-
- Finds rulesets whose triggers match `currentStatus` (and optionally `eventName`)
|
|
1436
|
-
- Extracts `SetState`/`ChangeState` rules → predicted next status
|
|
1437
|
-
- Extracts `SendEvent` rules → predicted follow-on events
|
|
1438
|
-
- Extracts `SendWebhook` rules → predicted webhook side effects
|
|
1439
|
-
- Identifies custom Java rules that cannot be statically predicted
|
|
1440
|
-
|
|
1441
|
-
**Important limitations (always disclosed in response):**
|
|
1442
|
-
|
|
1443
|
-
- **STATIC ONLY** — cannot evaluate runtime conditions, entity attributes, or settings values
|
|
1444
|
-
- **CUSTOM RULES OPAQUE** — Java plugin rules may conditionally execute; behavior unknown without live state
|
|
1445
|
-
- **NO CROSS-ENTITY** — does not follow SendEvent chains into child entity workflows
|
|
1446
|
-
- Use `workflow.transitions` for **authoritative live validation** of available actions
|
|
1447
|
-
|
|
1448
|
-
**Example** — simulate from CREATED status:
|
|
1449
|
-
|
|
1450
|
-
```json
|
|
1451
|
-
{
|
|
1452
|
-
"workflow": { "name": "ORDER::HD", "rulesets": [...] },
|
|
1453
|
-
"currentStatus": "CREATED",
|
|
1454
|
-
"eventName": "BookOrder"
|
|
1455
|
-
}
|
|
1456
|
-
```
|
|
1457
|
-
|
|
1458
|
-
Response:
|
|
1459
|
-
|
|
1460
|
-
```json
|
|
1461
|
-
{
|
|
1462
|
-
"ok": true,
|
|
1463
|
-
"workflowName": "ORDER::HD",
|
|
1464
|
-
"currentStatus": "CREATED",
|
|
1465
|
-
"eventName": "BookOrder",
|
|
1466
|
-
"matchedRulesets": 1,
|
|
1467
|
-
"rulesets": [
|
|
1468
|
-
{
|
|
1469
|
-
"name": "BookOrder",
|
|
1470
|
-
"matchType": "statusAndEvent",
|
|
1471
|
-
"predictedStatus": "BOOKED",
|
|
1472
|
-
"predictedEvents": ["SendToFulfilment"],
|
|
1473
|
-
"predictedWebhooks": [],
|
|
1474
|
-
"ruleCount": 3
|
|
1475
|
-
}
|
|
1476
|
-
],
|
|
1477
|
-
"prediction": {
|
|
1478
|
-
"likelyNextStatus": "BOOKED",
|
|
1479
|
-
"followOnEvents": ["SendToFulfilment"],
|
|
1480
|
-
"webhookSideEffects": [],
|
|
1481
|
-
"confidence": "low",
|
|
1482
|
-
"note": "Custom rules present — prediction may be incomplete or wrong."
|
|
1483
|
-
},
|
|
1484
|
-
"limitations": [
|
|
1485
|
-
"STATIC PREDICTION ONLY — does not account for runtime conditions, entity attributes, or settings values.",
|
|
1486
|
-
"1 custom rule(s) found whose behavior cannot be predicted statically: ForwardIfOrderCoordinatesPresent.",
|
|
1487
|
-
"Use workflow.transitions for AUTHORITATIVE live validation of available actions."
|
|
1488
|
-
]
|
|
1489
|
-
}
|
|
1490
|
-
```
|
|
1491
|
-
|
|
1492
|
-
---
|
|
1493
|
-
|
|
1494
|
-
## `setting.upsert`
|
|
1495
|
-
|
|
1496
|
-
Create or update a Fluent Commerce setting with upsert semantics.
|
|
1497
|
-
|
|
1498
|
-
Queries existing settings by `name` + `context` + `contextId` first. Creates if missing, updates if exists.
|
|
1499
|
-
|
|
1500
|
-
- **Required**:
|
|
1501
|
-
- `name`: setting key/name
|
|
1502
|
-
- `value`: setting value (for small values)
|
|
1503
|
-
- `context`: setting scope — `RETAILER`, `ACCOUNT`, `LOCATION`, `NETWORK`, `AGENT`, `CUSTOMER`
|
|
1504
|
-
- **Optional**:
|
|
1505
|
-
- `lobValue`: large object value for JSON payloads > 4KB (mutually exclusive with `value`)
|
|
1506
|
-
- `contextId`: context ID (e.g., retailer ID). Defaults to `FLUENT_RETAILER_ID` for `RETAILER` context.
|
|
1507
|
-
|
|
1508
|
-
**Key gotchas:**
|
|
1509
|
-
|
|
1510
|
-
- `context` is a plain String (`"RETAILER"`), NOT an object
|
|
1511
|
-
- `contextId` is a separate Int field
|
|
1512
|
-
- For large JSON values (> 4KB), use `lobValue` instead of `value`
|
|
1513
|
-
- Returns `created: true/false` for audit trail
|
|
1514
|
-
|
|
1515
|
-
**Example** — create a webhook URL setting:
|
|
1516
|
-
|
|
1517
|
-
```json
|
|
1518
|
-
{
|
|
1519
|
-
"name": "WEBHOOK_ORDER_NOTIFICATION",
|
|
1520
|
-
"value": "https://api.example.com/webhooks/orders",
|
|
1521
|
-
"context": "RETAILER"
|
|
1522
|
-
}
|
|
1523
|
-
```
|
|
1524
|
-
|
|
1525
|
-
Response (created):
|
|
1526
|
-
|
|
1527
|
-
```json
|
|
1528
|
-
{
|
|
1529
|
-
"ok": true,
|
|
1530
|
-
"created": true,
|
|
1531
|
-
"updated": false,
|
|
1532
|
-
"setting": {
|
|
1533
|
-
"id": "99",
|
|
1534
|
-
"name": "WEBHOOK_ORDER_NOTIFICATION",
|
|
1535
|
-
"value": "https://api.example.com/webhooks/orders",
|
|
1536
|
-
"context": "RETAILER",
|
|
1537
|
-
"contextId": 5
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
```
|
|
1541
|
-
|
|
1542
|
-
Response (updated existing):
|
|
1543
|
-
|
|
1544
|
-
```json
|
|
1545
|
-
{
|
|
1546
|
-
"ok": true,
|
|
1547
|
-
"created": false,
|
|
1548
|
-
"updated": true,
|
|
1549
|
-
"setting": { "id": "99", "name": "WEBHOOK_ORDER_NOTIFICATION", "value": "https://api.example.com/webhooks/orders-v2", "context": "RETAILER", "contextId": 5 },
|
|
1550
|
-
"previousValue": "https://api.example.com/webhooks/orders"
|
|
1551
|
-
}
|
|
1552
|
-
```
|
|
1553
|
-
|
|
1554
|
-
---
|
|
1555
|
-
|
|
1556
|
-
## `setting.bulkUpsert`
|
|
1557
|
-
|
|
1558
|
-
Batch create or update multiple settings in one call.
|
|
1559
|
-
|
|
1560
|
-
Processes up to 50 settings sequentially with individual error handling. Each setting is an independent upsert — failures on one setting don't block others.
|
|
1561
|
-
|
|
1562
|
-
- **Required**:
|
|
1563
|
-
- `settings`: array of setting objects (min 1, max 50), each with `name`, `value`, `context`, and optional `lobValue` and `contextId`
|
|
1564
|
-
|
|
1565
|
-
**Example** — bulk create 3 settings:
|
|
1566
|
-
|
|
1567
|
-
```json
|
|
1568
|
-
{
|
|
1569
|
-
"settings": [
|
|
1570
|
-
{ "name": "WEBHOOK_ORDER_URL", "value": "https://api.example.com/orders", "context": "RETAILER" },
|
|
1571
|
-
{ "name": "WEBHOOK_FULFILMENT_URL", "value": "https://api.example.com/fulfilments", "context": "RETAILER" },
|
|
1572
|
-
{ "name": "FEATURE_FLAG_RETURNS", "value": "true", "context": "RETAILER" }
|
|
1573
|
-
]
|
|
1574
|
-
}
|
|
1575
|
-
```
|
|
1576
|
-
|
|
1577
|
-
Response:
|
|
1578
|
-
|
|
1579
|
-
```json
|
|
1580
|
-
{
|
|
1581
|
-
"ok": true,
|
|
1582
|
-
"created": 2,
|
|
1583
|
-
"updated": 1,
|
|
1584
|
-
"failed": 0,
|
|
1585
|
-
"total": 3,
|
|
1586
|
-
"results": [
|
|
1587
|
-
{ "name": "WEBHOOK_ORDER_URL", "status": "created" },
|
|
1588
|
-
{ "name": "WEBHOOK_FULFILMENT_URL", "status": "created" },
|
|
1589
|
-
{ "name": "FEATURE_FLAG_RETURNS", "status": "updated" }
|
|
1590
|
-
]
|
|
1591
|
-
}
|
|
1592
|
-
```
|
|
1593
|
-
|
|
1594
|
-
---
|
|
1595
|
-
|
|
1596
|
-
## `environment.discover`
|
|
1597
|
-
|
|
1598
|
-
Full environment snapshot in one call.
|
|
1599
|
-
|
|
1600
|
-
Returns everything an agent needs to understand the Fluent environment: retailer details, locations, networks, catalogues, workflows, settings, modules, and user context.
|
|
1601
|
-
|
|
1602
|
-
- **Optional**:
|
|
1603
|
-
- `include`: array of sections to include. Default: `["retailer", "locations", "networks", "catalogues"]`. Available: `retailer`, `locations`, `networks`, `catalogues`, `workflows`, `settings`, `modules`, `users`
|
|
1604
|
-
|
|
1605
|
-
**Limitations:**
|
|
1606
|
-
|
|
1607
|
-
- Locations and settings return the first 100 items only (no auto-pagination)
|
|
1608
|
-
- Workflows section uses a transition API probe — may miss workflows with no user actions at the initial state. Use `fluent workflow list` via CLI for definitive listing.
|
|
1609
|
-
|
|
1610
|
-
**Example** — discover retailer, locations, and settings:
|
|
1611
|
-
|
|
1612
|
-
```json
|
|
1613
|
-
{
|
|
1614
|
-
"include": ["retailer", "locations", "settings"]
|
|
1615
|
-
}
|
|
1616
|
-
```
|
|
1617
|
-
|
|
1618
|
-
Response:
|
|
1619
|
-
|
|
1620
|
-
```json
|
|
1621
|
-
{
|
|
1622
|
-
"ok": true,
|
|
1623
|
-
"retailer": { "id": "5", "ref": "HM_TEST", "tradingName": "HM Test", "status": "ACTIVE" },
|
|
1624
|
-
"locations": [
|
|
1625
|
-
{ "id": "10", "ref": "LOC_WH_01", "type": "WAREHOUSE", "status": "ACTIVE", "name": "Main Warehouse" }
|
|
1626
|
-
],
|
|
1627
|
-
"settings": [
|
|
1628
|
-
{ "name": "WEBHOOK_ORDER_URL", "value": "https://...", "context": "RETAILER", "contextId": 5 }
|
|
1629
|
-
]
|
|
1630
|
-
}
|
|
1631
|
-
```
|
|
1632
|
-
|
|
1633
|
-
---
|
|
1634
|
-
|
|
1635
|
-
## `environment.validate`
|
|
1636
|
-
|
|
1637
|
-
Pre-flight environment validation. Runs configurable checks before E2E tests or deployments.
|
|
1638
|
-
|
|
1639
|
-
- **Optional**:
|
|
1640
|
-
- `checks`: array of checks to run. Default: `["auth", "retailer", "locations"]`. Available: `auth`, `retailer`, `locations`, `inventory`, `workflows`, `settings`, `modules`
|
|
1641
|
-
|
|
1642
|
-
**Available checks:**
|
|
1643
|
-
|
|
1644
|
-
| Check | What it validates |
|
|
1645
|
-
|---|---|
|
|
1646
|
-
| `auth` | Token valid, permissions sufficient |
|
|
1647
|
-
| `retailer` | Retailer exists and is active |
|
|
1648
|
-
| `locations` | At least one warehouse location exists |
|
|
1649
|
-
| `inventory` | At least one product with stock |
|
|
1650
|
-
| `workflows` | Key workflows deployed (ORDER, FULFILMENT) |
|
|
1651
|
-
| `settings` | Critical settings exist |
|
|
1652
|
-
| `modules` | Expected modules deployed |
|
|
1653
|
-
|
|
1654
|
-
Returns pass/fail per check with severity and actionable messages.
|
|
1655
|
-
|
|
1656
|
-
**Example** — full pre-flight validation:
|
|
1657
|
-
|
|
1658
|
-
```json
|
|
1659
|
-
{
|
|
1660
|
-
"checks": ["auth", "retailer", "locations", "workflows", "settings"]
|
|
1661
|
-
}
|
|
1662
|
-
```
|
|
1663
|
-
|
|
1664
|
-
Response:
|
|
1665
|
-
|
|
1666
|
-
```json
|
|
1667
|
-
{
|
|
1668
|
-
"ok": true,
|
|
1669
|
-
"results": [
|
|
1670
|
-
{ "check": "auth", "passed": true, "severity": "critical", "message": "Authenticated as admin@example.com" },
|
|
1671
|
-
{ "check": "retailer", "passed": true, "severity": "critical", "message": "Retailer HM_TEST (ID 5) is ACTIVE" },
|
|
1672
|
-
{ "check": "locations", "passed": true, "severity": "high", "message": "Found 3 locations (2 warehouses)" },
|
|
1673
|
-
{ "check": "workflows", "passed": false, "severity": "high", "message": "Missing workflow: FULFILMENT_OPTIONS::HD" },
|
|
1674
|
-
{ "check": "settings", "passed": true, "severity": "medium", "message": "12 retailer settings found" }
|
|
1675
|
-
],
|
|
1676
|
-
"summary": { "passed": 4, "failed": 1, "total": 5 }
|
|
1677
|
-
}
|
|
1678
|
-
```
|
|
1679
|
-
|
|
1680
|
-
---
|
|
1681
|
-
|
|
1682
|
-
## `test.assert`
|
|
1683
|
-
|
|
1684
|
-
Assert entity state matches expectations with optional polling.
|
|
1685
|
-
|
|
1686
|
-
Deep assertion on entity fields, attributes, and edge counts/statuses with human-readable failure messages.
|
|
1687
|
-
|
|
1688
|
-
- **Required**:
|
|
1689
|
-
- `entityType`: entity type (ORDER, FULFILMENT, etc.)
|
|
1690
|
-
- `assertions`: object with expected values
|
|
1691
|
-
- **Optional**:
|
|
1692
|
-
- `id`: entity ID (preferred)
|
|
1693
|
-
- `ref`: entity ref (fallback)
|
|
1694
|
-
- `poll`: if `true`, retry until assertions pass or timeout (default `false`)
|
|
1695
|
-
- `timeoutMs`: polling timeout in ms, 1000–300000 (default `60000`)
|
|
1696
|
-
- `intervalMs`: polling interval in ms, 1000–30000 (default `5000`)
|
|
1697
|
-
|
|
1698
|
-
**Assertion types:**
|
|
1699
|
-
|
|
1700
|
-
| Assertion | Description |
|
|
1701
|
-
|---|---|
|
|
1702
|
-
| `status` | Exact match on entity status |
|
|
1703
|
-
| `type` | Exact match on entity type |
|
|
1704
|
-
| `subtype` | Exact match on entity subtype |
|
|
1705
|
-
| `attributes` | Key-value pairs that must be present on the entity |
|
|
1706
|
-
| `edges` | Min/max count and status on related entities (e.g., fulfilments) |
|
|
1707
|
-
|
|
1708
|
-
**Polling mode:** Set `poll: true` to retry assertions on an interval until they pass or the timeout is reached. Useful after sending events when state changes are asynchronous.
|
|
1709
|
-
|
|
1710
|
-
**Example** — assert order is BOOKED with at least 1 fulfilment (polling):
|
|
1711
|
-
|
|
1712
|
-
```json
|
|
1713
|
-
{
|
|
1714
|
-
"entityType": "ORDER",
|
|
1715
|
-
"ref": "HD-001",
|
|
1716
|
-
"assertions": {
|
|
1717
|
-
"status": "BOOKED",
|
|
1718
|
-
"edges": {
|
|
1719
|
-
"fulfilments": { "minCount": 1 }
|
|
1720
|
-
}
|
|
1721
|
-
},
|
|
1722
|
-
"poll": true,
|
|
1723
|
-
"timeoutMs": 60000,
|
|
1724
|
-
"intervalMs": 5000
|
|
1725
|
-
}
|
|
1726
|
-
```
|
|
1727
|
-
|
|
1728
|
-
Response (pass):
|
|
1729
|
-
|
|
1730
|
-
```json
|
|
1731
|
-
{
|
|
1732
|
-
"ok": true,
|
|
1733
|
-
"passed": true,
|
|
1734
|
-
"entityType": "ORDER",
|
|
1735
|
-
"entity": { "id": "12345", "ref": "HD-001", "status": "BOOKED" },
|
|
1736
|
-
"assertions": {
|
|
1737
|
-
"status": { "expected": "BOOKED", "actual": "BOOKED", "passed": true },
|
|
1738
|
-
"edges.fulfilments.minCount": { "expected": 1, "actual": 2, "passed": true }
|
|
1739
|
-
},
|
|
1740
|
-
"polling": { "attempts": 3, "elapsed": 12500 }
|
|
1741
|
-
}
|
|
1742
|
-
```
|
|
1743
|
-
|
|
1744
|
-
Response (fail after timeout):
|
|
1745
|
-
|
|
1746
|
-
```json
|
|
1747
|
-
{
|
|
1748
|
-
"ok": true,
|
|
1749
|
-
"passed": false,
|
|
1750
|
-
"entityType": "ORDER",
|
|
1751
|
-
"entity": { "id": "12345", "ref": "HD-001", "status": "CREATED" },
|
|
1752
|
-
"assertions": {
|
|
1753
|
-
"status": { "expected": "BOOKED", "actual": "CREATED", "passed": false }
|
|
1754
|
-
},
|
|
1755
|
-
"polling": { "attempts": 12, "elapsed": 60000, "timedOut": true },
|
|
1756
|
-
"failureMessage": "Assertion failed: status expected \"BOOKED\" but got \"CREATED\" (timed out after 60000ms)"
|
|
1757
|
-
}
|
|
1758
|
-
```
|
|
1759
|
-
|
|
1760
|
-
---
|
|
1761
|
-
|
|
1762
|
-
## Retry and idempotency matrix
|
|
1763
|
-
|
|
1764
|
-
| Category | Tools | Retry behavior |
|
|
1765
|
-
|------|----------|-------------|
|
|
1766
|
-
| Read operations | `event.get`, `event.list`, `event.flowInspect`, `metrics.query`, `metrics.healthCheck`, `metrics.sloReport`, `metrics.labelCatalog`, `metrics.topEvents`, `workflow.transitions`, `graphql.query` (query), `graphql.queryAll`, `batch.status`, `batch.batchStatus`, `batch.results`, `graphql.introspect`, `connection.test`, `entity.get`, `environment.discover`, `environment.validate`, `test.assert`, `plugin.list` | retry + backoff |
|
|
1767
|
-
| Write operations | `event.send`, `batch.create`, `batch.send`, `graphql.query` (mutation), `graphql.batchMutate`, `entity.create`, `entity.update`, `workflow.upload`, `setting.upsert`, `setting.bulkUpsert` | timeout-only (no automatic retry) |
|
|
1768
|
-
| Local validation / no API | `config.validate`, `health.ping`, `event.build`, `webhook.validate`, `workflow.diff`, `workflow.simulate` | not applicable |
|
|
1769
|
-
|
|
1770
|
-
## Tool inventory summary (36 tools)
|
|
1771
|
-
|
|
1772
|
-
| Tool | Category | Requires SDK | Retry | Description |
|
|
1773
|
-
|------|----------|:------------:|:-----:|-------------|
|
|
1774
|
-
| `config.validate` | Diagnostics | No | — | Validate auth/base URL configuration |
|
|
1775
|
-
| `health.ping` | Diagnostics | No | — | Quick health check with config summary |
|
|
1776
|
-
| `connection.test` | Diagnostics | Yes | Yes | Full auth + GraphQL end-to-end test (returns user/account context) |
|
|
1777
|
-
| `event.build` | Events | No | — | Build event payload (no API call) |
|
|
1778
|
-
| `event.send` | Events | Yes | No | Send event (async/sync, supports dryRun) |
|
|
1779
|
-
| `event.get` | Events | Yes | Yes | Get single event by ID |
|
|
1780
|
-
| `event.list` | Events | Yes | Yes | List/filter events with pagination and optional analysis mode |
|
|
1781
|
-
| `event.flowInspect` | Events | Yes | Yes | One-call root-entity flow forensics with compact/full modes |
|
|
1782
|
-
| `metrics.query` | Metrics | Yes | Yes | Query Prometheus metrics (instant/range) |
|
|
1783
|
-
| `metrics.healthCheck` | Metrics | Yes | Yes | One-call anomaly assessment with Prometheus + Event API fallback |
|
|
1784
|
-
| `metrics.sloReport` | Metrics | Yes | Yes | SLO snapshot with rates, latency, and threshold findings |
|
|
1785
|
-
| `metrics.labelCatalog` | Metrics | Yes | Yes | Label discovery for a metric (live sampling + known hints) |
|
|
1786
|
-
| `metrics.topEvents` | Metrics | Yes | Yes | Ranked event analytics using Event API aggregation |
|
|
1787
|
-
| `workflow.transitions` | Orchestration | Yes | Yes | Query available user actions/transitions at any entity state |
|
|
1788
|
-
| `plugin.list` | Orchestration | Yes | Yes | List registered rules with optional name filter |
|
|
1789
|
-
| `graphql.query` | GraphQL | Yes | Varies | Execute single-page query or mutation (retries for queries only) |
|
|
1790
|
-
| `graphql.queryAll` | GraphQL | Yes | Yes | Auto-paginated query — follows cursors across all pages |
|
|
1791
|
-
| `graphql.batchMutate` | GraphQL | Yes | No | Execute up to 50 aliased mutations in one request |
|
|
1792
|
-
| `graphql.introspect` | GraphQL | Yes | Yes | Schema introspection (types, mutations, queries) |
|
|
1793
|
-
| `batch.create` | Batch | Yes | No | Create ingestion job |
|
|
1794
|
-
| `batch.send` | Batch | Yes | No | Send records to job |
|
|
1795
|
-
| `batch.status` | Batch | Yes | Yes | Check job status |
|
|
1796
|
-
| `batch.batchStatus` | Batch | Yes | Yes | Check specific batch within a job |
|
|
1797
|
-
| `batch.results` | Batch | Yes | Yes | Get per-record job outcomes |
|
|
1798
|
-
| `webhook.validate` | Webhooks | No | — | Validate webhook payload + optional signature verification |
|
|
1799
|
-
| `entity.create` | Entity | Yes | No | Type-safe entity creation with field validation and gotcha knowledge (12 entity types) |
|
|
1800
|
-
| `entity.update` | Entity | Yes | No | Status-aware entity updates with optional transition validation |
|
|
1801
|
-
| `entity.get` | Entity | Yes | Yes | Unified entity lookup by ID or ref with optional edge inclusion |
|
|
1802
|
-
| `workflow.upload` | Workflow Mgmt | Yes | No | Deploy workflow JSON via REST API with structure validation |
|
|
1803
|
-
| `workflow.diff` | Workflow Mgmt | No | — | Compare two workflows — rulesets, statuses, risk assessment, mermaid output |
|
|
1804
|
-
| `workflow.simulate` | Workflow Mgmt | No | — | Static prediction of event outcomes from workflow JSON |
|
|
1805
|
-
| `setting.upsert` | Settings | Yes | No | Create or update a setting with upsert semantics |
|
|
1806
|
-
| `setting.bulkUpsert` | Settings | Yes | No | Batch create/update up to 50 settings with per-setting error handling |
|
|
1807
|
-
| `environment.discover` | Environment | Yes | Yes | Full environment snapshot — retailer, locations, networks, catalogues, settings, modules |
|
|
1808
|
-
| `environment.validate` | Environment | Yes | Yes | Pre-flight validation checks: auth, retailer, locations, inventory, workflows |
|
|
1809
|
-
| `test.assert` | Test | Yes | Yes | Assert entity state with status, attribute, and edge assertions + polling mode |
|
|
1810
|
-
|
|
1
|
+
# Tool Reference
|
|
2
|
+
|
|
3
|
+
This document describes every MCP tool exposed by `fluent-mcp-extn`, including
|
|
4
|
+
purpose, required inputs, and example requests.
|
|
5
|
+
|
|
6
|
+
## Response envelope conventions
|
|
7
|
+
|
|
8
|
+
### Success
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"ok": true
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Most tools include extra fields like `event`, `response`, `job`, or `status`.
|
|
17
|
+
|
|
18
|
+
### Failure
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"ok": false,
|
|
23
|
+
"error": {
|
|
24
|
+
"code": "VALIDATION_ERROR",
|
|
25
|
+
"message": "Invalid tool arguments.",
|
|
26
|
+
"retryable": false,
|
|
27
|
+
"details": {}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`error.retryable` indicates whether a caller can safely retry the same request. Non-idempotent writes intentionally avoid automatic retry behavior.
|
|
33
|
+
|
|
34
|
+
## `config.validate`
|
|
35
|
+
|
|
36
|
+
Validates configuration and auth readiness without calling Fluent APIs.
|
|
37
|
+
|
|
38
|
+
- **Input**: no fields
|
|
39
|
+
- **Use when**: startup checks, CI preflight, environment validation
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## `health.ping`
|
|
48
|
+
|
|
49
|
+
Returns server health, SDK availability, and safe config summary.
|
|
50
|
+
|
|
51
|
+
- **Input**: no fields
|
|
52
|
+
- **Use when**: quick readiness checks in clients/agents
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## `event.build`
|
|
61
|
+
|
|
62
|
+
Builds a Fluent event payload only (no API call).
|
|
63
|
+
|
|
64
|
+
- **Required**: `name`, `entityRef`, `entityType`
|
|
65
|
+
- **Optional**: `entityId`, `rootEntityId`, `rootEntityType`, `source`, `type`, `attributes`, etc.
|
|
66
|
+
- **Note**: if available in your flow, provide both `entityRef` and `entityId`.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"name": "ORDER_CREATED",
|
|
73
|
+
"entityRef": "ORD-1001",
|
|
74
|
+
"entityId": "12345",
|
|
75
|
+
"entityType": "ORDER",
|
|
76
|
+
"attributes": {
|
|
77
|
+
"channel": "web"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## `event.send`
|
|
83
|
+
|
|
84
|
+
Builds and sends an event via SDK adapter.
|
|
85
|
+
|
|
86
|
+
- **Required**: `name`, `entityRef`, `entityType`
|
|
87
|
+
- **Optional**: all `event.build` fields plus:
|
|
88
|
+
- `mode`: `async` or `sync`
|
|
89
|
+
- `dryRun`: boolean
|
|
90
|
+
- **Important**: `retailerId` must match the target entity's retailer for real sends.
|
|
91
|
+
|
|
92
|
+
Example dry-run:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"name": "ORDER_CREATED",
|
|
97
|
+
"entityRef": "ORD-1001",
|
|
98
|
+
"entityId": "12345",
|
|
99
|
+
"entityType": "ORDER",
|
|
100
|
+
"dryRun": true
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Example send:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"name": "ORDER_CREATED",
|
|
109
|
+
"entityRef": "ORD-1001",
|
|
110
|
+
"entityId": "12345",
|
|
111
|
+
"entityType": "ORDER",
|
|
112
|
+
"mode": "async",
|
|
113
|
+
"dryRun": false
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## `event.get`
|
|
118
|
+
|
|
119
|
+
Gets one event by ID via SDK-native `getEventById(eventId)`:
|
|
120
|
+
|
|
121
|
+
- endpoint: `/api/v4.1/event/{id}`
|
|
122
|
+
- **Required**: `eventId`
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"eventId": "7815ee32-5985-485f-863a-2643e57b64a2"
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Typical success shape (truncated):
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"ok": true,
|
|
137
|
+
"event": {
|
|
138
|
+
"id": "7815ee32-5985-485f-863a-2643e57b64a2",
|
|
139
|
+
"name": "ORDER_CREATED",
|
|
140
|
+
"entityRef": "ORD-1001",
|
|
141
|
+
"entityType": "ORDER",
|
|
142
|
+
"retailerId": "5"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## `event.list`
|
|
148
|
+
|
|
149
|
+
Lists/filter events via SDK-native `getEvents(params)`:
|
|
150
|
+
|
|
151
|
+
- endpoint: `/api/v4.1/event`
|
|
152
|
+
- **Canonical filters**:
|
|
153
|
+
- `eventId`, `name`, `category`, `retailerId`
|
|
154
|
+
- `eventType`, `eventStatus`, `from`, `to`
|
|
155
|
+
- `start`, `count`
|
|
156
|
+
- `context.rootEntityType`, `context.rootEntityId`, `context.rootEntityRef`
|
|
157
|
+
- `context.entityType`, `context.entityId`, `context.entityRef`, `context.sourceEvents`
|
|
158
|
+
- **Alias filters** (mapped internally):
|
|
159
|
+
- `id` -> `eventId`
|
|
160
|
+
- `entityRef` -> `context.entityRef`
|
|
161
|
+
- `entityType` -> `context.entityType`
|
|
162
|
+
- `type` -> `eventType`
|
|
163
|
+
- **`context.sourceEvents` handling**:
|
|
164
|
+
- accepts array input in tool arguments
|
|
165
|
+
- serialized for query-string delivery to Event API
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
172
|
+
"eventStatus": "FAILED",
|
|
173
|
+
"context.rootEntityType": "ORDER",
|
|
174
|
+
"context.rootEntityRef": "ORD-1001",
|
|
175
|
+
"retailerId": "5",
|
|
176
|
+
"from": "2026-02-01T00:00:00.000Z",
|
|
177
|
+
"to": "2026-02-15T23:59:59.999Z",
|
|
178
|
+
"count": 25,
|
|
179
|
+
"start": 1
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Typical success shape (truncated):
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"ok": true,
|
|
188
|
+
"events": {
|
|
189
|
+
"start": 1,
|
|
190
|
+
"count": 25,
|
|
191
|
+
"hasMore": true,
|
|
192
|
+
"results": [
|
|
193
|
+
{ "id": "event-id-1", "name": "ORDER_CREATED" }
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
If `event.send` does not return an `id`, use `event.list` with `name`,
|
|
200
|
+
`entityRef`, and `retailerId` to resolve the latest event ID, then call
|
|
201
|
+
`event.get`.
|
|
202
|
+
|
|
203
|
+
## `event.flowInspect`
|
|
204
|
+
|
|
205
|
+
One-call runtime forensics for any root entity.
|
|
206
|
+
|
|
207
|
+
- **Required**: `rootEntityRef`
|
|
208
|
+
- **Optional**:
|
|
209
|
+
- `rootEntityType`: optional entity type filter (for example `ORDER`, `FULFILMENT`, `LOCATION`, `WAVE`, `PRODUCT`)
|
|
210
|
+
- `rootEntityId`: optional root entity ID (helps disambiguate reused refs)
|
|
211
|
+
- `compact`: return pre-analyzed summary instead of raw arrays (default `true`). Compact mode returns ~2-3k tokens with an `analysis` section; set to `false` for full ~24k raw data.
|
|
212
|
+
- `from`, `to`: optional time window
|
|
213
|
+
- `maxPages`: page cap per event type (`1..50`, default `10`)
|
|
214
|
+
- `includeEventDetails`: include compact orchestration event rows (default `true`, full mode only)
|
|
215
|
+
- `includeAuditDetails`: include compact audit action samples (default `false`, full mode only)
|
|
216
|
+
- `includeScheduled`: fetch SCHEDULED events separately (default `true`)
|
|
217
|
+
- `inspectStatuses`: statuses to drill via `event.get` (defaults: `["NO_MATCH", "PENDING", "FAILED"]`)
|
|
218
|
+
- `maxDrilldowns`: cap on individual event.get calls (`0..100`, default `50`)
|
|
219
|
+
- `actionSampleLimit`: max rows for action-derived sections (`1..200`, default `100`)
|
|
220
|
+
- **Default-on flags:**
|
|
221
|
+
- `compact`: pre-analyzed summary response (default `true`)
|
|
222
|
+
- `includeExceptions`: rule exceptions with class, message, ruleset context (default `true`)
|
|
223
|
+
- `includeNoMatchDetails`: enhanced NO_MATCH diagnostics from ruleSet audit events with closeMatches (default `true`)
|
|
224
|
+
- **Opt-in flags (default false):**
|
|
225
|
+
- `includeRuleDetails`: per-rule execution trace with class name, props, and timing (durationMs)
|
|
226
|
+
- `includeCustomLogs`: custom plugin log messages (LogCollection from CUSTOM category)
|
|
227
|
+
- `includeSnapshots`: entity state snapshots at each processing point
|
|
228
|
+
- `includeCrossEntity`: fetch child entity events (FULFILMENT_CHOICE, FULFILMENT) using rootEntityRef
|
|
229
|
+
|
|
230
|
+
**Compact mode (`compact: true`, the default):**
|
|
231
|
+
|
|
232
|
+
Returns a pre-analyzed summary with:
|
|
233
|
+
- `analysis.statusFlow`: ordered unique statuses seen (e.g., `["CREATED", "BOOKED", "SHIPPED"]`)
|
|
234
|
+
- `analysis.findings[]`: anomaly detection — NO_MATCH (CRITICAL), FAILED/webhook errors/exceptions (HIGH), PENDING/slow rulesets (MEDIUM)
|
|
235
|
+
- `analysis.failedWebhookEndpoints`: URLs that returned >= 400
|
|
236
|
+
- `analysis.slowestRulesets`: top 3 by duration
|
|
237
|
+
- `analysis.timespan`: first/last timestamps with durationMs
|
|
238
|
+
- `audit.webhookActions`: only failures (responseCode >= 400)
|
|
239
|
+
- `audit.mutationActions`: top 5 by queryName (name + count only)
|
|
240
|
+
- `audit.sendEventActions`: count + scheduledCount only
|
|
241
|
+
- `diagnostics.inspectedEvents`: summary (id, name, status, closeMatchCount)
|
|
242
|
+
|
|
243
|
+
**Full mode (`compact: false`):**
|
|
244
|
+
|
|
245
|
+
Returns complete raw arrays:
|
|
246
|
+
- Orchestration timeline counts (`status`, `entityType`, top names)
|
|
247
|
+
- Audit category/action breakdown with ruleset durations
|
|
248
|
+
- Mutation payload evidence from ACTION audit (`DynamicUpdateMutation` request blocks)
|
|
249
|
+
- Webhook diagnostics (`Request Endpoint`, `Response code`, `Response Body`, headers)
|
|
250
|
+
- SendEvent payloads and future-dated scheduling evidence
|
|
251
|
+
- Rule exceptions (when `includeExceptions: true`)
|
|
252
|
+
- NO_MATCH closeMatches with mismatch reasons (when `includeNoMatchDetails: true`)
|
|
253
|
+
- Per-rule execution with props/timing (when `includeRuleDetails: true`)
|
|
254
|
+
- Custom plugin logs (when `includeCustomLogs: true`)
|
|
255
|
+
- Entity snapshots (when `includeSnapshots: true`)
|
|
256
|
+
- Cross-entity events with full events array (when `includeCrossEntity: true`)
|
|
257
|
+
- `NO_MATCH`/`PENDING`/`FAILED` drilldown with full event payload via `event.get`
|
|
258
|
+
- Auto-recommendations based on findings
|
|
259
|
+
|
|
260
|
+
Example (compact — recommended first call):
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"rootEntityRef": "E2E_MULTI_202602221343",
|
|
265
|
+
"rootEntityType": "ORDER"
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Example (full data with all sections):
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"rootEntityRef": "ORD-001",
|
|
274
|
+
"rootEntityType": "ORDER",
|
|
275
|
+
"compact": false,
|
|
276
|
+
"includeRuleDetails": true,
|
|
277
|
+
"includeCustomLogs": true,
|
|
278
|
+
"includeSnapshots": true,
|
|
279
|
+
"includeCrossEntity": true
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Example (lightweight):
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"rootEntityRef": "ORD-001",
|
|
288
|
+
"rootEntityType": "ORDER",
|
|
289
|
+
"includeEventDetails": false,
|
|
290
|
+
"includeExceptions": false,
|
|
291
|
+
"includeNoMatchDetails": false,
|
|
292
|
+
"maxDrilldowns": 0
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## `metrics.query`
|
|
297
|
+
|
|
298
|
+
Queries Prometheus metrics for Fluent runtime telemetry.
|
|
299
|
+
|
|
300
|
+
- **Required**: `query` (PromQL expression)
|
|
301
|
+
- **Optional**:
|
|
302
|
+
- `type`: `instant` (default) or `range`
|
|
303
|
+
- `time`: instant query timestamp
|
|
304
|
+
- `start`, `end`, `step`: required together for range queries
|
|
305
|
+
- `timeout`: query timeout in seconds (`1..120`)
|
|
306
|
+
- **Execution path**:
|
|
307
|
+
- instant -> GraphQL `metricInstant(query, time?)`
|
|
308
|
+
- range -> GraphQL `metricRange(query, start, end, step)`
|
|
309
|
+
- **Note**: `metrics.query` routes PromQL through Fluent's GraphQL proxy. Raw Prometheus REST endpoints are not relied on directly by this tool.
|
|
310
|
+
|
|
311
|
+
Label hygiene reminder:
|
|
312
|
+
- `core_event_received_total` does **not** include `status`
|
|
313
|
+
- `rubix_event_runtime_seconds_*` does include `status`
|
|
314
|
+
- histogram bucket series add `le`
|
|
315
|
+
|
|
316
|
+
Accurate counter delta pattern for wider windows:
|
|
317
|
+
|
|
318
|
+
```promql
|
|
319
|
+
(last_over_time(metric[window]) - metric offset <period>)
|
|
320
|
+
or
|
|
321
|
+
last_over_time(metric[window])
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Use the fallback `or` arm when offset samples are missing.
|
|
325
|
+
|
|
326
|
+
Instant example:
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"query": "sum(rate(rubix_event_runtime_seconds_count[5m]))",
|
|
331
|
+
"type": "instant"
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Range example:
|
|
336
|
+
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"query": "rate(rubix_event_runtime_seconds_count{status=\"FAILED\"}[5m])",
|
|
340
|
+
"type": "range",
|
|
341
|
+
"start": "2026-02-20T00:00:00Z",
|
|
342
|
+
"end": "2026-02-20T01:00:00Z",
|
|
343
|
+
"step": "1m"
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## `metrics.topEvents`
|
|
348
|
+
|
|
349
|
+
Returns ranked event analytics for a time window by aggregating Event API results.
|
|
350
|
+
|
|
351
|
+
- **Required**: `from`
|
|
352
|
+
- **Optional**:
|
|
353
|
+
- `to`: end timestamp (defaults to now)
|
|
354
|
+
- `entityType`: filter to one entity type
|
|
355
|
+
- `eventType`: defaults to `ORCHESTRATION`
|
|
356
|
+
- `topN`: ranked rows to return (`1..100`, default `20`)
|
|
357
|
+
- `maxPages`: Event API pagination cap (`1..50`, default `10`)
|
|
358
|
+
- **Output summary**:
|
|
359
|
+
- `totalEvents`, `failureRate`, `statusBreakdown`
|
|
360
|
+
- `topEvents[]` grouped by `name + entityType + status`
|
|
361
|
+
- `uniqueEventNames`, `uniqueEntityTypes`
|
|
362
|
+
|
|
363
|
+
Example:
|
|
364
|
+
|
|
365
|
+
```json
|
|
366
|
+
{
|
|
367
|
+
"from": "2026-02-22T00:00:00Z",
|
|
368
|
+
"to": "2026-02-22T12:00:00Z",
|
|
369
|
+
"topN": 20,
|
|
370
|
+
"eventType": "ORCHESTRATION"
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## `metrics.healthCheck`
|
|
375
|
+
|
|
376
|
+
Runs a compact, one-call anomaly assessment.
|
|
377
|
+
|
|
378
|
+
- **Optional**:
|
|
379
|
+
- `window`: Prometheus/Event API analysis window (`1h`, `6h`, `24h`, `7d`; default `1h`)
|
|
380
|
+
- `includeTopEvents`: include ranked event list in response (default `true`)
|
|
381
|
+
- `topN`: max top event rows (`1..100`, default `10`)
|
|
382
|
+
- `thresholds`: override defaults
|
|
383
|
+
- `failureRate` (default `5`)
|
|
384
|
+
- `pendingRate` (default `10`)
|
|
385
|
+
- `dominanceRate` (default `50`)
|
|
386
|
+
- **Behavior**:
|
|
387
|
+
- Prometheus-first (`metrics.query` equivalent calls under the hood)
|
|
388
|
+
- Falls back to Event API aggregation if Prometheus is unavailable
|
|
389
|
+
|
|
390
|
+
Example:
|
|
391
|
+
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"window": "1h",
|
|
395
|
+
"includeTopEvents": true,
|
|
396
|
+
"topN": 10
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## `metrics.sloReport`
|
|
401
|
+
|
|
402
|
+
Returns a managed-services SLO snapshot with explicit KPI fields and threshold findings.
|
|
403
|
+
|
|
404
|
+
- **Optional**:
|
|
405
|
+
- `window`: KPI window (`30m`, `1h`, `24h`, `7d`; default `1h`)
|
|
406
|
+
- `includeTopFailingEvents`: include top failed events from Event API (default `true`)
|
|
407
|
+
- `topN`: top failed rows (`1..100`, default `10`)
|
|
408
|
+
- `maxPages`: Event API pagination cap for fallback/failed ranking (`1..50`, default `10`)
|
|
409
|
+
- `thresholds`: override defaults
|
|
410
|
+
- `failureRate` (default `5`)
|
|
411
|
+
- `noMatchRate` (default `0`)
|
|
412
|
+
- `pendingRate` (default `10`)
|
|
413
|
+
- `runtimeP95Seconds` (default `5`)
|
|
414
|
+
- `inflightP95Seconds` (default `60`)
|
|
415
|
+
- **Output highlights**:
|
|
416
|
+
- total/failed/no-match/pending event counts
|
|
417
|
+
- failure/no-match/pending rates
|
|
418
|
+
- p95 runtime and inflight latency (null when Prometheus fallback path is used)
|
|
419
|
+
- findings + recommendations + source (`prometheus` or `event_api`)
|
|
420
|
+
|
|
421
|
+
Example:
|
|
422
|
+
|
|
423
|
+
```json
|
|
424
|
+
{
|
|
425
|
+
"window": "1h",
|
|
426
|
+
"includeTopFailingEvents": true,
|
|
427
|
+
"topN": 10
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## `metrics.labelCatalog`
|
|
432
|
+
|
|
433
|
+
Discovers what labels a metric supports so PromQL groupings are correct.
|
|
434
|
+
|
|
435
|
+
- **Required**: `metric`
|
|
436
|
+
- **Optional**:
|
|
437
|
+
- `window`: live series sampling window (`30m`, `1h`, `24h`, `7d`; default `24h`)
|
|
438
|
+
- `includeKnownLabels`: include Fluent-known label hints (default `true`)
|
|
439
|
+
- `maxValuesPerLabel`: max sample values returned per label (`1..50`, default `10`)
|
|
440
|
+
- **Behavior**:
|
|
441
|
+
- runs `last_over_time(<metric>[window])` as an instant query
|
|
442
|
+
- extracts label keys from returned vector series
|
|
443
|
+
- reports presence/cardinality/sample values per label
|
|
444
|
+
- merges known Fluent label hints when available
|
|
445
|
+
|
|
446
|
+
Example:
|
|
447
|
+
|
|
448
|
+
```json
|
|
449
|
+
{
|
|
450
|
+
"metric": "core_event_received_total",
|
|
451
|
+
"window": "1h",
|
|
452
|
+
"maxValuesPerLabel": 10
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## `workflow.transitions`
|
|
457
|
+
|
|
458
|
+
Query available user actions (transitions) for entities at a given workflow state.
|
|
459
|
+
|
|
460
|
+
Calls `POST /api/v4.1/transition` to discover what events can be fired, what attributes they require, and how they appear in the UI.
|
|
461
|
+
|
|
462
|
+
- **Required**: `triggers` (array), each trigger requires `retailerId`
|
|
463
|
+
- **Optional per trigger**: `type`, `subtype`, `status`, `module`, `flexType`, `flexVersion`, `name`
|
|
464
|
+
- **Retailer behavior**: `retailerId` is required per trigger. Falls back to `FLUENT_RETAILER_ID` when omitted.
|
|
465
|
+
|
|
466
|
+
### Use cases
|
|
467
|
+
|
|
468
|
+
- Discover available actions at any workflow status without reading workflow JSON
|
|
469
|
+
- Build dynamic E2E test sequences that adapt to workflow changes
|
|
470
|
+
- Validate that expected user actions are available after deployment
|
|
471
|
+
- Get required event attributes for each action (avoids missing-attribute errors)
|
|
472
|
+
|
|
473
|
+
### Integration with event.send
|
|
474
|
+
|
|
475
|
+
The `eventName` from each `userAction` maps directly to `event.send`'s `name` parameter. The `attributes[]` tell you what to include in `event.send`'s `attributes` parameter.
|
|
476
|
+
|
|
477
|
+
### Example: ORDER at CREATED status
|
|
478
|
+
|
|
479
|
+
```json
|
|
480
|
+
{
|
|
481
|
+
"triggers": [
|
|
482
|
+
{
|
|
483
|
+
"type": "ORDER",
|
|
484
|
+
"subtype": "HD",
|
|
485
|
+
"status": "CREATED",
|
|
486
|
+
"retailerId": "5",
|
|
487
|
+
"flexType": "ORDER::HD"
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Example: MANIFEST with module filter
|
|
494
|
+
|
|
495
|
+
```json
|
|
496
|
+
{
|
|
497
|
+
"triggers": [
|
|
498
|
+
{
|
|
499
|
+
"type": "MANIFEST",
|
|
500
|
+
"subtype": "DEFAULT",
|
|
501
|
+
"status": "PENDING",
|
|
502
|
+
"module": "servicepoint",
|
|
503
|
+
"flexType": "CARRIER::DEFAULT",
|
|
504
|
+
"retailerId": "2"
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Typical success shape (truncated):
|
|
511
|
+
|
|
512
|
+
```json
|
|
513
|
+
{
|
|
514
|
+
"ok": true,
|
|
515
|
+
"response": {
|
|
516
|
+
"response": [
|
|
517
|
+
{
|
|
518
|
+
"trigger": {
|
|
519
|
+
"type": "MANIFEST",
|
|
520
|
+
"subtype": "DEFAULT",
|
|
521
|
+
"status": "PENDING",
|
|
522
|
+
"module": "servicepoint",
|
|
523
|
+
"flexType": "CARRIER::DEFAULT",
|
|
524
|
+
"retailerId": "2"
|
|
525
|
+
},
|
|
526
|
+
"userActions": [
|
|
527
|
+
{
|
|
528
|
+
"eventName": "UPDATE",
|
|
529
|
+
"context": [
|
|
530
|
+
{ "label": "SUBMIT", "type": "PRIMARY", "modules": ["servicepoint"], "confirm": false }
|
|
531
|
+
],
|
|
532
|
+
"attributes": []
|
|
533
|
+
}
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
]
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Dynamic test sequence pattern
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
1. Create entity via graphql.query (mutation)
|
|
545
|
+
2. workflow.transitions → get available actions at current status
|
|
546
|
+
3. For each action:
|
|
547
|
+
a. event.send with eventName and required attributes from response
|
|
548
|
+
b. Poll entity status until transition completes
|
|
549
|
+
c. workflow.transitions → get next available actions
|
|
550
|
+
4. Repeat until no more userActions (terminal state)
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## `plugin.list`
|
|
554
|
+
|
|
555
|
+
List all registered orchestration rules (standard + custom) with metadata.
|
|
556
|
+
|
|
557
|
+
Calls `GET /orchestration/rest/v1/plugin` and returns a map keyed by fully-qualified rule name (e.g. `ACCOUNT.context.RuleName`).
|
|
558
|
+
|
|
559
|
+
- **Optional**: `name` (string) — case-insensitive substring filter on rule keys
|
|
560
|
+
- **Requires**: SDK client with `request` method
|
|
561
|
+
|
|
562
|
+
### Each entry contains
|
|
563
|
+
|
|
564
|
+
- `ruleInfo`: name, description, accepted entity types, produced events
|
|
565
|
+
- `eventAttributes`: attributes the rule reads from events
|
|
566
|
+
- `parameters`: configurable rule parameters (with types and descriptions)
|
|
567
|
+
|
|
568
|
+
### Use cases
|
|
569
|
+
|
|
570
|
+
- Understand what rules do when analyzing workflows
|
|
571
|
+
- Discover all registered rules (standard + custom) for a given account
|
|
572
|
+
- Find rules by name pattern (e.g. all "SendEvent" variants)
|
|
573
|
+
- Cross-reference deployed rules with local source code for module validation
|
|
574
|
+
|
|
575
|
+
### Example: list all rules
|
|
576
|
+
|
|
577
|
+
```json
|
|
578
|
+
{}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Example: filter by name
|
|
582
|
+
|
|
583
|
+
```json
|
|
584
|
+
{
|
|
585
|
+
"name": "SendEvent"
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Typical success shape (truncated):
|
|
590
|
+
|
|
591
|
+
```json
|
|
592
|
+
{
|
|
593
|
+
"ok": true,
|
|
594
|
+
"response": {
|
|
595
|
+
"FLUENTRETAIL.base.SendEvent": {
|
|
596
|
+
"ruleInfo": {
|
|
597
|
+
"name": "SendEvent",
|
|
598
|
+
"description": "Sends an event to a specified entity",
|
|
599
|
+
"accepts": ["ORDER", "FULFILMENT", "ARTICLE"],
|
|
600
|
+
"produces": ["SendEvent"]
|
|
601
|
+
},
|
|
602
|
+
"eventAttributes": [
|
|
603
|
+
{ "name": "eventName", "type": "STRING", "description": "Name of the event to send" }
|
|
604
|
+
],
|
|
605
|
+
"parameters": [
|
|
606
|
+
{ "name": "eventName", "type": "STRING", "description": "Event name to dispatch" }
|
|
607
|
+
]
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
## `graphql.query`
|
|
614
|
+
|
|
615
|
+
Executes a Fluent Commerce GraphQL query or mutation through the SDK.
|
|
616
|
+
|
|
617
|
+
- **Required**: `query`
|
|
618
|
+
- **Optional**: `variables`
|
|
619
|
+
- **Retry behavior**:
|
|
620
|
+
- query operations use configured retry/backoff
|
|
621
|
+
- mutation operations disable retries automatically to avoid duplicate writes
|
|
622
|
+
|
|
623
|
+
### Basic Query
|
|
624
|
+
|
|
625
|
+
```json
|
|
626
|
+
{
|
|
627
|
+
"query": "query { orders(first: 5) { edges { cursor node { id ref status } } pageInfo { hasNextPage } } }"
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Pagination
|
|
632
|
+
|
|
633
|
+
Fluent uses Relay-style connections. Cursors live on each **edge**, not on `pageInfo`. There is no `endCursor` or `startCursor`.
|
|
634
|
+
|
|
635
|
+
```json
|
|
636
|
+
{
|
|
637
|
+
"query": "query($cursor: String) { orders(first: 50, after: $cursor) { edges { cursor node { id ref status } } pageInfo { hasNextPage } } }",
|
|
638
|
+
"variables": { "cursor": "Y3Vyc29yOi0tLTM2X18xNzcxMTc2MzMyMTc0" }
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
To paginate: take the `cursor` from the **last edge** in the response, pass it as the `after` variable. Repeat while `hasNextPage` is `true`.
|
|
643
|
+
|
|
644
|
+
Pagination args: `first`/`after` (forward), `last`/`before` (backward).
|
|
645
|
+
|
|
646
|
+
### Mutation
|
|
647
|
+
|
|
648
|
+
```json
|
|
649
|
+
{
|
|
650
|
+
"query": "mutation($input: UpdateOrderInput!) { updateOrder(input: $input) { id ref status } }",
|
|
651
|
+
"variables": { "input": { "id": "36", "status": "RECEIVED" } }
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
### Synchronous Fulfilment Options (Live Checkout)
|
|
656
|
+
|
|
657
|
+
`graphql.query` supports synchronous fulfilment options orchestration calls,
|
|
658
|
+
commonly used by checkout journeys to get plans in the same response.
|
|
659
|
+
|
|
660
|
+
```json
|
|
661
|
+
{
|
|
662
|
+
"query": "mutation CreateFulfilmentOption($retailerId: Int!, $type: String!, $orderType: String!, $ref: String!, $products: [CreateFulfilmentOptionProductInput!], $longitude: Float!, $latitude: Float!, $radius: Json!) { createFulfilmentOption(input: { retailerId: $retailerId, type: $type, orderType: $orderType, ref: $ref, products: $products, address: { city: \"Kellyville Ridge\", state: \"New South Wales\", country: \"Australia\", postcode: \"2155\", addressLine1: \"Kellyville Ridge NSW, Australia\", latitude: $latitude, longitude: $longitude }, attributes: { name: \"radius\", type: \"DOUBLE\", value: $radius } }, executionMode: AWAIT_ORCHESTRATION) { id createdOn plans { edges { node { ref status eta splitCount fulfilments { locationRef eta items { productRef availableQuantity requestedQuantity } } } } } } }",
|
|
663
|
+
"variables": {
|
|
664
|
+
"retailerId": 5,
|
|
665
|
+
"type": "CC",
|
|
666
|
+
"orderType": "CC",
|
|
667
|
+
"ref": "CHECKOUT-FO-12345",
|
|
668
|
+
"products": [
|
|
669
|
+
{ "productRef": "MP09-34-Blue", "requestedQuantity": 1, "catalogueRef": "" }
|
|
670
|
+
],
|
|
671
|
+
"longitude": 150.9193671,
|
|
672
|
+
"latitude": -33.7068442,
|
|
673
|
+
"radius": 50
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
Notes:
|
|
679
|
+
- `executionMode: AWAIT_ORCHESTRATION` makes the call synchronous.
|
|
680
|
+
- The environment must contain a matching workflow for `type` (for example `FULFILMENT_OPTIONS::CC`).
|
|
681
|
+
- If no matching workflow exists, Fluent returns a backend workflow-not-found error.
|
|
682
|
+
|
|
683
|
+
### Connection Shape
|
|
684
|
+
|
|
685
|
+
All list queries return connections:
|
|
686
|
+
|
|
687
|
+
```graphql
|
|
688
|
+
{
|
|
689
|
+
edges {
|
|
690
|
+
cursor
|
|
691
|
+
node { ...fields }
|
|
692
|
+
}
|
|
693
|
+
pageInfo { hasNextPage }
|
|
694
|
+
}
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
Common query roots: `orders`, `fulfilments`, `fulfilmentChoices`, `locations`, `inventoryPositions`, `products`, `categories`, `settings`, `waves`, `articles`.
|
|
698
|
+
|
|
699
|
+
## `batch.create`
|
|
700
|
+
|
|
701
|
+
Creates a Fluent ingestion job.
|
|
702
|
+
|
|
703
|
+
- **Required**: `name`
|
|
704
|
+
- **Optional**: `retailerId`, `entityType`, `action`
|
|
705
|
+
- **Note**: `retailerId` falls back to `FLUENT_RETAILER_ID` if omitted
|
|
706
|
+
|
|
707
|
+
Example:
|
|
708
|
+
|
|
709
|
+
```json
|
|
710
|
+
{
|
|
711
|
+
"name": "inventory-load-2026-02-06",
|
|
712
|
+
"entityType": "INVENTORY_POSITION",
|
|
713
|
+
"action": "UPSERT"
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
## `batch.send`
|
|
718
|
+
|
|
719
|
+
Sends payload records to an existing batch job.
|
|
720
|
+
|
|
721
|
+
- **Required**:
|
|
722
|
+
- `jobId`
|
|
723
|
+
- `payload.action`
|
|
724
|
+
- `payload.entityType`
|
|
725
|
+
- `payload.entities` (non-empty array)
|
|
726
|
+
|
|
727
|
+
Example:
|
|
728
|
+
|
|
729
|
+
```json
|
|
730
|
+
{
|
|
731
|
+
"jobId": "JOB-123",
|
|
732
|
+
"payload": {
|
|
733
|
+
"action": "UPSERT",
|
|
734
|
+
"entityType": "INVENTORY_POSITION",
|
|
735
|
+
"entities": [
|
|
736
|
+
{
|
|
737
|
+
"locationRef": "LOC-1",
|
|
738
|
+
"skuRef": "SKU-1",
|
|
739
|
+
"qty": 10,
|
|
740
|
+
"type": "LAST_ON_HAND",
|
|
741
|
+
"status": "ACTIVE"
|
|
742
|
+
}
|
|
743
|
+
]
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
## `batch.status`
|
|
749
|
+
|
|
750
|
+
Reads status for a previously created batch job.
|
|
751
|
+
|
|
752
|
+
- **Required**: `jobId`
|
|
753
|
+
|
|
754
|
+
Example:
|
|
755
|
+
|
|
756
|
+
```json
|
|
757
|
+
{
|
|
758
|
+
"jobId": "JOB-123"
|
|
759
|
+
}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
## `batch.batchStatus`
|
|
763
|
+
|
|
764
|
+
Gets the status of a specific batch within a job. Useful for troubleshooting partial failures in multi-batch jobs.
|
|
765
|
+
|
|
766
|
+
- **Required**: `jobId`, `batchId`
|
|
767
|
+
|
|
768
|
+
Example:
|
|
769
|
+
|
|
770
|
+
```json
|
|
771
|
+
{
|
|
772
|
+
"jobId": "JOB-123",
|
|
773
|
+
"batchId": "BATCH-456"
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
## `batch.results`
|
|
778
|
+
|
|
779
|
+
Gets per-record outcomes for a completed job. Call after `batch.status` reports a terminal state.
|
|
780
|
+
|
|
781
|
+
- **Required**: `jobId`
|
|
782
|
+
|
|
783
|
+
Example:
|
|
784
|
+
|
|
785
|
+
```json
|
|
786
|
+
{
|
|
787
|
+
"jobId": "JOB-123"
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## `graphql.queryAll`
|
|
794
|
+
|
|
795
|
+
Executes a GraphQL query with SDK auto-pagination. Automatically follows cursors, merges all edges, and deduplicates by node ID. Use instead of `graphql.query` when you need ALL records from a connection.
|
|
796
|
+
|
|
797
|
+
- **Required**: `query`
|
|
798
|
+
- **Optional**:
|
|
799
|
+
- `variables`: query variables (include cursor variable, usually `null` for first page)
|
|
800
|
+
- `maxPages`: max pages to fetch (default: 100, max: 500)
|
|
801
|
+
- `maxRecords`: max total records to accumulate (default: 10000, max: 50000)
|
|
802
|
+
- `timeoutMs`: hard timeout for entire pagination run (default: 300000ms = 5 min)
|
|
803
|
+
- `direction`: `forward` (first/after) or `backward` (last/before)
|
|
804
|
+
- `errorHandling`: `throw` (fail on errors) or `partial` (return partial data + errors)
|
|
805
|
+
- **Timeout behavior**:
|
|
806
|
+
- uses `timeoutMs` for the whole pagination run
|
|
807
|
+
- independent from `FLUENT_REQUEST_TIMEOUT_MS` single-call timeout
|
|
808
|
+
|
|
809
|
+
### How it works
|
|
810
|
+
|
|
811
|
+
1. SDK detects pagination variables (`first`/`after` or `last`/`before`) in your query
|
|
812
|
+
2. Executes the first page
|
|
813
|
+
3. Extracts cursor from the last edge, follows `hasNextPage`
|
|
814
|
+
4. Merges edges across pages, deduplicates by node ID
|
|
815
|
+
5. Stops when: no more pages, maxPages reached, maxRecords reached, or timeout
|
|
816
|
+
|
|
817
|
+
### Response
|
|
818
|
+
|
|
819
|
+
Includes `extensions.autoPagination`:
|
|
820
|
+
|
|
821
|
+
```json
|
|
822
|
+
{
|
|
823
|
+
"totalPages": 5,
|
|
824
|
+
"totalRecords": 487,
|
|
825
|
+
"truncated": false,
|
|
826
|
+
"direction": "forward"
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
If truncated: `truncationReason` will be `"maxPages"`, `"maxRecords"`, or `"timeout"`.
|
|
831
|
+
|
|
832
|
+
### Example: Fetch all active orders
|
|
833
|
+
|
|
834
|
+
```json
|
|
835
|
+
{
|
|
836
|
+
"query": "query($cursor: String) { orders(first: 100, after: $cursor, status: \"ACTIVE\") { edges { cursor node { id ref status createdOn } } pageInfo { hasNextPage } } }",
|
|
837
|
+
"variables": { "cursor": null },
|
|
838
|
+
"maxRecords": 5000
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
### Example: Fetch all locations (backward)
|
|
843
|
+
|
|
844
|
+
```json
|
|
845
|
+
{
|
|
846
|
+
"query": "query($cursor: String) { locations(last: 50, before: $cursor) { edges { cursor node { id ref name type } } pageInfo { hasPreviousPage } } }",
|
|
847
|
+
"variables": { "cursor": null },
|
|
848
|
+
"direction": "backward"
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
## `graphql.batchMutate`
|
|
853
|
+
|
|
854
|
+
Executes multiple GraphQL mutations in a single request using aliased mutations. Sends up to 50 mutations at once, each with its own input.
|
|
855
|
+
|
|
856
|
+
- **Required**: `mutation`, `inputs`
|
|
857
|
+
- **Optional**: `returnFields`, `operationName`
|
|
858
|
+
- **Retry behavior**: disabled (non-idempotent write operation)
|
|
859
|
+
|
|
860
|
+
### How it works
|
|
861
|
+
|
|
862
|
+
1. Builds an aliased mutation query:
|
|
863
|
+
```graphql
|
|
864
|
+
mutation BatchUpdateOrders($input1: UpdateOrderInput!, $input2: UpdateOrderInput!) {
|
|
865
|
+
updateOrder1: updateOrder(input: $input1) { id ref status }
|
|
866
|
+
updateOrder2: updateOrder(input: $input2) { id ref status }
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
2. Sends as a single GraphQL request
|
|
870
|
+
3. Parses per-mutation results (success/failure for each input)
|
|
871
|
+
|
|
872
|
+
### Response
|
|
873
|
+
|
|
874
|
+
```json
|
|
875
|
+
{
|
|
876
|
+
"ok": true,
|
|
877
|
+
"summary": "All 3 mutations succeeded",
|
|
878
|
+
"executed": 3,
|
|
879
|
+
"failed": 0,
|
|
880
|
+
"allSucceeded": true,
|
|
881
|
+
"allFailed": false,
|
|
882
|
+
"results": [
|
|
883
|
+
{ "alias": "updateOrder1", "index": 0, "success": true, "data": { "id": "1", "ref": "ORD-001", "status": "SHIPPED" } },
|
|
884
|
+
{ "alias": "updateOrder2", "index": 1, "success": true, "data": { "id": "2", "ref": "ORD-002", "status": "SHIPPED" } }
|
|
885
|
+
]
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
On partial failure, `errors` array includes per-mutation error details with `alias`, `index`, `message`, and `inputRef`.
|
|
890
|
+
|
|
891
|
+
### Example: Bulk update order statuses
|
|
892
|
+
|
|
893
|
+
```json
|
|
894
|
+
{
|
|
895
|
+
"mutation": "updateOrder",
|
|
896
|
+
"inputs": [
|
|
897
|
+
{ "id": "36", "status": "SHIPPED" },
|
|
898
|
+
{ "id": "37", "status": "SHIPPED" },
|
|
899
|
+
{ "id": "38", "status": "SHIPPED" }
|
|
900
|
+
],
|
|
901
|
+
"returnFields": ["id", "ref", "status"]
|
|
902
|
+
}
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
### Example: Batch create inventory positions
|
|
906
|
+
|
|
907
|
+
```json
|
|
908
|
+
{
|
|
909
|
+
"mutation": "updateInventoryPosition",
|
|
910
|
+
"inputs": [
|
|
911
|
+
{ "id": "100", "status": "ACTIVE" },
|
|
912
|
+
{ "id": "101", "status": "ACTIVE" }
|
|
913
|
+
],
|
|
914
|
+
"returnFields": ["id", "ref", "status", "onHand"]
|
|
915
|
+
}
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
## `graphql.introspect`
|
|
919
|
+
|
|
920
|
+
Inspects the Fluent Commerce GraphQL schema via introspection. Fetches the full schema and caches it for 1 hour. Use to discover mutations, input types, and field requirements at runtime.
|
|
921
|
+
|
|
922
|
+
- **Optional** (specify one mode):
|
|
923
|
+
- `type`: name of an INPUT_OBJECT type to inspect (e.g., `UpdateOrderInput`)
|
|
924
|
+
- `mutation`: name of a mutation to inspect (e.g., `updateOrder`)
|
|
925
|
+
- `listMutations`: `true` to list all available mutation names
|
|
926
|
+
- `listQueries`: `true` to list all available query root field names
|
|
927
|
+
|
|
928
|
+
### Example: List all mutations
|
|
929
|
+
|
|
930
|
+
```json
|
|
931
|
+
{
|
|
932
|
+
"listMutations": true
|
|
933
|
+
}
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
Response:
|
|
937
|
+
|
|
938
|
+
```json
|
|
939
|
+
{
|
|
940
|
+
"ok": true,
|
|
941
|
+
"mutations": ["createOrder", "updateOrder", "createFulfilment", "..."],
|
|
942
|
+
"count": 142
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
### Example: Inspect a mutation
|
|
947
|
+
|
|
948
|
+
```json
|
|
949
|
+
{
|
|
950
|
+
"mutation": "updateOrder"
|
|
951
|
+
}
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
Response:
|
|
955
|
+
|
|
956
|
+
```json
|
|
957
|
+
{
|
|
958
|
+
"ok": true,
|
|
959
|
+
"mutation": {
|
|
960
|
+
"name": "updateOrder",
|
|
961
|
+
"description": "Updates an existing order",
|
|
962
|
+
"args": [
|
|
963
|
+
{
|
|
964
|
+
"name": "input",
|
|
965
|
+
"type": "UpdateOrderInput!",
|
|
966
|
+
"required": true
|
|
967
|
+
}
|
|
968
|
+
],
|
|
969
|
+
"returnType": "Order"
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### Example: Inspect an input type
|
|
975
|
+
|
|
976
|
+
```json
|
|
977
|
+
{
|
|
978
|
+
"type": "UpdateOrderInput"
|
|
979
|
+
}
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
Response:
|
|
983
|
+
|
|
984
|
+
```json
|
|
985
|
+
{
|
|
986
|
+
"ok": true,
|
|
987
|
+
"inputType": {
|
|
988
|
+
"name": "UpdateOrderInput",
|
|
989
|
+
"fields": [
|
|
990
|
+
{ "name": "id", "type": "ID!", "required": true },
|
|
991
|
+
{ "name": "ref", "type": "String", "required": false },
|
|
992
|
+
{ "name": "status", "type": "String", "required": false },
|
|
993
|
+
{ "name": "attributes", "type": "[AttributeInput]", "required": false, "isArray": true }
|
|
994
|
+
]
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
### Chaining example
|
|
1000
|
+
|
|
1001
|
+
1. `{ "listMutations": true }` → find `updateOrder`
|
|
1002
|
+
2. `{ "mutation": "updateOrder" }` → see it takes `UpdateOrderInput!`
|
|
1003
|
+
3. `{ "type": "UpdateOrderInput" }` → see all updatable fields
|
|
1004
|
+
4. Use `graphql.query` or `graphql.batchMutate` with the discovered schema
|
|
1005
|
+
|
|
1006
|
+
## `connection.test`
|
|
1007
|
+
|
|
1008
|
+
Comprehensive Fluent Commerce connectivity test. Authenticates, executes a `me` query, and returns the authenticated user's details.
|
|
1009
|
+
|
|
1010
|
+
- **Input**: no fields
|
|
1011
|
+
|
|
1012
|
+
### Response (success)
|
|
1013
|
+
|
|
1014
|
+
```json
|
|
1015
|
+
{
|
|
1016
|
+
"ok": true,
|
|
1017
|
+
"duration": 342,
|
|
1018
|
+
"details": {
|
|
1019
|
+
"userId": "12",
|
|
1020
|
+
"username": "admin@hmtest.com",
|
|
1021
|
+
"userType": "RETAILER",
|
|
1022
|
+
"userStatus": "ACTIVE",
|
|
1023
|
+
"retailerId": "5",
|
|
1024
|
+
"retailerRef": "HMTEST",
|
|
1025
|
+
"retailerName": "HM Test",
|
|
1026
|
+
"locationId": "10",
|
|
1027
|
+
"locationRef": "W252",
|
|
1028
|
+
"locationName": "Warehouse 252"
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### Response (failure)
|
|
1034
|
+
|
|
1035
|
+
```json
|
|
1036
|
+
{
|
|
1037
|
+
"ok": false,
|
|
1038
|
+
"duration": 1500,
|
|
1039
|
+
"error": "Authentication failed after 3 retries: 401 Unauthorized"
|
|
1040
|
+
}
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
More thorough than `health.ping` — actually verifies the GraphQL endpoint works end-to-end. Use when first connecting, debugging auth issues, or verifying retailer/location context.
|
|
1044
|
+
|
|
1045
|
+
## `webhook.validate`
|
|
1046
|
+
|
|
1047
|
+
Validates a Fluent Commerce webhook payload and optionally verifies its signature.
|
|
1048
|
+
|
|
1049
|
+
- **Required**: `payload`
|
|
1050
|
+
- **Optional**:
|
|
1051
|
+
- `rawBody`: exact original HTTP request body string for signature checks
|
|
1052
|
+
- `signature`: the `X-Fluent-Signature` header value (base64-encoded)
|
|
1053
|
+
- `publicKey`: the Fluent public key (PEM format) for signature verification
|
|
1054
|
+
- `algorithm`: `SHA512withRSA` (default) or `MD5withRSA`
|
|
1055
|
+
|
|
1056
|
+
### Mode 1: Basic validation
|
|
1057
|
+
|
|
1058
|
+
Checks that required fields (`name`, `id`, `retailerId`) are present in the payload.
|
|
1059
|
+
|
|
1060
|
+
```json
|
|
1061
|
+
{
|
|
1062
|
+
"payload": {
|
|
1063
|
+
"name": "OrderCreated",
|
|
1064
|
+
"id": "event-123",
|
|
1065
|
+
"retailerId": "5",
|
|
1066
|
+
"entityType": "ORDER",
|
|
1067
|
+
"entityRef": "ORD-001"
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
Response:
|
|
1073
|
+
|
|
1074
|
+
```json
|
|
1075
|
+
{
|
|
1076
|
+
"ok": true,
|
|
1077
|
+
"basicValidation": { "valid": true, "missingFields": [] },
|
|
1078
|
+
"note": "Basic field validation passed. Provide signature + publicKey for signature verification."
|
|
1079
|
+
}
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
### Mode 2: Signature validation
|
|
1083
|
+
|
|
1084
|
+
Verifies the webhook body against the `X-Fluent-Signature` header using the provided public key.
|
|
1085
|
+
Use `rawBody` whenever possible because signature verification is byte-sensitive.
|
|
1086
|
+
|
|
1087
|
+
```json
|
|
1088
|
+
{
|
|
1089
|
+
"payload": {
|
|
1090
|
+
"name": "OrderCreated",
|
|
1091
|
+
"id": "event-123",
|
|
1092
|
+
"retailerId": "5"
|
|
1093
|
+
},
|
|
1094
|
+
"rawBody": "{\"name\":\"OrderCreated\",\"id\":\"event-123\",\"retailerId\":\"5\"}",
|
|
1095
|
+
"signature": "base64-encoded-signature-from-header",
|
|
1096
|
+
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----",
|
|
1097
|
+
"algorithm": "SHA512withRSA"
|
|
1098
|
+
}
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
Response:
|
|
1102
|
+
|
|
1103
|
+
```json
|
|
1104
|
+
{
|
|
1105
|
+
"ok": true,
|
|
1106
|
+
"basicValidation": { "valid": true, "missingFields": [] },
|
|
1107
|
+
"signatureValidation": {
|
|
1108
|
+
"valid": true,
|
|
1109
|
+
"algorithm": "SHA512withRSA",
|
|
1110
|
+
"payloadSource": "rawBody"
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
---
|
|
1116
|
+
|
|
1117
|
+
## `entity.create`
|
|
1118
|
+
|
|
1119
|
+
Type-safe entity creation with built-in validation and gotcha knowledge.
|
|
1120
|
+
|
|
1121
|
+
**Supported entity types:** ORDER, FULFILMENT, LOCATION, NETWORK, CUSTOMER, PRODUCT, INVENTORY_POSITION, VIRTUAL_CATALOGUE, VIRTUAL_POSITION, CATEGORY, CARRIER, SETTING
|
|
1122
|
+
|
|
1123
|
+
- **Required**:
|
|
1124
|
+
- `entityType`: entity type string (see supported list above)
|
|
1125
|
+
- `data`: creation input fields matching the GraphQL create input type
|
|
1126
|
+
- **Optional**:
|
|
1127
|
+
- `returnFields`: array of field names to return (defaults to entity type defaults)
|
|
1128
|
+
- `dryRun`: if `true`, builds and validates the mutation without executing (default `false`)
|
|
1129
|
+
|
|
1130
|
+
**Key behaviors:**
|
|
1131
|
+
|
|
1132
|
+
- Validates required fields BEFORE sending (e.g., Location requires `openingSchedule`)
|
|
1133
|
+
- Encodes compound key rules (ProductKey needs `ref` + `catalogue.ref`)
|
|
1134
|
+
- Auto-resolves `retailerId` from config for retailer-scoped entities
|
|
1135
|
+
- Returns the executed mutation string for audit trail
|
|
1136
|
+
|
|
1137
|
+
**Known gotchas (encoded in validation):**
|
|
1138
|
+
|
|
1139
|
+
- No create inputs have a `status` field — status is auto-set to `CREATED`
|
|
1140
|
+
- `Customer` has NO `ref` field; `username` is the identifier
|
|
1141
|
+
- `Network` uses `name` (not `ref`) in create; `retailers` is a plural array
|
|
1142
|
+
- `Location` requires `openingSchedule` even for 24/7 (use `allHours: true`)
|
|
1143
|
+
- `Product` `gtin` has a 20-character max
|
|
1144
|
+
- `Setting` `context` is a plain String, `contextId` is a separate Int
|
|
1145
|
+
|
|
1146
|
+
**Example** — create a location (dry run):
|
|
1147
|
+
|
|
1148
|
+
```json
|
|
1149
|
+
{
|
|
1150
|
+
"entityType": "LOCATION",
|
|
1151
|
+
"data": {
|
|
1152
|
+
"ref": "LOC_WH_01",
|
|
1153
|
+
"type": "WAREHOUSE",
|
|
1154
|
+
"name": "Main Warehouse",
|
|
1155
|
+
"openingSchedule": { "allHours": true }
|
|
1156
|
+
},
|
|
1157
|
+
"dryRun": true
|
|
1158
|
+
}
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
Response (dry run):
|
|
1162
|
+
|
|
1163
|
+
```json
|
|
1164
|
+
{
|
|
1165
|
+
"ok": true,
|
|
1166
|
+
"dryRun": true,
|
|
1167
|
+
"entityType": "LOCATION",
|
|
1168
|
+
"mutation": "mutation CreateLocation($input: CreateLocationInput!) { createLocation(input: $input) { id ref status type name } }",
|
|
1169
|
+
"inputType": "CreateLocationInput",
|
|
1170
|
+
"variables": { "input": { "ref": "LOC_WH_01", "type": "WAREHOUSE", "name": "Main Warehouse", "openingSchedule": { "allHours": true } } },
|
|
1171
|
+
"requiredFields": ["ref", "type", "name", "openingSchedule"],
|
|
1172
|
+
"gotchas": ["Location requires openingSchedule — use { allHours: true } for 24/7"],
|
|
1173
|
+
"note": "No API call made. Set dryRun=false to execute."
|
|
1174
|
+
}
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1177
|
+
**Example** — create a location (real):
|
|
1178
|
+
|
|
1179
|
+
```json
|
|
1180
|
+
{
|
|
1181
|
+
"entityType": "LOCATION",
|
|
1182
|
+
"data": {
|
|
1183
|
+
"ref": "LOC_WH_01",
|
|
1184
|
+
"type": "WAREHOUSE",
|
|
1185
|
+
"name": "Main Warehouse",
|
|
1186
|
+
"openingSchedule": { "allHours": true }
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
Response:
|
|
1192
|
+
|
|
1193
|
+
```json
|
|
1194
|
+
{
|
|
1195
|
+
"ok": true,
|
|
1196
|
+
"entityType": "LOCATION",
|
|
1197
|
+
"entity": { "id": "42", "ref": "LOC_WH_01", "status": "CREATED", "type": "WAREHOUSE", "name": "Main Warehouse" },
|
|
1198
|
+
"mutation": "mutation CreateLocation($input: CreateLocationInput!) { ... }"
|
|
1199
|
+
}
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
---
|
|
1203
|
+
|
|
1204
|
+
## `entity.update`
|
|
1205
|
+
|
|
1206
|
+
Status-aware entity updates with optional transition validation.
|
|
1207
|
+
|
|
1208
|
+
- **Required**:
|
|
1209
|
+
- `entityType`: entity type string
|
|
1210
|
+
- `id`: entity ID (required for updates)
|
|
1211
|
+
- `fields`: object of fields to update (matches GraphQL update input type)
|
|
1212
|
+
- **Optional**:
|
|
1213
|
+
- `returnFields`: array of field names to return
|
|
1214
|
+
- `validateTransition`: if `true` and `status` is being changed, queries `workflow.transitions` to verify the transition is allowed before executing (default `false`)
|
|
1215
|
+
|
|
1216
|
+
**Key behaviors:**
|
|
1217
|
+
|
|
1218
|
+
- Auto-detects the correct GraphQL mutation name from entity type
|
|
1219
|
+
- When `validateTransition` is `true`: fetches current entity state, queries transition API, warns if no workflow transitions exist from the current status
|
|
1220
|
+
- Returns the previous status for audit trail when transition validation is used
|
|
1221
|
+
- Uses no-retry semantics (write operation)
|
|
1222
|
+
|
|
1223
|
+
**Example** — update order status with transition validation:
|
|
1224
|
+
|
|
1225
|
+
```json
|
|
1226
|
+
{
|
|
1227
|
+
"entityType": "ORDER",
|
|
1228
|
+
"id": "12345",
|
|
1229
|
+
"fields": { "status": "COMPLETE" },
|
|
1230
|
+
"validateTransition": true
|
|
1231
|
+
}
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
Response:
|
|
1235
|
+
|
|
1236
|
+
```json
|
|
1237
|
+
{
|
|
1238
|
+
"ok": true,
|
|
1239
|
+
"entityType": "ORDER",
|
|
1240
|
+
"entity": { "id": "12345", "ref": "HD-001", "status": "COMPLETE" },
|
|
1241
|
+
"mutation": "mutation UpdateOrder($input: UpdateOrderInput!) { ... }"
|
|
1242
|
+
}
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
If no workflow transition exists:
|
|
1246
|
+
|
|
1247
|
+
```json
|
|
1248
|
+
{
|
|
1249
|
+
"ok": true,
|
|
1250
|
+
"entityType": "ORDER",
|
|
1251
|
+
"entity": { "id": "12345", "ref": "HD-001", "status": "COMPLETE" },
|
|
1252
|
+
"mutation": "...",
|
|
1253
|
+
"transitionWarning": "No workflow transitions available from status \"BOOKED\" for ORDER. The status update may succeed as a direct mutation but will not trigger workflow orchestration."
|
|
1254
|
+
}
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
---
|
|
1258
|
+
|
|
1259
|
+
## `entity.get`
|
|
1260
|
+
|
|
1261
|
+
Unified entity lookup by ID or ref with optional edge inclusion.
|
|
1262
|
+
|
|
1263
|
+
- **Required**:
|
|
1264
|
+
- `entityType`: entity type string
|
|
1265
|
+
- At least one of `id` or `ref`
|
|
1266
|
+
- **Optional**:
|
|
1267
|
+
- `id`: entity ID (preferred lookup method)
|
|
1268
|
+
- `ref`: entity ref (fallback — not available for CUSTOMER)
|
|
1269
|
+
- `fields`: array of field names to return (defaults to entity type defaults: id, ref, status, type, createdOn, etc.)
|
|
1270
|
+
- `includeEdges`: array of related entity edges to fetch (e.g., `["fulfilments", "items", "attributes"]`)
|
|
1271
|
+
|
|
1272
|
+
**Key behaviors:**
|
|
1273
|
+
|
|
1274
|
+
- Prefers ID-based lookup (single entity query) over ref-based lookup (connection query)
|
|
1275
|
+
- Ref-based lookup uses the connection query pattern (edges/node)
|
|
1276
|
+
- Returns `{ found: false }` when entity is not found (not an error)
|
|
1277
|
+
- CUSTOMER entity type does not support ref-based lookup
|
|
1278
|
+
|
|
1279
|
+
**Example** — get order by ref with fulfilments:
|
|
1280
|
+
|
|
1281
|
+
```json
|
|
1282
|
+
{
|
|
1283
|
+
"entityType": "ORDER",
|
|
1284
|
+
"ref": "HD-001",
|
|
1285
|
+
"includeEdges": ["fulfilments"]
|
|
1286
|
+
}
|
|
1287
|
+
```
|
|
1288
|
+
|
|
1289
|
+
Response:
|
|
1290
|
+
|
|
1291
|
+
```json
|
|
1292
|
+
{
|
|
1293
|
+
"ok": true,
|
|
1294
|
+
"found": true,
|
|
1295
|
+
"entityType": "ORDER",
|
|
1296
|
+
"entity": {
|
|
1297
|
+
"id": "12345",
|
|
1298
|
+
"ref": "HD-001",
|
|
1299
|
+
"status": "BOOKED",
|
|
1300
|
+
"type": "HD",
|
|
1301
|
+
"fulfilments": {
|
|
1302
|
+
"edges": [
|
|
1303
|
+
{ "node": { "id": "67890", "ref": "FUL-001", "status": "CREATED" } }
|
|
1304
|
+
]
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
```
|
|
1309
|
+
|
|
1310
|
+
---
|
|
1311
|
+
|
|
1312
|
+
## `workflow.upload`
|
|
1313
|
+
|
|
1314
|
+
Deploy a workflow JSON definition to the Fluent environment.
|
|
1315
|
+
|
|
1316
|
+
Uploads via REST API `POST /api/v4.1/workflow/{retailerId}` — the same endpoint the Fluent CLI uses.
|
|
1317
|
+
|
|
1318
|
+
- **Required**:
|
|
1319
|
+
- `workflow`: workflow JSON definition (as object or JSON string)
|
|
1320
|
+
- **Optional**:
|
|
1321
|
+
- `retailerId`: target retailer ID (falls back to `FLUENT_RETAILER_ID`)
|
|
1322
|
+
- `validate`: validate structure before uploading (default `true`)
|
|
1323
|
+
- `dryRun`: validate only, do not deploy (default `false`)
|
|
1324
|
+
|
|
1325
|
+
**Validation checks:**
|
|
1326
|
+
|
|
1327
|
+
- `name` field present
|
|
1328
|
+
- At least one status defined in `statuses` array
|
|
1329
|
+
- All rulesets have `name`, `triggers`, and `rules` (warns if empty)
|
|
1330
|
+
|
|
1331
|
+
**Important:** For production deployments, prefer `fluent module install` via CLI which bundles workflows with settings, rules, and data in a versioned module. Use this tool for interactive editing, hotfixes, or when CLI is unavailable.
|
|
1332
|
+
|
|
1333
|
+
**Example** — dry-run validation:
|
|
1334
|
+
|
|
1335
|
+
```json
|
|
1336
|
+
{
|
|
1337
|
+
"workflow": {
|
|
1338
|
+
"name": "ORDER::HD",
|
|
1339
|
+
"type": "ORDER",
|
|
1340
|
+
"subtype": "HD",
|
|
1341
|
+
"statuses": [{ "name": "CREATED" }, { "name": "BOOKED" }],
|
|
1342
|
+
"rulesets": [
|
|
1343
|
+
{
|
|
1344
|
+
"name": "BookOrder",
|
|
1345
|
+
"triggers": [{ "status": "CREATED" }],
|
|
1346
|
+
"rules": [{ "name": "com.fluentretail.rubix.rule.order.SendEventOnVerifyingState", "props": { "status": "BOOKED" } }]
|
|
1347
|
+
}
|
|
1348
|
+
]
|
|
1349
|
+
},
|
|
1350
|
+
"dryRun": true
|
|
1351
|
+
}
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
Response (dry run — valid):
|
|
1355
|
+
|
|
1356
|
+
```json
|
|
1357
|
+
{
|
|
1358
|
+
"ok": true,
|
|
1359
|
+
"dryRun": true,
|
|
1360
|
+
"valid": true,
|
|
1361
|
+
"workflowName": "ORDER::HD",
|
|
1362
|
+
"retailerId": "5",
|
|
1363
|
+
"note": "Validation passed. Set dryRun=false to deploy."
|
|
1364
|
+
}
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
---
|
|
1368
|
+
|
|
1369
|
+
## `workflow.diff`
|
|
1370
|
+
|
|
1371
|
+
Compare two workflow JSON definitions and identify changes.
|
|
1372
|
+
|
|
1373
|
+
Pure local computation — no API calls.
|
|
1374
|
+
|
|
1375
|
+
- **Required**:
|
|
1376
|
+
- `base`: base workflow JSON (before changes)
|
|
1377
|
+
- `target`: target workflow JSON (after changes)
|
|
1378
|
+
- **Optional**:
|
|
1379
|
+
- `format`: output format — `summary` (default), `detailed`, or `mermaid`
|
|
1380
|
+
|
|
1381
|
+
**Comparison scope:**
|
|
1382
|
+
|
|
1383
|
+
- Rulesets: added, removed, modified (trigger changes, rule additions/removals, prop changes)
|
|
1384
|
+
- Statuses: added, removed
|
|
1385
|
+
- Risk assessment: removing rulesets or statuses = HIGH, modifying props = MEDIUM, adding = LOW
|
|
1386
|
+
|
|
1387
|
+
**Formats:**
|
|
1388
|
+
|
|
1389
|
+
| Format | Returns |
|
|
1390
|
+
|---|---|
|
|
1391
|
+
| `summary` | Change counts, status changes, risk level |
|
|
1392
|
+
| `detailed` | Full `WorkflowDiffResult` object with per-ruleset breakdown |
|
|
1393
|
+
| `mermaid` | `stateDiagram-v2` with color-coded added/removed/modified states and transitions |
|
|
1394
|
+
|
|
1395
|
+
**Example** — summary diff:
|
|
1396
|
+
|
|
1397
|
+
```json
|
|
1398
|
+
{
|
|
1399
|
+
"base": { "name": "ORDER::HD", "statuses": [...], "rulesets": [...] },
|
|
1400
|
+
"target": { "name": "ORDER::HD", "statuses": [...], "rulesets": [...] },
|
|
1401
|
+
"format": "summary"
|
|
1402
|
+
}
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
Response:
|
|
1406
|
+
|
|
1407
|
+
```json
|
|
1408
|
+
{
|
|
1409
|
+
"ok": true,
|
|
1410
|
+
"summary": "3 ruleset change(s): 1 added, 0 removed, 2 modified. 1 status change(s): 1 added, 0 removed. Risk level: MEDIUM.",
|
|
1411
|
+
"riskLevel": "medium",
|
|
1412
|
+
"rulesetChanges": { "added": 1, "removed": 0, "modified": 2 },
|
|
1413
|
+
"statusChanges": { "added": ["AWAITING_PICKUP"], "removed": [] }
|
|
1414
|
+
}
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
---
|
|
1418
|
+
|
|
1419
|
+
## `workflow.simulate`
|
|
1420
|
+
|
|
1421
|
+
Static prediction of workflow event outcomes from workflow JSON.
|
|
1422
|
+
|
|
1423
|
+
Pure local computation — no API calls. Parses workflow JSON to find matching rulesets and predict state transitions, follow-on events, and webhook side effects.
|
|
1424
|
+
|
|
1425
|
+
- **Required**:
|
|
1426
|
+
- `workflow`: workflow JSON definition (as object or JSON string)
|
|
1427
|
+
- `currentStatus`: entity status to simulate from (e.g., `CREATED`, `BOOKED`)
|
|
1428
|
+
- **Optional**:
|
|
1429
|
+
- `eventName`: event name to match against ruleset names (if omitted, returns all rulesets triggered by `currentStatus`)
|
|
1430
|
+
- `entityType`: entity type filter for trigger matching
|
|
1431
|
+
- `entitySubtype`: entity subtype filter for trigger matching
|
|
1432
|
+
|
|
1433
|
+
**What it does:**
|
|
1434
|
+
|
|
1435
|
+
- Finds rulesets whose triggers match `currentStatus` (and optionally `eventName`)
|
|
1436
|
+
- Extracts `SetState`/`ChangeState` rules → predicted next status
|
|
1437
|
+
- Extracts `SendEvent` rules → predicted follow-on events
|
|
1438
|
+
- Extracts `SendWebhook` rules → predicted webhook side effects
|
|
1439
|
+
- Identifies custom Java rules that cannot be statically predicted
|
|
1440
|
+
|
|
1441
|
+
**Important limitations (always disclosed in response):**
|
|
1442
|
+
|
|
1443
|
+
- **STATIC ONLY** — cannot evaluate runtime conditions, entity attributes, or settings values
|
|
1444
|
+
- **CUSTOM RULES OPAQUE** — Java plugin rules may conditionally execute; behavior unknown without live state
|
|
1445
|
+
- **NO CROSS-ENTITY** — does not follow SendEvent chains into child entity workflows
|
|
1446
|
+
- Use `workflow.transitions` for **authoritative live validation** of available actions
|
|
1447
|
+
|
|
1448
|
+
**Example** — simulate from CREATED status:
|
|
1449
|
+
|
|
1450
|
+
```json
|
|
1451
|
+
{
|
|
1452
|
+
"workflow": { "name": "ORDER::HD", "rulesets": [...] },
|
|
1453
|
+
"currentStatus": "CREATED",
|
|
1454
|
+
"eventName": "BookOrder"
|
|
1455
|
+
}
|
|
1456
|
+
```
|
|
1457
|
+
|
|
1458
|
+
Response:
|
|
1459
|
+
|
|
1460
|
+
```json
|
|
1461
|
+
{
|
|
1462
|
+
"ok": true,
|
|
1463
|
+
"workflowName": "ORDER::HD",
|
|
1464
|
+
"currentStatus": "CREATED",
|
|
1465
|
+
"eventName": "BookOrder",
|
|
1466
|
+
"matchedRulesets": 1,
|
|
1467
|
+
"rulesets": [
|
|
1468
|
+
{
|
|
1469
|
+
"name": "BookOrder",
|
|
1470
|
+
"matchType": "statusAndEvent",
|
|
1471
|
+
"predictedStatus": "BOOKED",
|
|
1472
|
+
"predictedEvents": ["SendToFulfilment"],
|
|
1473
|
+
"predictedWebhooks": [],
|
|
1474
|
+
"ruleCount": 3
|
|
1475
|
+
}
|
|
1476
|
+
],
|
|
1477
|
+
"prediction": {
|
|
1478
|
+
"likelyNextStatus": "BOOKED",
|
|
1479
|
+
"followOnEvents": ["SendToFulfilment"],
|
|
1480
|
+
"webhookSideEffects": [],
|
|
1481
|
+
"confidence": "low",
|
|
1482
|
+
"note": "Custom rules present — prediction may be incomplete or wrong."
|
|
1483
|
+
},
|
|
1484
|
+
"limitations": [
|
|
1485
|
+
"STATIC PREDICTION ONLY — does not account for runtime conditions, entity attributes, or settings values.",
|
|
1486
|
+
"1 custom rule(s) found whose behavior cannot be predicted statically: ForwardIfOrderCoordinatesPresent.",
|
|
1487
|
+
"Use workflow.transitions for AUTHORITATIVE live validation of available actions."
|
|
1488
|
+
]
|
|
1489
|
+
}
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
---
|
|
1493
|
+
|
|
1494
|
+
## `setting.upsert`
|
|
1495
|
+
|
|
1496
|
+
Create or update a Fluent Commerce setting with upsert semantics.
|
|
1497
|
+
|
|
1498
|
+
Queries existing settings by `name` + `context` + `contextId` first. Creates if missing, updates if exists.
|
|
1499
|
+
|
|
1500
|
+
- **Required**:
|
|
1501
|
+
- `name`: setting key/name
|
|
1502
|
+
- `value`: setting value (for small values)
|
|
1503
|
+
- `context`: setting scope — `RETAILER`, `ACCOUNT`, `LOCATION`, `NETWORK`, `AGENT`, `CUSTOMER`
|
|
1504
|
+
- **Optional**:
|
|
1505
|
+
- `lobValue`: large object value for JSON payloads > 4KB (mutually exclusive with `value`)
|
|
1506
|
+
- `contextId`: context ID (e.g., retailer ID). Defaults to `FLUENT_RETAILER_ID` for `RETAILER` context.
|
|
1507
|
+
|
|
1508
|
+
**Key gotchas:**
|
|
1509
|
+
|
|
1510
|
+
- `context` is a plain String (`"RETAILER"`), NOT an object
|
|
1511
|
+
- `contextId` is a separate Int field
|
|
1512
|
+
- For large JSON values (> 4KB), use `lobValue` instead of `value`
|
|
1513
|
+
- Returns `created: true/false` for audit trail
|
|
1514
|
+
|
|
1515
|
+
**Example** — create a webhook URL setting:
|
|
1516
|
+
|
|
1517
|
+
```json
|
|
1518
|
+
{
|
|
1519
|
+
"name": "WEBHOOK_ORDER_NOTIFICATION",
|
|
1520
|
+
"value": "https://api.example.com/webhooks/orders",
|
|
1521
|
+
"context": "RETAILER"
|
|
1522
|
+
}
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
Response (created):
|
|
1526
|
+
|
|
1527
|
+
```json
|
|
1528
|
+
{
|
|
1529
|
+
"ok": true,
|
|
1530
|
+
"created": true,
|
|
1531
|
+
"updated": false,
|
|
1532
|
+
"setting": {
|
|
1533
|
+
"id": "99",
|
|
1534
|
+
"name": "WEBHOOK_ORDER_NOTIFICATION",
|
|
1535
|
+
"value": "https://api.example.com/webhooks/orders",
|
|
1536
|
+
"context": "RETAILER",
|
|
1537
|
+
"contextId": 5
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
```
|
|
1541
|
+
|
|
1542
|
+
Response (updated existing):
|
|
1543
|
+
|
|
1544
|
+
```json
|
|
1545
|
+
{
|
|
1546
|
+
"ok": true,
|
|
1547
|
+
"created": false,
|
|
1548
|
+
"updated": true,
|
|
1549
|
+
"setting": { "id": "99", "name": "WEBHOOK_ORDER_NOTIFICATION", "value": "https://api.example.com/webhooks/orders-v2", "context": "RETAILER", "contextId": 5 },
|
|
1550
|
+
"previousValue": "https://api.example.com/webhooks/orders"
|
|
1551
|
+
}
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
---
|
|
1555
|
+
|
|
1556
|
+
## `setting.bulkUpsert`
|
|
1557
|
+
|
|
1558
|
+
Batch create or update multiple settings in one call.
|
|
1559
|
+
|
|
1560
|
+
Processes up to 50 settings sequentially with individual error handling. Each setting is an independent upsert — failures on one setting don't block others.
|
|
1561
|
+
|
|
1562
|
+
- **Required**:
|
|
1563
|
+
- `settings`: array of setting objects (min 1, max 50), each with `name`, `value`, `context`, and optional `lobValue` and `contextId`
|
|
1564
|
+
|
|
1565
|
+
**Example** — bulk create 3 settings:
|
|
1566
|
+
|
|
1567
|
+
```json
|
|
1568
|
+
{
|
|
1569
|
+
"settings": [
|
|
1570
|
+
{ "name": "WEBHOOK_ORDER_URL", "value": "https://api.example.com/orders", "context": "RETAILER" },
|
|
1571
|
+
{ "name": "WEBHOOK_FULFILMENT_URL", "value": "https://api.example.com/fulfilments", "context": "RETAILER" },
|
|
1572
|
+
{ "name": "FEATURE_FLAG_RETURNS", "value": "true", "context": "RETAILER" }
|
|
1573
|
+
]
|
|
1574
|
+
}
|
|
1575
|
+
```
|
|
1576
|
+
|
|
1577
|
+
Response:
|
|
1578
|
+
|
|
1579
|
+
```json
|
|
1580
|
+
{
|
|
1581
|
+
"ok": true,
|
|
1582
|
+
"created": 2,
|
|
1583
|
+
"updated": 1,
|
|
1584
|
+
"failed": 0,
|
|
1585
|
+
"total": 3,
|
|
1586
|
+
"results": [
|
|
1587
|
+
{ "name": "WEBHOOK_ORDER_URL", "status": "created" },
|
|
1588
|
+
{ "name": "WEBHOOK_FULFILMENT_URL", "status": "created" },
|
|
1589
|
+
{ "name": "FEATURE_FLAG_RETURNS", "status": "updated" }
|
|
1590
|
+
]
|
|
1591
|
+
}
|
|
1592
|
+
```
|
|
1593
|
+
|
|
1594
|
+
---
|
|
1595
|
+
|
|
1596
|
+
## `environment.discover`
|
|
1597
|
+
|
|
1598
|
+
Full environment snapshot in one call.
|
|
1599
|
+
|
|
1600
|
+
Returns everything an agent needs to understand the Fluent environment: retailer details, locations, networks, catalogues, workflows, settings, modules, and user context.
|
|
1601
|
+
|
|
1602
|
+
- **Optional**:
|
|
1603
|
+
- `include`: array of sections to include. Default: `["retailer", "locations", "networks", "catalogues"]`. Available: `retailer`, `locations`, `networks`, `catalogues`, `workflows`, `settings`, `modules`, `users`
|
|
1604
|
+
|
|
1605
|
+
**Limitations:**
|
|
1606
|
+
|
|
1607
|
+
- Locations and settings return the first 100 items only (no auto-pagination)
|
|
1608
|
+
- Workflows section uses a transition API probe — may miss workflows with no user actions at the initial state. Use `fluent workflow list` via CLI for definitive listing.
|
|
1609
|
+
|
|
1610
|
+
**Example** — discover retailer, locations, and settings:
|
|
1611
|
+
|
|
1612
|
+
```json
|
|
1613
|
+
{
|
|
1614
|
+
"include": ["retailer", "locations", "settings"]
|
|
1615
|
+
}
|
|
1616
|
+
```
|
|
1617
|
+
|
|
1618
|
+
Response:
|
|
1619
|
+
|
|
1620
|
+
```json
|
|
1621
|
+
{
|
|
1622
|
+
"ok": true,
|
|
1623
|
+
"retailer": { "id": "5", "ref": "HM_TEST", "tradingName": "HM Test", "status": "ACTIVE" },
|
|
1624
|
+
"locations": [
|
|
1625
|
+
{ "id": "10", "ref": "LOC_WH_01", "type": "WAREHOUSE", "status": "ACTIVE", "name": "Main Warehouse" }
|
|
1626
|
+
],
|
|
1627
|
+
"settings": [
|
|
1628
|
+
{ "name": "WEBHOOK_ORDER_URL", "value": "https://...", "context": "RETAILER", "contextId": 5 }
|
|
1629
|
+
]
|
|
1630
|
+
}
|
|
1631
|
+
```
|
|
1632
|
+
|
|
1633
|
+
---
|
|
1634
|
+
|
|
1635
|
+
## `environment.validate`
|
|
1636
|
+
|
|
1637
|
+
Pre-flight environment validation. Runs configurable checks before E2E tests or deployments.
|
|
1638
|
+
|
|
1639
|
+
- **Optional**:
|
|
1640
|
+
- `checks`: array of checks to run. Default: `["auth", "retailer", "locations"]`. Available: `auth`, `retailer`, `locations`, `inventory`, `workflows`, `settings`, `modules`
|
|
1641
|
+
|
|
1642
|
+
**Available checks:**
|
|
1643
|
+
|
|
1644
|
+
| Check | What it validates |
|
|
1645
|
+
|---|---|
|
|
1646
|
+
| `auth` | Token valid, permissions sufficient |
|
|
1647
|
+
| `retailer` | Retailer exists and is active |
|
|
1648
|
+
| `locations` | At least one warehouse location exists |
|
|
1649
|
+
| `inventory` | At least one product with stock |
|
|
1650
|
+
| `workflows` | Key workflows deployed (ORDER, FULFILMENT) |
|
|
1651
|
+
| `settings` | Critical settings exist |
|
|
1652
|
+
| `modules` | Expected modules deployed |
|
|
1653
|
+
|
|
1654
|
+
Returns pass/fail per check with severity and actionable messages.
|
|
1655
|
+
|
|
1656
|
+
**Example** — full pre-flight validation:
|
|
1657
|
+
|
|
1658
|
+
```json
|
|
1659
|
+
{
|
|
1660
|
+
"checks": ["auth", "retailer", "locations", "workflows", "settings"]
|
|
1661
|
+
}
|
|
1662
|
+
```
|
|
1663
|
+
|
|
1664
|
+
Response:
|
|
1665
|
+
|
|
1666
|
+
```json
|
|
1667
|
+
{
|
|
1668
|
+
"ok": true,
|
|
1669
|
+
"results": [
|
|
1670
|
+
{ "check": "auth", "passed": true, "severity": "critical", "message": "Authenticated as admin@example.com" },
|
|
1671
|
+
{ "check": "retailer", "passed": true, "severity": "critical", "message": "Retailer HM_TEST (ID 5) is ACTIVE" },
|
|
1672
|
+
{ "check": "locations", "passed": true, "severity": "high", "message": "Found 3 locations (2 warehouses)" },
|
|
1673
|
+
{ "check": "workflows", "passed": false, "severity": "high", "message": "Missing workflow: FULFILMENT_OPTIONS::HD" },
|
|
1674
|
+
{ "check": "settings", "passed": true, "severity": "medium", "message": "12 retailer settings found" }
|
|
1675
|
+
],
|
|
1676
|
+
"summary": { "passed": 4, "failed": 1, "total": 5 }
|
|
1677
|
+
}
|
|
1678
|
+
```
|
|
1679
|
+
|
|
1680
|
+
---
|
|
1681
|
+
|
|
1682
|
+
## `test.assert`
|
|
1683
|
+
|
|
1684
|
+
Assert entity state matches expectations with optional polling.
|
|
1685
|
+
|
|
1686
|
+
Deep assertion on entity fields, attributes, and edge counts/statuses with human-readable failure messages.
|
|
1687
|
+
|
|
1688
|
+
- **Required**:
|
|
1689
|
+
- `entityType`: entity type (ORDER, FULFILMENT, etc.)
|
|
1690
|
+
- `assertions`: object with expected values
|
|
1691
|
+
- **Optional**:
|
|
1692
|
+
- `id`: entity ID (preferred)
|
|
1693
|
+
- `ref`: entity ref (fallback)
|
|
1694
|
+
- `poll`: if `true`, retry until assertions pass or timeout (default `false`)
|
|
1695
|
+
- `timeoutMs`: polling timeout in ms, 1000–300000 (default `60000`)
|
|
1696
|
+
- `intervalMs`: polling interval in ms, 1000–30000 (default `5000`)
|
|
1697
|
+
|
|
1698
|
+
**Assertion types:**
|
|
1699
|
+
|
|
1700
|
+
| Assertion | Description |
|
|
1701
|
+
|---|---|
|
|
1702
|
+
| `status` | Exact match on entity status |
|
|
1703
|
+
| `type` | Exact match on entity type |
|
|
1704
|
+
| `subtype` | Exact match on entity subtype |
|
|
1705
|
+
| `attributes` | Key-value pairs that must be present on the entity |
|
|
1706
|
+
| `edges` | Min/max count and status on related entities (e.g., fulfilments) |
|
|
1707
|
+
|
|
1708
|
+
**Polling mode:** Set `poll: true` to retry assertions on an interval until they pass or the timeout is reached. Useful after sending events when state changes are asynchronous.
|
|
1709
|
+
|
|
1710
|
+
**Example** — assert order is BOOKED with at least 1 fulfilment (polling):
|
|
1711
|
+
|
|
1712
|
+
```json
|
|
1713
|
+
{
|
|
1714
|
+
"entityType": "ORDER",
|
|
1715
|
+
"ref": "HD-001",
|
|
1716
|
+
"assertions": {
|
|
1717
|
+
"status": "BOOKED",
|
|
1718
|
+
"edges": {
|
|
1719
|
+
"fulfilments": { "minCount": 1 }
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
"poll": true,
|
|
1723
|
+
"timeoutMs": 60000,
|
|
1724
|
+
"intervalMs": 5000
|
|
1725
|
+
}
|
|
1726
|
+
```
|
|
1727
|
+
|
|
1728
|
+
Response (pass):
|
|
1729
|
+
|
|
1730
|
+
```json
|
|
1731
|
+
{
|
|
1732
|
+
"ok": true,
|
|
1733
|
+
"passed": true,
|
|
1734
|
+
"entityType": "ORDER",
|
|
1735
|
+
"entity": { "id": "12345", "ref": "HD-001", "status": "BOOKED" },
|
|
1736
|
+
"assertions": {
|
|
1737
|
+
"status": { "expected": "BOOKED", "actual": "BOOKED", "passed": true },
|
|
1738
|
+
"edges.fulfilments.minCount": { "expected": 1, "actual": 2, "passed": true }
|
|
1739
|
+
},
|
|
1740
|
+
"polling": { "attempts": 3, "elapsed": 12500 }
|
|
1741
|
+
}
|
|
1742
|
+
```
|
|
1743
|
+
|
|
1744
|
+
Response (fail after timeout):
|
|
1745
|
+
|
|
1746
|
+
```json
|
|
1747
|
+
{
|
|
1748
|
+
"ok": true,
|
|
1749
|
+
"passed": false,
|
|
1750
|
+
"entityType": "ORDER",
|
|
1751
|
+
"entity": { "id": "12345", "ref": "HD-001", "status": "CREATED" },
|
|
1752
|
+
"assertions": {
|
|
1753
|
+
"status": { "expected": "BOOKED", "actual": "CREATED", "passed": false }
|
|
1754
|
+
},
|
|
1755
|
+
"polling": { "attempts": 12, "elapsed": 60000, "timedOut": true },
|
|
1756
|
+
"failureMessage": "Assertion failed: status expected \"BOOKED\" but got \"CREATED\" (timed out after 60000ms)"
|
|
1757
|
+
}
|
|
1758
|
+
```
|
|
1759
|
+
|
|
1760
|
+
---
|
|
1761
|
+
|
|
1762
|
+
## Retry and idempotency matrix
|
|
1763
|
+
|
|
1764
|
+
| Category | Tools | Retry behavior |
|
|
1765
|
+
|------|----------|-------------|
|
|
1766
|
+
| Read operations | `event.get`, `event.list`, `event.flowInspect`, `metrics.query`, `metrics.healthCheck`, `metrics.sloReport`, `metrics.labelCatalog`, `metrics.topEvents`, `workflow.transitions`, `graphql.query` (query), `graphql.queryAll`, `batch.status`, `batch.batchStatus`, `batch.results`, `graphql.introspect`, `connection.test`, `entity.get`, `environment.discover`, `environment.validate`, `test.assert`, `plugin.list` | retry + backoff |
|
|
1767
|
+
| Write operations | `event.send`, `batch.create`, `batch.send`, `graphql.query` (mutation), `graphql.batchMutate`, `entity.create`, `entity.update`, `workflow.upload`, `setting.upsert`, `setting.bulkUpsert` | timeout-only (no automatic retry) |
|
|
1768
|
+
| Local validation / no API | `config.validate`, `health.ping`, `event.build`, `webhook.validate`, `workflow.diff`, `workflow.simulate` | not applicable |
|
|
1769
|
+
|
|
1770
|
+
## Tool inventory summary (36 tools)
|
|
1771
|
+
|
|
1772
|
+
| Tool | Category | Requires SDK | Retry | Description |
|
|
1773
|
+
|------|----------|:------------:|:-----:|-------------|
|
|
1774
|
+
| `config.validate` | Diagnostics | No | — | Validate auth/base URL configuration |
|
|
1775
|
+
| `health.ping` | Diagnostics | No | — | Quick health check with config summary |
|
|
1776
|
+
| `connection.test` | Diagnostics | Yes | Yes | Full auth + GraphQL end-to-end test (returns user/account context) |
|
|
1777
|
+
| `event.build` | Events | No | — | Build event payload (no API call) |
|
|
1778
|
+
| `event.send` | Events | Yes | No | Send event (async/sync, supports dryRun) |
|
|
1779
|
+
| `event.get` | Events | Yes | Yes | Get single event by ID |
|
|
1780
|
+
| `event.list` | Events | Yes | Yes | List/filter events with pagination and optional analysis mode |
|
|
1781
|
+
| `event.flowInspect` | Events | Yes | Yes | One-call root-entity flow forensics with compact/full modes |
|
|
1782
|
+
| `metrics.query` | Metrics | Yes | Yes | Query Prometheus metrics (instant/range) |
|
|
1783
|
+
| `metrics.healthCheck` | Metrics | Yes | Yes | One-call anomaly assessment with Prometheus + Event API fallback |
|
|
1784
|
+
| `metrics.sloReport` | Metrics | Yes | Yes | SLO snapshot with rates, latency, and threshold findings |
|
|
1785
|
+
| `metrics.labelCatalog` | Metrics | Yes | Yes | Label discovery for a metric (live sampling + known hints) |
|
|
1786
|
+
| `metrics.topEvents` | Metrics | Yes | Yes | Ranked event analytics using Event API aggregation |
|
|
1787
|
+
| `workflow.transitions` | Orchestration | Yes | Yes | Query available user actions/transitions at any entity state |
|
|
1788
|
+
| `plugin.list` | Orchestration | Yes | Yes | List registered rules with optional name filter |
|
|
1789
|
+
| `graphql.query` | GraphQL | Yes | Varies | Execute single-page query or mutation (retries for queries only) |
|
|
1790
|
+
| `graphql.queryAll` | GraphQL | Yes | Yes | Auto-paginated query — follows cursors across all pages |
|
|
1791
|
+
| `graphql.batchMutate` | GraphQL | Yes | No | Execute up to 50 aliased mutations in one request |
|
|
1792
|
+
| `graphql.introspect` | GraphQL | Yes | Yes | Schema introspection (types, mutations, queries) |
|
|
1793
|
+
| `batch.create` | Batch | Yes | No | Create ingestion job |
|
|
1794
|
+
| `batch.send` | Batch | Yes | No | Send records to job |
|
|
1795
|
+
| `batch.status` | Batch | Yes | Yes | Check job status |
|
|
1796
|
+
| `batch.batchStatus` | Batch | Yes | Yes | Check specific batch within a job |
|
|
1797
|
+
| `batch.results` | Batch | Yes | Yes | Get per-record job outcomes |
|
|
1798
|
+
| `webhook.validate` | Webhooks | No | — | Validate webhook payload + optional signature verification |
|
|
1799
|
+
| `entity.create` | Entity | Yes | No | Type-safe entity creation with field validation and gotcha knowledge (12 entity types) |
|
|
1800
|
+
| `entity.update` | Entity | Yes | No | Status-aware entity updates with optional transition validation |
|
|
1801
|
+
| `entity.get` | Entity | Yes | Yes | Unified entity lookup by ID or ref with optional edge inclusion |
|
|
1802
|
+
| `workflow.upload` | Workflow Mgmt | Yes | No | Deploy workflow JSON via REST API with structure validation |
|
|
1803
|
+
| `workflow.diff` | Workflow Mgmt | No | — | Compare two workflows — rulesets, statuses, risk assessment, mermaid output |
|
|
1804
|
+
| `workflow.simulate` | Workflow Mgmt | No | — | Static prediction of event outcomes from workflow JSON |
|
|
1805
|
+
| `setting.upsert` | Settings | Yes | No | Create or update a setting with upsert semantics |
|
|
1806
|
+
| `setting.bulkUpsert` | Settings | Yes | No | Batch create/update up to 50 settings with per-setting error handling |
|
|
1807
|
+
| `environment.discover` | Environment | Yes | Yes | Full environment snapshot — retailer, locations, networks, catalogues, settings, modules |
|
|
1808
|
+
| `environment.validate` | Environment | Yes | Yes | Pre-flight validation checks: auth, retailer, locations, inventory, workflows |
|
|
1809
|
+
| `test.assert` | Test | Yes | Yes | Assert entity state with status, attribute, and edge assertions + polling mode |
|
|
1810
|
+
|