younifyd
Menu

Connectors

Catalog Connector

Load your own catalogs into the platform and search them by meaning from a workflow, with per-customer pricing, per-customer visibility and incremental-sync state.

On this page

What this connector is for

Loads your own catalog into the platform and searches it by meaning rather than by keyword — "waterproof walking boots" finds a product titled "Merrell Moab GTX" even though they share no words. On top of that it resolves per-customer pricing and per-customer visibility, so the same catalog can show a different price to two customers and hide a product from a third entirely.

No connection is needed. Like the Data Table and Variable connectors this operates on platform-managed storage keyed to your store automatically.

A catalog is defined outside this connector, in the designer's Catalogs page: a set of typed fields, one or more of which form the primary key, plus a template that decides what text gets embedded for meaning-search. See Managing catalogs for that side of it.

Every action returns a plain object under the standard envelope — read it as {{<stepReference>.response.data.<field>}}. None of these actions halt the workflow with an HTTP-style error the way Schema Validator does; a bad catalog id or an unknown filter field surfaces as a thrown error in the execution log.

The one thing to get right first: item keys

Every item is identified by an item key, which the platform derives from the catalog's primary key fields — you never supply it on write. A catalog whose primary key is sku derives the key from attributes.sku; a composite key of sku + region joins both.

This matters because writes are upserts keyed on that derived value. If the attributes you send produce a key that already exists, you update that item; if not, you create a new one. Passing a literal itemKey field in attributes does not set the key — it just creates an ordinary attribute called "itemKey", and the real key still comes from the primary key fields.

The read actions (Get Catalog Items, Delete Catalog Items) and price rows work the other way round: there you pass the derived key directly, because by then it exists.

Search Catalog

The main action. Combines meaning-based (vector) search with keyword search, applies your filters and the customer's visibility rules, and resolves a price per result.

Configuring it

  • Catalog — required. Picked from a live dropdown of your store's catalogs.
  • Query — required. Plain language, describing the item itself. Put exact constraints in Filters rather than in the query text: "boots under £100" searches for the words "under 100", whereas a price filter actually restricts the results.
  • Filters — a JSON array against the catalog's filterable fields:
    [{"field":"brand","op":"eq","value":"Nike"},
     {"field":"price","op":"lte","value":100}]
    
    Operators are eq, in, lt, lte, gt, gte — there is no ne, no contains and no regex. A field that isn't declared filterable on the catalog is rejected with an error rather than ignored, so a typo fails loudly instead of silently widening the result set. Only 8 fields per catalog can be filterable, and range operators (lt/lte/gt/gte) work only on numeric fields.
  • Limit — default 10, maximum 50.
  • Minimum Relevance — 0–1. Drops results whose meaning match is weaker than this, so a query nothing matches returns nothing instead of the closest ten. Roughly: 0.2 loosely related, 0.35 on-topic, 0.5+ strong. Leave it empty to return everything found. Exact keyword matches are never dropped by this.
  • Relative Cutoff — 0–1. Keeps only results scoring at least this fraction of the best match, trimming a weak tail when the top hits are good. Independent of Minimum Relevance: use that one to reject a bad query, this one to shorten a good one.
  • Entitlement Terms — comma-separated visibility grants for the customer this step is acting for, e.g. seg:gold,region:uk. Leave empty and the search sees only items visible to everyone.
  • Price Contexts — comma-separated, most specific first, e.g. cust:A,pricelist:PL-42,region:uk. The first context that has a price for an item wins.
  • Currency — restricts resolved prices to one currency.
  • Include Debug Info — returns vector and keyword ranks plus timings alongside results. For tuning relevance; leave it off in production.

Reading the response

  • {{<stepReference>.response.data.items}} — the matched items, best first. Each carries:
    • itemKey — the derived key.
    • attributes — only the fields marked returnable on the catalog, not everything stored.
    • relevance — 0–1, how well the item matched the meaning of the query. This is the number to threshold on and to show a human.
    • price — present only when pricing is enabled and a context matched: { price, currency, available, minQuantity, contextKey, metadata }. contextKey tells you which context supplied the price, which is what answers "why this price?".
    • score — internal fused rank. Ordering only — it is not a quality measure and its absolute value is not meaningful. Use relevance.
  • {{<stepReference>.response.data.total}} — how many items matched.

Example

Catalog: products
Query: waterproof walking boots
Filters: [{"field":"price","op":"lte","value":120}]
Entitlement Terms: cust:A,seg:gold
Price Contexts: cust:A,pricelist:PL-42

Returns only items Customer A is allowed to see, priced against their contract first and the PL-42 price list second.

Upsert Catalog Items

Writes items into a catalog — the action a product sync calls. Creates or updates by derived item key.

