From c33002ed4ab089161bbeffc286b8a2e3e5c96360 Mon Sep 17 00:00:00 2001 From: andrewreshitko Date: Mon, 7 Sep 2026 16:06:17 +0300 Subject: [PATCH 1/5] Add tutorial articles explaining standard actions and triggers Adds three getting-started tutorials covering the recurring action/trigger vocabulary (Upsert Object, Lookup Objects, Make Raw Request, polling/webhook/real-time triggers, etc.) shared across most components: what each one means, why components are named generically, and how to choose between them when building a flow. Co-Authored-By: Claude Sonnet 5 --- .../actions-and-triggers-overview.md | 71 ++++++++++++++++++ .../_getting-started/choosing-a-trigger.md | 72 +++++++++++++++++++ .../_getting-started/choosing-an-action.md | 72 +++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 content/_getting-started/actions-and-triggers-overview.md create mode 100644 content/_getting-started/choosing-a-trigger.md create mode 100644 content/_getting-started/choosing-an-action.md diff --git a/content/_getting-started/actions-and-triggers-overview.md b/content/_getting-started/actions-and-triggers-overview.md new file mode 100644 index 00000000..02a2e47f --- /dev/null +++ b/content/_getting-started/actions-and-triggers-overview.md @@ -0,0 +1,71 @@ +--- +title: Understanding Actions and Triggers +layout: article +section: Tutorials +order: 3 +description: What the standard actions and triggers you see in most components mean, why they're named the way they are, and how to pick between them when building a flow. +category: actions-and-triggers +--- + +If you've opened a few different components on the platform, you've probably noticed the same handful of names keep coming back: **Upsert Object**, **Lookup Objects**, **Delete Object By ID**, **Get New and Updated Objects Polling**, **Make Raw Request**. That's not an accident, and it's not a lack of imagination — it's a deliberate design choice, and understanding it will save you time in every flow you build afterwards. + +This article explains what that choice is, why it looks the way it does, and gives you a starting point for picking the right action or trigger. Two follow-up articles go deeper on each side: [Choosing a Trigger](choosing-a-trigger) and [Choosing an Action](choosing-an-action). + +> New to the terms "action" and "trigger" themselves? Start with the [Integration Component Overview](/getting-started/integration-component) — this article assumes you know that a trigger starts a flow and an action consumes what the trigger (or a previous action) produced. + +## Why the same few names keep showing up + +A connector built by a competitor for, say, Salesforce might expose an action literally called "Create Invoice" and another called "New Lead." That reads nicely, but it only exists because someone wrote code specifically for the `Invoice` object and specifically for the `Lead` object. The moment you need a third object — `Opportunity`, or a custom object your team added last month — that connector either doesn't support it, or a developer has to ship an update. + +Our components are built differently, most of them on top of [Open Integration Hub](https://openintegrationhub.org), an API-driven approach where the component doesn't hard-code which object it works with. Instead it exposes one **Upsert Object** action, and you tell it which object — Invoice, Lead, Opportunity, or anything else the connected system exposes — through a configuration field. The trade-off is right there in the name: **Upsert Object** is less immediately readable than **Create Invoice**, but it works with every object the API has, today and after you add a custom one next year, with nothing to wait on from us. + +Once you know this, the naming stops looking vague and starts looking like a pattern: + +* the **verb** (`Upsert`, `Lookup`, `Delete`, `Get New and Updated`) tells you what happens +* the **noun** (`Object`) is a placeholder you fill in yourself, in the action's or trigger's configuration +* a small number of connectors — the ones tied to a schema-less API, like [Airtable](/components/airtable) or a plain [REST API](/components/rest-api) component — use `Row`, `Record`, or `Resource` in place of `Object`, but the same logic applies + +## The standard vocabulary + +Across the platform's components, most of what you'll build a flow from boils down to this set. You will not find every one of these in every component — a component only exposes the operations its underlying API actually supports — but if a component has actions or triggers at all, they are very likely drawn from this list. + +| You want to... | Look for a trigger or action named... | Type | +|---|---|---| +| Start a flow when a record is created or changed, and the system has to be asked periodically | **Get New and Updated Objects (Polling)** | Trigger | +| Start a flow the instant something happens, pushed by the external system | **Webhook** | Trigger | +| Create a record, or update it if a matching one already exists | **Upsert Object** | Action | +| Create a new record only | **Create Object** | Action | +| Change a record you already have the ID for | **Update Object** | Action | +| Fetch one specific record | **Lookup Object (By ID)** | Action | +| Fetch a list of records matching some criteria | **Lookup Objects (plural)** | Action | +| Remove a record | **Delete Object (By ID)** | Action | +| Do something the standard actions above don't cover | **Make Raw Request** | Action | + +The last row matters: **Make Raw Request** (sometimes called **Raw Request** or, for GraphQL APIs, **Execute Mutation**) is the escape hatch. It sends a direct call to the connected system's API and hands you the raw response. It's more work to configure, but it means you're never stuck if your use case doesn't fit one of the friendlier actions above — reach for it deliberately, not as your first choice. [Choosing an Action](choosing-an-action) covers when it actually makes sense. + +## The dropdown is doing the work + +Here is the piece that makes the generic naming workable in practice: every one of these actions and triggers has a configuration field — usually called **Object Type**, **Object**, or **Table** — where you pick the specific thing you want to work with, populated live from your own connected account. + +That single dropdown is the difference between an abstract-sounding **Upsert Object** and a concrete result. Pick `Invoice` in that field, and the action becomes, in effect, "create or update an invoice." Pick `Contact`, and it becomes "create or update a contact." The action's name in the component list stays generic on purpose — so it can be reused for any object — but what it *does* in your flow is exactly as specific as what you configure. + +Practically, this means: + +1. Drag in the action or trigger by its generic name. +2. Open its configuration and set the **Object Type** (or equivalent) field first — most of the other fields on the step depend on it. +3. The input and output fields will update to match the object you picked, often pulling the real field list straight from the connected system. + +## Where to go from here + +* [Choosing a Trigger](choosing-a-trigger) — a closer look at polling, webhooks, and real-time streams, and how to tell which one a given component offers. +* [Choosing an Action](choosing-an-action) — a decision guide for picking between Create, Update, Upsert, Lookup, Delete, and Raw Request. +* [Creating a Basic Integration Flow](first-flow) — if you haven't built a flow at all yet, start there first. +* Each component's own documentation page (for example, [Salesforce](/components/salesforce), [AFAS](/components/afas), [Shopify Admin](/components/shopify-admin-v2)) lists exactly which of these actions and triggers it supports, plus any configuration fields specific to that system. + +## Related links + +- [Integration Component Overview](/getting-started/integration-component) +- [Integration Flow Overview](/getting-started/integration-flow) +- [Creating a Basic Integration Flow](first-flow) +- [Understanding Data Sample](/guides/data-sample-overview) +- [Mapping Data](/guides/mapping-data) diff --git a/content/_getting-started/choosing-a-trigger.md b/content/_getting-started/choosing-a-trigger.md new file mode 100644 index 00000000..cb1f6930 --- /dev/null +++ b/content/_getting-started/choosing-a-trigger.md @@ -0,0 +1,72 @@ +--- +title: Choosing a Trigger +layout: article +section: Tutorials +order: 4 +description: A practical guide to the trigger types you'll find across components — polling, webhooks, and real-time streams — and how to decide between them. +category: actions-and-triggers +--- + +Every flow starts with a trigger. Before you pick a component, it helps to know that almost every trigger on the platform is one of three kinds, regardless of which system it connects to. This article walks through each, and gives you a way to decide which one your flow needs. + +> This article assumes you've read [Understanding Actions and Triggers](actions-and-triggers-overview) first. + +## The three kinds of trigger + +| Kind | Typical name | How it starts your flow | Delay | +|---|---|---|---| +| Polling | **Get New and Updated Objects (Polling)** | The platform asks the connected system, on a schedule, "anything new or changed since last time?" | Minutes, depending on your polling interval | +| Webhook | **Webhook** / **Webhook Subscription** | The connected system pushes a notification to the platform the moment something happens | Seconds | +| Real-time stream | **Subscribe to Platform Events**, **Subscribe to PubSub**, and similar | The flow keeps an open, persistent connection to the connected system's event stream | Near-instant, but only inside a real-time flow | + +### Polling triggers + +A polling trigger — usually named **Get New and Updated Objects (Polling)** — checks the connected system on an interval you configure (for example, every 15 minutes) and starts your flow once for every record that's new or has changed since the last check. It's the most widely available trigger type, because it only requires the connected system to have a normal read API — it doesn't need the system to support webhooks or streaming at all. + +Use a polling trigger when: + +* the connected system doesn't offer webhooks for the object you care about +* a delay of a few minutes between something happening and your flow reacting is acceptable +* you want the simplest, most predictable setup — polling triggers rarely need anything configured on the *other* system's side, only here + +Keep in mind: a shorter polling interval means faster reactions but more API calls to the connected system, which can run into that system's rate limits. Match the interval to how time-sensitive the flow actually is, not to the shortest interval available. + +### Webhook triggers + +A webhook-based trigger — often just named **Webhook**, sometimes **Webhook Subscription** — takes the opposite approach: instead of asking, it waits. The connected system calls a URL the platform gives you the moment a relevant event happens, and that call starts your flow immediately. This is faster than polling and puts less load on the connected system, but it requires that system to support sending webhooks, and usually a one-time setup step (registering the webhook URL) either automatically by the component or manually in the connected system's settings. + +Use a webhook trigger when: + +* the component and the connected system both support it for the event you need +* near-real-time reaction matters for the use case (order placed, ticket created, payment received) + +See the [Webhook Overview](webhooks-overview) and [Creating a webhook flow](webhooks-flow) for a full walkthrough of setting one up. + +### Real-time stream triggers + +A smaller number of components — Salesforce's **Subscribe to Platform Events** and **Subscribe to PubSub**, for example — offer a trigger that stays connected to a live event stream from the source system. These are the fastest option, but they only run inside a [real-time flow](/guides/realtime-flows), a specific flow mode on the platform built to keep a persistent connection open. If you drop one of these triggers into a regular, non-real-time flow, it won't work — check the component's own documentation page for whether a trigger has this requirement before you build around it. + +Use a real-time stream trigger when: + +* the connected system exposes an event-streaming API (platform events, pub/sub, CDC streams) for what you need +* you're already using, or are prepared to set up, a real-time flow + +## Neither fits? Look for Delta Detection + +Some systems offer no webhooks and no reliable "changed since" field to poll against. For those, look for a flow built around the [Delta Detection component](/components/delta-detection), which keeps its own record of what it has already seen and works out what's new or changed by comparison, rather than relying on the source system to tell it. + +## Quick decision guide + +1. **Does the component offer a Webhook trigger for what you need, and can you register it?** Use it — it's the best combination of speed and simplicity for most flows. +2. **Do you need near-instant reaction and are you working in a real-time flow?** Look for a streaming trigger like Subscribe to Platform Events. +3. **Otherwise, use the polling trigger** and set the interval to match how time-sensitive the flow actually is. +4. **No webhook, no stream, and no reliable "changed since" field on the source system?** Reach for Delta Detection. + +## Related links + +- [Understanding Actions and Triggers](actions-and-triggers-overview) +- [Choosing an Action](choosing-an-action) +- [Webhook Overview](webhooks-overview) +- [Creating a webhook flow](webhooks-flow) +- [Building real-time flows](/guides/realtime-flows) +- [Delta Detection component](/components/delta-detection) diff --git a/content/_getting-started/choosing-an-action.md b/content/_getting-started/choosing-an-action.md new file mode 100644 index 00000000..0433a3b4 --- /dev/null +++ b/content/_getting-started/choosing-an-action.md @@ -0,0 +1,72 @@ +--- +title: Choosing an Action +layout: article +section: Tutorials +order: 5 +description: A decision guide for the standard actions you'll find across components — Create, Update, Upsert, Lookup, Delete, and Raw Request — and when to reach for each. +category: actions-and-triggers +--- + +Once a trigger has started your flow, actions are the steps that do something with the data — usually writing it into another system, or fetching more data from one. Most components draw from the same small set of actions. This article walks through each one and when to use it. + +> This article assumes you've read [Understanding Actions and Triggers](actions-and-triggers-overview) first. + +## Start with the Object Type field + +Whichever action you pick, the first thing to configure is almost always which kind of record it applies to — a field usually named **Object Type**, **Object**, or **Table**, and populated live from your connected account. Set this first: the rest of the action's fields (which data it expects as input, which fields it returns as output) are generated from whatever you choose here, so they won't be right — or won't appear at all — until it's set. + +## Writing data: Create, Update, or Upsert? + +| Action | Does | Needs | +|---|---|---| +| **Create Object** | Always makes a new record | Just the field values for the new record | +| **Update Object** | Changes a record that already exists | The record's ID (or another unique identifier), plus the fields to change | +| **Upsert Object** | Creates a new record, *or* updates a matching one if it finds it | A field to match on (an ID, an external ID, or another unique field) | + +If you already know for certain whether the record exists — for instance, you just created it two steps earlier in the same flow and have its ID — **Create** or **Update** is the more direct choice. If you don't know, and the flow needs to work correctly either way (a very common case: "sync this contact, whether or not we've seen them before"), **Upsert Object** is built for exactly that, and saves you from having to do a Lookup first just to decide. + +## Reading data: Lookup Object vs. Lookup Objects + +Components consistently distinguish singular from plural in this pair, so read the title carefully: + +* **Lookup Object (By ID)** — fetches exactly one record, by an ID or another value that uniquely identifies it. Use it when you know precisely which record you want. +* **Lookup Objects (plural)** — fetches a list of records matching some criteria (a filter, a search field, sometimes a full query). Use it when you need several records, or when you don't have a unique identifier to search by. + +Some components additionally expose a direct query action — for example **Query** or **Bulk Query** on Salesforce, running a SOQL statement — for cases where a simple field-match lookup isn't expressive enough. + +## Removing data: Delete Object + +**Delete Object** (sometimes **Delete Object By ID**) removes a single record, identified the same way as **Lookup Object** — usually by ID. There's no "bulk delete" equivalent in most components beyond running this once per record; if you need to remove many records, feed a list into this action from an earlier step in the flow. + +## When none of the above fits: Make Raw Request + +**Make Raw Request** (also seen as **Raw Request**, or **Execute Mutation** on GraphQL-based components) sends a request directly to the connected system's API, exactly as you construct it, and returns the raw response. Nothing about the object model or field mapping is done for you. + +Reach for it when: + +* you need an API operation the component doesn't expose as a standard action — for example, a specialized endpoint that doesn't map cleanly to create/update/lookup/delete +* you're working with an API the component only partially covers (components typically implement the endpoints most integrators need, not the entire API surface — see the [Integration Component Overview](/getting-started/integration-component#action) for why) +* you already know the target API well and configuring the URL, method, and body directly is faster than working through a generic action + +It's more configuration work — you're responsible for the URL, HTTP method, and request body — so treat it as the option you use when a more specific action doesn't cover your case, not as the default. + +## Moving large volumes: Bulk actions + +A handful of components — Salesforce is the clearest example, with **Bulk Create/Update/Delete/Upsert** and **Bulk Query** — offer bulk variants built for moving large numbers of records efficiently (tens of thousands at once), usually by accepting or producing a CSV file rather than one message per record. If you're processing more than a few hundred records in a single run and the component offers a bulk action, it will generally perform far better than looping the equivalent single-record action. + +## Quick decision guide + +1. **Writing a record and you're not sure if it exists yet?** Upsert Object. +2. **Writing a record and you're sure whether it exists?** Create Object or Update Object. +3. **Reading exactly one known record?** Lookup Object (By ID). +4. **Reading a list of records matching some criteria?** Lookup Objects. +5. **Removing a record?** Delete Object. +6. **Moving a large batch of records at once, and a Bulk action is available?** Use it instead of the single-record action. +7. **None of the above covers what you need?** Make Raw Request — check the component's documentation page first for the exact input it expects. + +## Related links + +- [Understanding Actions and Triggers](actions-and-triggers-overview) +- [Choosing a Trigger](choosing-a-trigger) +- [Understanding Data Sample](/guides/data-sample-overview) +- [Mapping Data](/guides/mapping-data) From 7534f5c203e6b789c40d478c1e9c5f6643cc3f90 Mon Sep 17 00:00:00 2001 From: andrewreshitko Date: Mon, 7 Sep 2026 16:52:59 +0300 Subject: [PATCH 2/5] Add Platform Features articles for core flow-building capabilities Rounds out the Getting Started > Platform Features list, which previously skewed toward auth/admin topics (Component Overview, Webhooks, OpenID, Recipes, Copy/Export, Re-auth) with little coverage of the core flow-building capabilities that differentiate the platform. Each article is a concise, concept-first explainer in the style of the existing "Understanding Actions and Triggers" tutorial, linking out to the full deep-dive guide in the Integrator Guide rather than duplicating its content: - Content-Based Routing - Real-Time Flows - Rebound Feature - Custom Error Handler - Flow Linking - Scheduled Executions - Data Mapping - Embedded Recipe Co-Authored-By: Claude Sonnet 5 --- .../_getting-started/content-based-routing.md | 39 +++++++++++++++++++ .../_getting-started/custom-error-handler.md | 34 ++++++++++++++++ content/_getting-started/data-mapping.md | 31 +++++++++++++++ .../embedded-recipe-overview.md | 32 +++++++++++++++ content/_getting-started/flow-linking.md | 30 ++++++++++++++ content/_getting-started/real-time-flows.md | 37 ++++++++++++++++++ content/_getting-started/rebound-overview.md | 30 ++++++++++++++ .../_getting-started/scheduled-executions.md | 32 +++++++++++++++ 8 files changed, 265 insertions(+) create mode 100644 content/_getting-started/content-based-routing.md create mode 100644 content/_getting-started/custom-error-handler.md create mode 100644 content/_getting-started/data-mapping.md create mode 100644 content/_getting-started/embedded-recipe-overview.md create mode 100644 content/_getting-started/flow-linking.md create mode 100644 content/_getting-started/real-time-flows.md create mode 100644 content/_getting-started/rebound-overview.md create mode 100644 content/_getting-started/scheduled-executions.md diff --git a/content/_getting-started/content-based-routing.md b/content/_getting-started/content-based-routing.md new file mode 100644 index 00000000..722375aa --- /dev/null +++ b/content/_getting-started/content-based-routing.md @@ -0,0 +1,39 @@ +--- +title: Content-Based Routing +layout: article +section: Platform Features +order: 12 +description: How to send each message down a different branch of your flow based on what's actually in it, instead of building a separate flow for every case. +category: platform-features +--- + +Most flows you build early on are linear: a trigger fires, and a fixed sequence of actions runs on every message the same way. That works fine until you hit a case where different messages need different handling — some orders ship domestically and some internationally, some leads are enterprise and some are self-serve, some product updates belong on one storefront and some on another. **Content-Based Routing** is the platform feature that lets one flow make that decision per-message, instead of you maintaining several near-identical flows by hand. + +## The idea + +A Content-Based Router sits in your flow like any other action, but instead of doing one thing to every message, it evaluates each message against a set of conditions you define — one per branch — and forwards the message down the first branch whose condition matches. Everything downstream of that branch only ever sees messages that satisfy it. + +![Content-Based Router principle](/assets/img/integrator-guide/cbr/cbr-principle.png "Content-Based Router principle") + +In the diagram above, a single incoming stream of orders is split by product type: sport shoes go to one downstream system, other shoe types to another. Nothing about the trigger or the incoming data changes — only what happens *after* the router does. + +## How the conditions work + +Each branch's condition is a [JSONata](http://jsonata.org/) expression evaluated against the message — the same expression language used elsewhere on the platform for [transforming data](/guides/transforming-data). The expression must resolve to `true` or `false`; the first branch that evaluates to `true` receives the message. A router configured with a single branch behaves as a simple filter — pass or drop — rather than a full multi-way split. + +Because the condition is just an expression over the message's own fields, you're not limited to equality checks on one field — you can combine multiple conditions, compare numbers, or check whether a field exists at all. + +## When to reach for it + +Content-Based Routing is worth using whenever you catch yourself about to build two or more flows that are identical except for a filter at the top, or a manual step where someone decides which system a record should go to. Common cases: + +* Splitting orders, leads, or tickets by region, tier, or type across different destination systems +* Sending only records that meet a threshold (an order total, a priority level) further down a flow, and quietly dropping the rest +* Feeding the same trigger into several unrelated downstream actions without duplicating the trigger itself + +## Related links + +- [Content-Based Routing](/guides/content-based-router) — the full walkthrough, including building a working example step by step +- [Router component](/components/router/index) +- [Transforming data](/guides/transforming-data) +- [Creating a Basic Integration Flow](first-flow) diff --git a/content/_getting-started/custom-error-handler.md b/content/_getting-started/custom-error-handler.md new file mode 100644 index 00000000..7839ed05 --- /dev/null +++ b/content/_getting-started/custom-error-handler.md @@ -0,0 +1,34 @@ +--- +title: Custom Error Handler +layout: article +section: Platform Features +order: 15 +description: How to turn a flow's errors into a Slack message, an email, or a spreadsheet row instead of letting them disappear into the execution log. +category: platform-features +--- + +By default, when a step in your flow fails, that failure is recorded in the flow's execution log — useful if you're actively watching, easy to miss if you're not. The **Custom Error Handler** feature lets you attach an actual action to a flow's errors, so instead of just being logged, they get *sent* somewhere: an email to your team, a row in a tracking spreadsheet, a message in Slack, or anything else a component's action can do. + +## How it works + +Any component that has an action can be attached as a flow's error handler — there's no separate "error handling" component to learn. From a flow's draft, you add error handling and pick which component and action should run when something in the flow fails; that action then receives the error's details (its name and message, at minimum) as its input, the same way any other action receives input from a previous step. + +Two examples cover most use cases: + +* **Email** — send yourself or your team the error name and message the moment something breaks, so you find out from your inbox instead of by a customer noticing first. +* **Spreadsheet** — append a row with the error details to a shared sheet, building up a running log you can filter, sort, and share with people who don't have access to the platform itself. + +## Setting it up + +A few things are worth knowing before you configure one: + +* Your flow's draft needs at least one step already configured before you can add error handling to it. +* It generally makes sense to configure error handling last, once the rest of the flow's steps are in place. +* Decide what you actually want to happen with an error *before* you start configuring — "send an email" and "log a spreadsheet row" call for slightly different input mapping. + +## Related links + +- [Custom Error Handler](/guides/custom-error-handler) — the full walkthrough, with both the Email and Google Spreadsheet examples configured step by step +- [Managing Flow Errors](/guides/managing-flow-errors) +- [Email component](/components/email/) +- [Google Spreadsheet component](/components/gspreadsheet/) diff --git a/content/_getting-started/data-mapping.md b/content/_getting-started/data-mapping.md new file mode 100644 index 00000000..d82fd316 --- /dev/null +++ b/content/_getting-started/data-mapping.md @@ -0,0 +1,31 @@ +--- +title: Data Mapping +layout: article +section: Platform Features +order: 18 +description: How data actually moves from one step of a flow to the next, and the tools you have for shaping it along the way. +category: platform-features +--- + +A trigger or action produces output fields; the next step in your flow needs input fields — and those two field lists are almost never identical in name, shape, or format. **Data Mapping** is where you tell the platform exactly how one step's output becomes the next step's input, and it's a step you'll touch in essentially every flow you build. + +## The mapping screen + +Every step after the first in a flow has a mapping screen showing the incoming fields on one side, sourced from a real [data sample](/guides/data-sample-overview) pulled from the previous step, and the fields the current step expects on the other. Mapping a simple value is a matter of connecting the two — drag a source field onto a destination field, or type a fixed value directly into it. + +Not every field needs a value: a field marked required must be mapped or filled in before the step will save; optional fields can be left empty. Getting this distinction right early avoids a class of runtime errors that only show up once real data starts flowing. + +## When a straight connection isn't enough + +Source and destination fields don't always line up cleanly — a date might need reformatting, a full name might need splitting into first and last, a numeric total might need to be summed across an array. For that, the mapper has a **developer mode** where any field's value can be written as a [JSONata](http://jsonata.org/) expression instead of a straight connection — the same expression language used in [Content-Based Routing](content-based-routing) conditions. The mapper evaluates the expression against your real data sample as you write it, so you can see the actual result before the flow ever runs. + +## Arrays and nested objects + +Mapping gets more interesting once either side involves an array — for example, mapping each line item of an order to a corresponding line in an invoice. The platform supports array-to-array mapping directly in the same interface, including cases where the array itself contains objects with their own nested fields, without requiring a separate step or a hand-written loop. + +## Related links + +- [Mapping Data](/guides/mapping-data) — the full walkthrough, including array-to-array mapping and array-of-objects examples +- [Transforming data](/guides/transforming-data) — a closer look at JSONata itself: strings, numbers, dates, and arrays +- [Understanding Data Sample](/guides/data-sample-overview) +- [Content-Based Routing](content-based-routing) diff --git a/content/_getting-started/embedded-recipe-overview.md b/content/_getting-started/embedded-recipe-overview.md new file mode 100644 index 00000000..46706f11 --- /dev/null +++ b/content/_getting-started/embedded-recipe-overview.md @@ -0,0 +1,32 @@ +--- +title: Embedded Recipe +layout: article +section: Platform Features +order: 19 +description: How to let an end user activate a pre-built integration from a link or an iframe in your own product, without ever seeing the platform itself. +category: platform-features +--- + +A [Recipe](recipes) is a pre-configured, reusable flow template — the fast way to give someone a working integration without them building one from scratch. **Embedded Recipe** takes that a step further: it lets you hand a recipe to an end user who shouldn't (or doesn't want to) manage the platform directly at all, by generating a link, or an iframe, that walks them straight to activating it under their own account. + +## Who this is for + +This is aimed squarely at the case where you're offering integrations *through* your own product — a SaaS app that wants to let its customers connect their own Salesforce or Shopify account, say — rather than at internal use where your own team is comfortable working in the platform's UI directly. The end user authenticates and activates the recipe; nothing about the underlying platform is exposed to them beyond that. + +## How it's set up + +Setting one up is done through the API, from your own backend, ahead of handing the link to the user: + +1. Create a user via the API, and add them as a member of the relevant contract and workspace. +2. Scope that user's permissions down to only what's needed to activate the recipe — nothing more. +3. Generate a one-time token for the user, which is what authenticates them without a normal login. +4. Build the activation URL from your platform domain, the recipe's ID, and that one-time token. + +That URL can be handed to the user directly, or embedded in an `