Reference for $defs.cipStep* on Business Entity execution.cip.operations.*.steps. Each step is a single keyed object — one of the types below. Authoring context: Configure data flow. Certification: CIP execution.
CIP has no if / else / switch step. It does not hide vendor logic or replace Connected System capabilities. Pipelines compose fetch, map, filter, and related steps over declared operations — vendor behavior stays in OpenAPI, MCP, and certified capabilities, not inside CIP control-flow.
Prerequisites
- Configure data flow open while editing
execution.cip - Schema synced via Builder validate (or local
aifabrix validate <systemKey>)
Where it lives
| Layer | Location |
|---|---|
| Steps | <datasourceKey>.json → execution.cip.operations.<key>.steps[] |
| Schema defs | External datasource schema $defs.cipStep* |
| Help | Builder topic cipOverview, schemaCatalog |
How to set
- Open the operation under
execution.cip.operationsthat matches your capability key. - Append steps as single-key objects only — never invent keys outside this catalog.
- Prefer the common list pattern:
fetch→paginate→map→filter→output. - Re-run
aifabrix validate <systemKey>after each structural change.
Defaults and examples
| Step | Purpose | Typical default |
|---|---|---|
fetch |
Obtain raw payload | source: openapi + openapiRef |
paginate |
Walk list pages | strategy: cursor (when vendor pages) |
map |
Normalize fields | useFieldMappings: true |
merge |
Combine accumulates | strategy: array |
filter |
ABAC / JMESPath | abac.mode: mandatory |
output |
Emit records | mode: records |
pythonInline |
Sandboxed transform | rare — prefer map/filter |
Step types at a glance
| Step | Purpose |
|---|---|
fetch |
Obtain raw payload from OpenAPI, HTTP, another datasource, a viewpoint, or local record storage |
paginate |
Walk cursor / page / offset results after fetch |
map |
Normalize vendor fields via fieldMappings or inline DSL |
merge |
Combine accumulated fetch outputs |
filter |
ABAC and/or JMESPath filter before emit |
output |
Emit records for sync and agents |
pythonInline |
Sandboxed transform when declarative steps are not enough |
Common optional step wrappers: enabled, order, stepId, onError, lineage (where schema allows).
fetch
Purpose: Load a payload for the operation. source is where I/O happens. CIP operation shape (create | update | delete) is persist vs read on local storage — not the capability key. Pipeline output.mode stays records (plural collection envelope).
Required: object key fetch with source. Closed enum:
source |
Meaning | Required | Forbidden |
|---|---|---|---|
openapi |
Vendor/OpenAPI HTTP | openapiRef and/or operationId |
SQL query |
http |
Explicit method + path | method + path |
SQL query |
datasource |
Nested datasource operation | datasource (unless composition selects) + operation (default list) |
SQL query |
viewpoint |
Saved SQL against record storage (storageType: view) |
SQL string query |
vendor path/method; persist shapes |
record |
Local table I/O (no vendor HTTP) | none beyond source (identity/body from the capability and exposed.profiles) |
SQL query; do not treat as vendor HTTP |
source: record is singular. Do not write fetch.source: records. output.mode: records remains plural.
CIP fetch.source compiles to COM source (same pattern as writeShape). Named perspectives are source: viewpoint. Local table and vendor fetches are the other tokens — capability keys such as list / search / get are examples, not a closed set.
Useful optional fields (vendor/nested): query (parameter object), bodyTemplate, headers, expectedItemsPath, accumulate, skipUnauthorized, parameters (datasource). query is a SQL string only for viewpoint.
{
"fetch": {
"source": "openapi",
"openapiRef": "list",
"query": { "limit": "100" }
}
}
Viewpoint (SQL against governed record storage):
{
"fetch": {
"source": "viewpoint",
"query": "SELECT customer_id AS \"customerId\", SUM(amount) AS total FROM records_example_orders GROUP BY customer_id"
}
}
Local table read (list, get, or any other published read capability — no shape):
{
"fetch": {
"source": "record"
}
}
Local table persist — shape on the operation, not inferred from the key:
{
"registerEmployee": {
"shape": "create",
"steps": [
{ "fetch": { "source": "record" } },
{ "output": { "mode": "records" } }
]
}
}
When to use: Vendor or child-entity HTTP (openapi / http / datasource); calculated read-only perspectives (viewpoint); local record-storage list/get/create/update with no vendor HTTP (record).
When not: Do not invent a sixth source. record is not vendor HTTP. viewpoint is not a table write. Do not use a capability key (create, list, …) as the fetch source.
paginate
Purpose: Continue fetching pages after an initial fetch (typically list).
Required: paginate.strategy — cursor | page | offset | none.
{
"paginate": {
"strategy": "cursor",
"cursorField": "$.paging.next.after",
"cursorParam": "after",
"pageSizeParam": "limit",
"pageSize": 100,
"maxPages": 50
}
}
When to use: List operations with vendor pagination.
When not: Single-record get / create / update / delete unless the vendor truly pages those responses.
map
Purpose: Transform raw API records into the normalized metadata model.
Required: object key map (inner properties optional; defaults apply).
Prefer useFieldMappings: true so fieldMappings.attributes drive expressions. Optional inputPath (default $.items[*]) and inline overrides.
{
"map": {
"useFieldMappings": true,
"inputPath": "$.results[*]"
}
}
When to use: Almost every list/get after fetch.
When not: Do not invent a transform step — use map (or pythonInline as last resort).
merge
Purpose: Merge accumulated fetch outputs (fetch.accumulate: true) — data transformation only.
Required: merge.strategy — object | array | union | byKey (optional keyField).
{
"merge": {
"strategy": "array"
}
}
When to use: Multi-fetch orchestration into one batch.
When not: Simple single-fetch pipelines — skip merge.
filter
Purpose: Filter mapped records before output (ABAC and/or JMESPath).
Required: object key filter.
{
"filter": {
"abac": {
"mode": "mandatory",
"policyScope": "datasource"
}
}
}
abac.mode: mandatory | optional | disabled. Optional expression (+ expressionLanguage: jmespath).
When to use: Enforce or explain ABAC on list/get results.
When not: Do not disable ABAC in production tenants without a documented exception.
output
Purpose: Finalize the pipeline and emit records.
Required: object key output. Prefer mode: records.
{
"output": {
"mode": "records"
}
}
Optional: limit, includeConfidence.
When to use: End of every operation that should return governed records.
When not: Leaving output off often breaks sync and agent consumption — include it unless validate proves otherwise.
pythonInline
Purpose: Sandboxed inline Python on the current record batch when declarative steps cannot express the transform.
Required: pythonInline.code — small pure snippet; no imports, IO, or network (runtime must enforce sandboxing).
{
"pythonInline": {
"code": "return [r for r in records if r.get('status') == 'active']"
}
}
When to use: Rare edge transforms after map.
When not: Prefer map / filter first. Prefer execution.engine: python only for full custom handlers, not routine field mapping.
Validate
aifabrix validate <systemKey>
Reject invented step keys; use this catalog and Builder help topic schemaCatalog when property names fail.
Common mistakes
| Mistake | Fix |
|---|---|
transform or other invented keys |
Use map / filter / pythonInline |
fetch without openapiRef for source: openapi |
Add openapiRef matching openapi.operations |
List without output |
Add {"output":{"mode":"records"}} |
pythonInline for routine field renames |
Use map + fieldMappings |