Configuring it

  • Catalog — required.

  • Items — required. A JSON array. Each entry:

    [{"attributes": {"sku":"SKU-1","title":"Walking Boot","brand":"Merrell"},
      "entitlementTerms": ["seg:gold"],
      "denyTerms": ["cust:C"]}]
    
    • attributes — the catalog's own fields. Must include the primary key field(s).
    • entitlementTerms — who may see this item. An item with no terms is visible to everyone; adding terms restricts it to customers whose search passes a matching term.
    • denyTerms — who may not see it, evaluated after entitlements. This is how you hide one product from one customer without touching everyone else's grants.

    Both are only used by catalogs with entitlement enabled.

Reading the response

  • {{<stepReference>.response.data.upserted}} — items written.
  • {{<stepReference>.response.data.skipped}} — items unchanged, so not rewritten.
  • {{<stepReference>.response.data.errors}} — per-item failures. A bad item does not fail the whole batch, so always check this rather than assuming success from a 200.
  • {{<stepReference>.response.data.warning}} — a configuration problem worth seeing, e.g. writing into a catalog whose schema changed and that needs re-indexing.

Indexing is asynchronous

An upserted item is stored immediately but not searchable immediately — its text has to be embedded first, which happens on a background worker. Items appear with an "Indexing" status on the catalog's Records tab and become findable once that completes. A sync that writes and then immediately searches will not see its own writes; don't build a workflow that depends on that.

Upsert Catalog Prices

Loads per-customer prices, separately from the items themselves. Prices are their own rows because they change far more often than product content does, and changing a price must not trigger a re-embed.

Configuring it

  • Catalog — required.
  • Prices — required. A JSON array:
    [{"itemKey":"SKU-1","contextKey":"cust:A","price":10,"currency":"GBP","available":true}]
    
    • itemKey — the derived key of an item already in the catalog.
    • contextKey — whose price this is. The shape is yours to choose; cust:A, pricelist:PL-42 and region:uk are conventions, not requirements. What matters is that the same strings appear in Price Contexts at search time.
    • available — set false to mark an item unsellable in that context while leaving it visible.

One row per (item, context) pair: writing the same pair again replaces it.

Reading the response

  • {{<stepReference>.response.data.upserted}} — prices written.

Example

Customer A pays £10 and Customer B pays £12 for the same product:

[{"itemKey":"SKU-1","contextKey":"cust:A","price":10,"currency":"GBP"},
 {"itemKey":"SKU-1","contextKey":"cust:B","price":12,"currency":"GBP"}]

Customer C sees neither price. If they should not see the product at all, that's a denyTerms entry on the item, not a missing price — an item with no price in any of a customer's contexts still appears in their results, just without one.

Get Catalog Items

Fetches specific items by key, with no searching involved. Use it when you already know what you want — enriching an order line, checking whether a sync wrote what you expected.

  • Catalog — required.
  • Item Keys — required. Comma-separated, or a JSON array.

Returns {{<stepReference>.response.data.items}}. Keys that don't exist are simply absent from the result rather than returned as nulls, so compare lengths if a missing item matters.

Delete Catalog Items

Removes items by key, and every price attached to them in every context. There is no soft delete and no undo.

  • Catalog — required.
  • Item Keys — required. Comma-separated, or a JSON array.

Returns {{<stepReference>.response.data.deleted}}.

To stop selling something without losing its prices, set available: false on its price rows instead.

Get Catalog Sync State / Set Catalog Sync State

A small piece of scratch storage per catalog, for incremental syncs. Rather than keeping a watermark in a data table or an external system, store it next to the catalog it belongs to.

  • Get Catalog Sync State takes just Catalog and returns {{<stepReference>.response.data.syncState}}{} before the first successful run, so a first run needs no special case.
  • Set Catalog Sync State takes Catalog and Sync State (a JSON object — typically a timestamp or cursor) and returns {{<stepReference>.response.data.saved}}.

Write the watermark only after the upsert step succeeded. Setting it first means a failed sync advances the cursor and silently skips those records on the next run.

A typical incremental sync

  1. Get Catalog Sync State{"updatedSince":"2026-01-01T00:00:00Z"}
  2. Fetch changed products from your source system using that timestamp.
  3. Upsert Catalog Items with the page of results.
  4. Check errors is empty.
  5. Set Catalog Sync State with the new high-water mark.

Entitlements and pricing together

The two are independent and answer different questions:

ControlsSet byRead at search time by
EntitlementsWhether a customer can see an itementitlementTerms / denyTerms on the itemEntitlement Terms
PricingWhat a customer paysPrice rows, one per (item, context)Price Contexts

An item visible to a customer with no matching price appears without one. An item with a price but no matching entitlement doesn't appear at all. Both features are switched on per catalog — a catalog with neither enabled ignores these fields entirely.

Because entitlement terms come from the step's configuration and not from the conversation, an AI assistant calling this connector through an MCP server cannot widen its own visibility: whatever the workflow supplies is what the search is scoped to.

Also applies here

Step Name

What it's for

Every step in a workflow gets a name — either one you set or a default based on the connector and action (e.g. "Get Order Details", "Send Welcome Email"). It's shown throughout the UI and in your execution history, and it's also the source for the step's Reference — a camelCase identifier auto-generated from the name (e.g. "Get Order Details" → getOrderDetails) — which is what you actually use in {{...}} expressions to read this step's output from later steps. See Step Reference.

Rules

  • Must be at least 2 characters, and 50 characters or fewer.
  • Must be unique within the workflow — reusing a name that's already taken will be rejected, with a suggested alternative (e.g. "Get Order Details 2").
  • Can't be empty.

Tips

  • Prefer a descriptive, human-readable name over a generic one — "Get Order Details" is easier to work with later than "HTTP Request 2", especially once a workflow has a dozen steps.
  • Renaming a step updates every reference to it elsewhere in the workflow automatically.

Step Reference

Syntax

Any input field can reference earlier data using {{expression}}. The expression is evaluated as JSONata — so simple dot-paths and more advanced queries (filters, functions) both work.

Referencing a step's output

Use the step's Reference — a camelCase identifier auto-generated from its Name (e.g. "Get Order Details" → getOrderDetails), shown read-only wherever the step's fields are configured — followed by the field path. Elsewhere in these docs this general pattern is written as {{<stepReference>.field.path}}:

{{getOrderDetails.response.data.id}}
{{getOrderDetails.response.status}}

The raw display name won't work here even though it's what you see in the UI — {{Get Order Details.response.data.id}} isn't valid, since a bare name containing spaces isn't a single JSONata identifier. Always use the camelCase Reference.

You can also reference steps by position instead of by reference:

{{steps[0].response.data.id}}

Referencing trigger data

{{trigger.headers.authorization}}
{{trigger.body.customerId}}
{{trigger.query.page}}
{{trigger.params.orderId}}
{{trigger.method}}
{{trigger.path}}

Referencing workflow variables

{{variables.myVariable}}

See Variables for the full list of variable types and more examples, including connection-type variables.

Referencing runtime variables

A separate, mutable namespace written by the Variable connector while a run is in progress — not the same as the workflow-level variables above:

{{runtimeVariables.myVariable}}

Notes

  • If an expression can't be resolved (a typo in a step name, a field that doesn't exist), it resolves to an empty string rather than failing the workflow — check your execution history if a value comes through blank.
  • Object values are automatically JSON-stringified when interpolated into a string field.

Execution Settings

Fire-and-forget

When enabled, the workflow doesn't wait for this step to complete before moving to the next one. Use it for steps whose result nothing downstream depends on — logging, analytics, notifications — so they don't add latency to the steps that matter.

Continue on error

When enabled, a failure in this step doesn't stop the workflow — execution continues to the next step. The failure is still recorded in the execution history; this just controls whether it's fatal.

Combine the two for steps that are genuinely optional to the outcome: fire-and-forget so they don't add latency, continue-on-error so a failure in them (e.g. an analytics endpoint being briefly down) doesn't take down an otherwise-successful workflow run.

Caching

What it does

When enabled, a step's result is cached for a configurable TTL (time-to-live, in seconds). If the step runs again with the same effective cache key before the TTL expires, the cached result is returned instead of re-running the step.

Configuring it

  • Enabled — turn caching on or off for this step.
  • TTL — how long (in seconds) a cached result stays valid.
  • Cache key template — an expression (supporting the same {{...}} step reference syntax used elsewhere) that determines what counts as "the same call". By default this is based on the step's resolved input; a custom template lets you cache more narrowly or broadly than that.

When to use it

Good candidates are steps that call something slow or rate-limited but return the same answer for the same input within a short window — a lookup against a rarely-changing external system, for example. Skip it for steps whose result must always be fresh (anything involving live inventory, pricing, or payment state).

Locking

What it does

When enabled, only one execution of this step (for a given lock key) can run at a time. If a second execution tries to run the same step while a lock is held, it waits until the lock is released or the hold period elapses.

Configuring it

  • Enabled — turn locking on or off for this step.
  • Lock key — an expression (supporting the same {{...}} step reference syntax used elsewhere) that determines what counts as "the same resource". By default the lock is scoped to the step itself; a custom key lets you lock per-customer, per-order, or any other identifier that needs serialized access.
  • Period — how long (in seconds) the lock is held before it's automatically released, in case an execution doesn't complete normally.

When to use it

Use it whenever concurrent executions could race on the same resource — e.g. two workflow runs both trying to update the same order's status at once. A lock key scoped to the order id ensures only one of them proceeds at a time.