---
title: "Connect to Your CRM"
description: "How to send form responses into Salesforce or Microsoft Dynamics — looking up existing records, creating and updating them, and mapping fields."
slug: "connect-to-your-crm"
audience: "all"
generated_at: "2026-10-16T18:51:19.070Z"
screenshots:
  - "/docs/screenshots/connect-to-your-crm/connections.png"
---

## What the CRM connectors do

FormAssembly's CRM connectors let a workflow read from and write to your CRM as part of a submission. Two CRMs are covered in this article:

- **Salesforce** — FormAssembly can look up Salesforce records, create new records, update existing ones, and upload files on any standard or custom object your Salesforce user can access.
- **Dynamics 365 (Dataverse)** — FormAssembly can look up Dynamics records, create new records, and update existing ones on any Dataverse table (entity) available to your Dynamics user.

Both connectors work in two directions. An outbound action (**Create a record**, **Update a record**, and — on Salesforce only — **Upload a file**) pushes submitted answers out to the CRM. An inbound action (**Lookup a record**) reads a CRM record and brings its values back into the workflow — useful for prefilling a form with what you already know about a contact, or for fetching an ID that a later action needs.

The two connectors differ in how you configure them. **Only Salesforce connector steps open an editable action card.** A Dynamics 365 step shows a read-only summary ending "Connector configuration is managed by the AI agent." — configure a Dynamics 365 step by asking Fai, FormAssembly's built-in AI assistant, reachable from the chat panel. Everything in this article about action cards, stages, pickers and mapping tables describes the Salesforce step; the Dynamics 365 capabilities and limits are stated in their own section at the end.

This matters because CRM work is usually the point of the form: instead of exporting responses and importing them later, the record exists in Salesforce or Dynamics 365 the moment someone submits.

For spreadsheet, storage, and payment connectors, see the other connector articles — this article covers the CRM connectors only.

## Before you start

You need an authorized connection to the CRM. Connections are listed on the **Connections** page, where Salesforce has its own settings link ("Salesforce connection settings"). Dynamics 365 is shown on the Connections page as **Coming soon** and cannot be opened there — connect Dynamics 365 by asking Fai. If you have not authorized your CRM yet — or if a connection has lapsed and needs reconnecting — see the **Connecting Services** article, which owns authorizing, reconnecting, sandbox connections, and disconnecting.

The Salesforce connector and the Dynamics 365 connector are both account add-ons. If your account does not include the connector you are using, FormAssembly refuses to save a workflow that contains that connector's step — ask your account administrator to add the connector to your account.

![The Connections page in FormAssembly, listing available service connections](/docs/screenshots/connect-to-your-crm/connections.png)

## What your CRM account needs to permit

FormAssembly acts as the user who authorized the connection. That user's permissions define the ceiling on everything below.

- **Salesforce:** the authorizing user needs API access enabled, plus read/create/update permission on each object and field you intend to use. FormAssembly lists an object only if the user can create it, update it, or both query and retrieve it. A field appears if the user can write it or filter on it — so read-only fields such as Created Date, Last Modified Date, formula fields and rollup summaries can be used in match rules and read into the workflow, but cannot be written to. Internal platform objects, and objects whose names end in `__Share` or `__Tag`, are never offered.
- **Dynamics 365:** the authorizing user needs a security role granting read/create/write on each table you intend to use. FormAssembly lists tables that are valid for Advanced Find, and lists a column only if it is valid for create or valid for update.

If an object or field you expect is missing from a picker, the usual cause is permissions on the CRM side, not FormAssembly.

## How do I add a CRM action to a workflow?

CRM work happens in a **connect** step in the workflow builder. A Salesforce step can contain several actions, and each action is one operation against the CRM (create a Contact, update an Opportunity, look up an Account).

1. Open the workflow and switch to **Focus** view in the builder, then click **Add step**. **Add step** is a Focus-view control; **Overview** is a read-only map of the whole workflow, so nothing on the Overview canvas adds, moves or removes a step. You can also ask Fai to add a step from either view.
2. In the **Add step** menu, the **Connect** group has one item, **Salesforce**. Add a Salesforce step from there and open it to configure it. A Dynamics 365 step is not in the menu — ask Fai to add one.
3. Confirm which connection the step uses. Until a connection is chosen, a Salesforce step shows "Choose a Salesforce connection above to configure this step." and no action configuration is displayed.
4. Configure each action stage by stage: **Operation & record**, **Find the record** (for Update and Lookup), **Field mapping**, **Return data to the workflow**, **Additional options**, plus **File to upload** for an upload action and **Handle repeated content** when the action references a repeatable section.
5. Use **Test** in the workflow toolbar to run the step before you **Publish**.

Every Salesforce action lives in an expandable action card. Nothing described below is hidden behind an "advanced" disclosure — no-match behavior and error handling stay visible while you configure. An action that builds on an earlier one also shows a read-only **Dependencies** section, which shows only its description; ask Fai to see or change what an action depends on.

### How the action's direction works

Each action's direction is derived from the operation you choose; there is no separate direction control on the action card. **Create a record**, **Update a record** and **Upload a file** are outbound (data leaves FormAssembly). **Lookup a record** is inbound (data comes back into the workflow).

The column order of the mapping table follows the action's direction, so it always reads left to right in the direction the data actually moves: FormAssembly on the left for an outbound action, Salesforce on the left for a Lookup.

Changing the operation on an action that already has mapped rows opens a **Switch the operation?** dialog, because a mapping row means the opposite thing in each direction. The dialog names how many mapped fields and returned fields the switch clears. Choose **Switch and clear mapping** to switch and clear the rows that no longer apply, or **Cancel** to leave the action as it is. The change cannot be undone, so build the other direction as a new action if you want to keep both.

### Choosing the object and operation

The **Operation & record** stage carries two controls, side by side:

- **Operation** — a dropdown listing the operations the connector declares. Salesforce offers **Create a record**, **Update a record**, **Lookup a record** and **Upload a file**, and all four are fully configurable in the action card.
- **Record** — a searchable picker over the Salesforce objects your connection can see. Type to filter, and press `Escape` or click outside to close.

One line beneath the record picker states what the chosen operation will do, for example "Adds a new record every time" for **Create a record**.

Choosing **Update a record** or **Lookup a record** reveals the **Find the record** stage (see Match rules below). Choosing **Upload a file** reveals the **File to upload** stage instead, and narrows the record picker to the records Salesforce can attach a file to.

Changing the record type **never clears a mapping row you gave a value to**. Rows holding a value are kept and re-validated against the new record type: a mapping that still resolves is left alone, and one that no longer resolves is flagged rather than silently deleted. Two kinds of empty row are removed: on a Create action, a row with a target and no source, and an upload action's association row, which is replaced with the new record's.

### Repeated sections: how many times an action runs

When an action references a field inside a repeatable section — from a mapping row, a match rule or a precondition — a **Handle repeated content** stage appears with a single record selector. It answers the one question the rest of the card cannot: when a respondent submits three rows, does this action run once or three times?

- For an outbound action the selector reads **Run this action**, with **Once per submission** or **Once for every submitted [Section label]**; for a Lookup it reads **Repeat this content for every result found**.
- Options are the whole submission plus every outermost repeatable section the action references, grouped by step. Choosing a section shows an inline note: "This action runs once per submitted instance of [Section Name]."
- The selection never limits which fields you can map. It only controls how many times the action runs.
- Filling one section instance per result found is not yet supported on a Lookup, and an upload whose file field sits inside a repeatable section always runs once per uploaded file.

## How do I map form answers into my CRM?

Field mappings live in a table at the bottom of each expanded Salesforce action card. Every row pairs a **target** field with a **source** value.

Click **Add another** to add a row, then choose the source type:

- **Field** — one combined picker listing form fields from any Collect step in the workflow (grouped by step) and workflow variables set by earlier actions in the same run.
- **Value** — a single input for static text or a formula. Type plain text and it is sent as-is. Click **Insert** to drop a field token into the text, so you can combine several fields with surrounding text, such as "Hi [Full Name], your total is [Amount]". An expand icon opens the Formula Editor for conditional logic or system values such as today's date.

On a **Lookup a record** action the columns are reversed: the source is a picker over the Salesforce record's fields (read-only fields included), and the target is a FormAssembly field or a workflow variable. On a Lookup row you can also create the variable as you go: type a name into the target picker and choose **Declare "{name}" as a new variable**, and FormAssembly declares a text variable on the connect step and points the row at it. If you then delete the row, clear its target, or point it somewhere else while the step's panel is still open, FormAssembly removes the variable it declared for you — as long as nothing else in the workflow writes to it. A step that only reads the variable does not keep it: that reference is left pointing at nothing, and publishing is refused. Everything else about the variable, such as a different type or a description, is edited in the step's settings panel.

A row's target field is required. A row with no source shows "Choose or type a value" and is treated as unmapped — it is not sent to the CRM. An unfinished row is not marked as an error while you are still building it: only after a publish or test attempt has been refused does an empty source read "Missing value" and an empty target read "Missing field."

### Required fields

Fields the CRM object requires are marked with an indicator both in the target picker and on a mapped row, on **Create a record** actions only. When you set (or change) a Create action's object, FormAssembly automatically adds one row for each required field that is not already mapped, with the target pre-filled and the source left as "Choose or type a value." These rows cannot be deleted, and their target cannot be cleared, because removing the row would hide the reminder without removing the CRM's constraint — you can still point the row at a different field. Required indicators and rows are not shown for Update or Lookup actions, since required-ness is a create-time constraint.

### Map specific values

On rows whose FormAssembly side is a choice field (select, multi-select, radio, or checkbox), a **Map specific values** toggle lets you translate form answers into the values your CRM expects — for example, sending `1` instead of `Yes`. Press the toggle to expand a panel below the row, listing one line per option of that field, in form order.

- **Map specific values works in both directions.** On an outbound row the form option is the key and the Salesforce value is what gets stored; on a **Lookup a record** row the Salesforce value is the key and the form option is what gets filled in.
- When Salesforce lists a field's allowed values — a picklist or multi-select picklist — the Salesforce side is a dropdown of those values. For a field with no enumerated values, such as a checkbox or a text field, you type the value instead.
- **There is no catch-all row.** An option you leave unmapped is sent unchanged, so you only need to fill in the options whose value has to differ.
- On a Lookup, two options cannot map to the same incoming value; FormAssembly refuses the second and names the option that already claims it.
- The map belongs to the row's choice field. Pointing that end of the row at a different field clears the map.

### Return Salesforce data to the workflow

**Return data to the workflow** is an optional stage on an outbound action. Click **+ Add variable** to store part of the result in a workflow variable that later steps can use. The most common use is capturing the new record's ID and URL so a later action, a condition, or a confirmation message can refer to the record that was just written.

Salesforce returns the created or updated record's ID and a link to it. A variable written here is workflow-scoped: anything later in the workflow can reference it, not just later actions in the same step. As on a Lookup row, typing a name into the destination picker offers **Declare "{name}" as a new variable**, which declares a text variable on the connect step and points the row at it; clearing or retargeting that row while the panel is still open removes the variable again if nothing else in the workflow writes to it. A step that only reads the variable does not keep it: that reference is left pointing at nothing, and publishing is refused. A half-filled row — a result field with no destination, or a destination with no result field — is kept and refused at publish.

A **Lookup a record** action does not use this stage. A Lookup writes what it fetched through its own mapping table, where the Salesforce field is the source and the FormAssembly field or variable is the target — that is the mechanism that prefills.

## Upload a File actions

**Upload a file** is a Salesforce operation for sending a respondent's uploaded file into Salesforce. Choose the target record — **Attachment** or **ContentVersion** — then use the **File to upload** picker to choose which file field in your Collect steps this action attaches. FormAssembly fills in the file's own required fields automatically at submission time (file name, file content, and file path), so they never occupy a row in the mapping table.

What is left for you to set is the parent association: **ParentId** for an Attachment, **FirstPublishLocationId** for a ContentVersion. FormAssembly inserts that row for you as soon as you choose the record. Map it to the ID of the record the file belongs to, usually a variable returned by an earlier Create or Lookup action.

Leaving the association row without a source blocks publishing, and the message names the action so you know which one to fix. Publishing is also blocked if the action has a recognized record but no file chosen. Changing the target record replaces the association row with the new record's; rows you authored yourself are untouched. You can override a file-derived field — for example, setting **Title** from a form field — by adding an ordinary row for it; delete that row and the field goes back to being filled from the file.

## Match rules for Update and Lookup

When an action's operation is **Update a record** or **Lookup a record**, the **Find the record** stage shows a rule builder that identifies which Salesforce record the action targets.

Each rule is a row with a field (a searchable picker over the Salesforce record, whose placeholder names the service — "Choose a Salesforce field"), an operator filtered to what the connector supports, and a value. The value can be typed text, or you can pick any Collect step's fields and workflow variables — including a field from the repeated-section instance the action is currently processing. **+ Add condition** appends another rule. Every row carries a letter — A, B, C… — as its stable identity, in every mode.

Once there is more than one rule, a combinator control appears between the rules and **+ Add condition**, reading **Match [All] of these conditions**. **All** (the default) requires every rule to be true; **Any** requires at least one. **Custom** reveals a logic input where you write an expression over the rows by letter, such as `(A AND B) OR C`, for mixed logic that All and Any cannot express. The expression is validated as you type, and a reference to a row you later delete is flagged rather than silently rewritten.

**Find the record** and **Precondition** rules use the same rule builder as form conditions and branching, so they offer **is one of**, **is not one of** and **is between** alongside the ordinary comparison operators. Only pattern-matching operators are not offered, because their wildcards match nothing useful when typed into a single value box.

**Respondent input is matched literally in Salesforce lookups.** A `%` or `_` a respondent types is matched as that character, not as a wildcard.

A started but unfinished rule — a field with no value, or a value with no field — is saved and survives a reload, and it blocks publishing rather than being quietly dropped. **An Update or Lookup action with no conditions at all also blocks publishing, on Salesforce and Dynamics 365 alike, and a test run is refused on the same terms.**

If a step's conditions were written by Fai or the API using logic the rule builder cannot show as rows, FormAssembly shows them as plain-language text with the note that this editor cannot show them as rows yet, plus a **Replace conditions** button. Replacing asks for confirmation, then clears the stored conditions and opens one empty row.

The same rule builder is used for preconditions (see below).

## When no match is found

Each action has a **When no match is found** dropdown, with a one-line description of the consequence under the selected option. The available options are filtered to what the connector supports:

- **Skip this action and dependent actions** — later actions that depend on this one will not run.
- **Fail this step and follow the error path** — this step fails and the workflow's failure path is triggered.
- **Continue dependent actions** — proceed as if nothing happened; the action does nothing, and later actions still run.

An **Update a record** action that matches no record behaves as a no-match: FormAssembly does not fall back to creating a record.

FormAssembly writes this choice, and the multiple-match choice, the moment an action switches into Update or Lookup. An action that arrives from Fai or the API without one shows "Choose an option" with no consequence line, reads as incomplete, and blocks publishing until you choose.

## When multiple matches are found

A **When multiple matches are found** dropdown controls what happens when the match rules resolve to more than one record. When several records match, an Update or Lookup action can skip, stop, or use one record — there is no option to act on every match. To change several records, add more actions.

- **Use the most recently modified record** — the action acts on the match the CRM last modified. A caption under the dropdown names the method, so it is never hidden: Salesforce orders by Last Modified Date descending, with record ID ascending behind it as a tiebreaker, so the pick is always deterministic. Where the object has no Last Modified Date, the caption reads "This object has no Last Modified Date, so the record ID decides."
- **Skip this action and dependent actions** — later actions that depend on this one will not run.
- **Fail this step and follow the error path** — this step fails and the workflow's failure path is triggered.

Choosing the record by a sort of your own is planned but not yet offered. Until it ships, use match conditions to narrow the result when "most recently modified" is not the record you want.

## Preconditions

Preconditions are how you make a CRM write conditional — for example, only creating an Opportunity when an amount exceeds a threshold. A **Precondition** is an optional stage that runs the action only when its rules are true, and it is not part of an action until you add one: choose **Add precondition** from the action's own menu to reveal the section, and **Remove precondition** to remove it and clear the stored conditions. With no precondition, the action always runs once the action it depends on completes.

The precondition section uses the same rule builder as matching — including **All**, **Any** and **Custom** logic — but over the workflow's own fields and variables rather than the Salesforce record's fields. An unmet precondition skips the action and everything that depends on it; it is not an error, and actions that do not depend on it still run.

An unfinished precondition rule — a field with no value, or a value with no field — blocks **Publish** and **Test**, and the refusal names the action that holds it.

## Step behavior on failure

One respondent-facing failure message applies to the whole connect step — not one per action — and its controls live in the step's settings panel: **Message shown to the respondent**, **Include the original error message from [Connector]**, and **On error, go to**. Enter the message shown to the respondent, or leave it blank to use the default banner. **Include the original error message from [Connector]** appends the raw CRM error; it defaults off, because raw CRM errors are useful in internal or test workflows but risky on respondent-facing production forms. **On error, go to** sets which step the workflow's failure path leads to.

An email notification to the workflow owner when a step fails is a separate mechanism and is not configured here.

## Additional options

**Additional options** is the action's last stage and starts collapsed. It shows only a summary of the options already set on the action — for example "Set: preserve blank values" — and carries no control for changing them.

To keep a blank form answer from overwriting an existing Salesforce value, ask Fai to turn that option on for the action, or leave the field unmapped. The setting is per action, not per step, because whether a blank should erase a stored value depends on what that action writes. It applies to Update actions only; there is no existing value to preserve on a Create. If every mapped field on an update resolves to blank while this option is on, FormAssembly skips the action rather than sending an empty update.

## Salesforce-specific behavior and limits

- **Duplicate rules** are acknowledged automatically by default, so a save that trips a duplicate alert still goes through. Salesforce **assignment rules** are applied on create and update by default.
- **Records saved by FormAssembly do not pollute your recent items** in Salesforce.
- **Blank values are sent on update by default,** which overwrites the Salesforce field with a blank value. Ask Fai to turn on the option that preserves blank values for the action, or leave the field unmapped, if you want to preserve what is already there.
- **Read-only fields cannot be written.** A formula, rollup summary or system audit field can be used in a match rule or read into the workflow, but it is not a valid mapping target for a Create or Update action.
- **Multi-select picklist fields hold several values at once.** FormAssembly handles the Salesforce format for you: a Lookup brings back each selected value separately, and an outbound action can send several values into one multi-select picklist field.
- **One record per action.** An Update action updates a single record; there is no multi-record update. Add more actions when you need to touch several records.
- **Upsert by external ID is not supported.** To achieve the same result, use an Update action with a match rule on the external ID field, or a Lookup followed by a conditional Create.
- **Salesforce daily API call limits apply.** An action that targets a repeated section may run several times per submission, which counts against those limits.

## Dynamics 365-specific behavior and limits

A Dynamics 365 connect step shows a read-only summary of what it will do, ending "Connector configuration is managed by the AI agent." To create, update or look up Dynamics 365 records, describe what you want in the chat panel and let Fai configure the step; the configuration is not editable in the builder today. The Dynamics 365 connection itself is also set up by asking Fai, since the Connections page shows Dynamics 365 as **Coming soon**. A Dynamics 365 step can still send submitted answers into Dataverse tables and prefill a form from a Dynamics record, and it runs at submission time like any other step.

- **An Update or Lookup action with no conditions cannot be published**, exactly as on Salesforce, and a test run is refused on the same terms.
- **Option set (picklist), state, and status fields use numeric values, not display labels.** Ask Fai to map the underlying numeric value rather than the label shown in Dynamics.
- **Lookup fields take the related record's ID (a GUID), not its name.** A common pattern is a lookup action that finds the parent record, stores its ID in a workflow variable, and a later action that maps that variable into the lookup field.
- **Updates are unconditional.** Dynamics updates do not perform a concurrency check, so the last write in a run wins.
- **One Dynamics instance per connection.** If your Dynamics account has access to more than one instance, FormAssembly uses the first one discovered when the connection is authorized.
- **Upsert by alternate key is not supported.** Use an update with match conditions, or a lookup plus a conditional create.
- **File and image columns, and activity party fields, are not supported** as mapping targets, and there is no file upload operation for Dynamics 365.
- **Wildcard "like" matching is not available in Dynamics match rules.** Use the **contains**, **starts with** or **ends with** operators instead; publishing is blocked if a rule uses a pattern-style match, because the pattern would be matched as literal text.

## Tips & Troubleshooting

- **An object or field is missing from a picker.** FormAssembly only lists what the authorizing CRM user can access. Check that user's profile, permission set, or security role on the CRM side, then reopen the picker.
- **The action card is empty and asks for a connection.** A Salesforce step shows "Choose a Salesforce connection above to configure this step." until you select a connection. Choose one and the stages appear.
- **The step reports a failure and asks you to reconnect.** CRM access has lapsed, and the connection shows **Reconnect required**. Click **Reconnect** on the **Connections** page — see the **Connecting Services** article for the reconnect flow.
- **Nothing was written to the CRM and the workflow continued anyway.** An Update or Lookup action found no matching record and the action's **When no match is found** setting is **Skip this action and dependent actions** or **Continue dependent actions**. Check the match rules against real data, or change the setting to **Fail this step and follow the error path** while testing so failures are obvious.
- **The wrong record was updated.** More than one record matched and the action took the most recently modified one. Tighten the match rules until they identify a single record.
- **A field was cleared unexpectedly on update.** Blank values are sent on update by default. Ask Fai to turn on the option that preserves blank values for that action, or remove that mapping row.
- **A required row cannot be deleted.** That is intended — the CRM requires the field. Fill in a source, point the row at a different field, or change the action's object if the field does not apply.
- **A variable I declared from a mapping row disappeared.** FormAssembly removes a variable a row declared if you delete that row, clear its target, or point it elsewhere while the panel is still open and nothing else in the workflow writes to it. A step that only reads the variable does not keep it — that reference is left pointing at nothing, and publishing is refused. Declare it again from the row, or add it in the step's settings panel if you want it to stay.
- **A mapped value arrived as literal text with brackets.** Field tokens must be inserted with **Insert** in the Value editor, not typed by hand. Typed plain text is always sent as-is.
- **Switching the operation warns that it will clear my mapping.** Switching between an outbound operation and **Lookup a record** reverses what every mapping row means, so the rows cannot be carried over. Choose **Switch and clear mapping** to proceed, or **Cancel** and build the other direction as a separate action.
- **Publishing is refused and the action is named as incomplete.** The usual causes are a match rule with a field but no value (or a value but no field), an Update or Lookup action with no match rules at all, an unfinished precondition rule, a no-match or multiple-match option left as "Choose an option," an upload action's association row with no source, an upload action with no file chosen, or a mapping, return-data or record-lookup row that uses a field or variable that no longer exists. In that last case publishing names the step and action ("…uses a field or variable that no longer exists. Open the step and choose a new one, or remove it."), and the row reads "This field or variable no longer exists in the workflow." Open the action and look for the "Missing field" and "Missing value" notes under the empty controls.
- **The conditions show as plain text with a Replace conditions button.** Those conditions use logic the rule builder cannot show as rows yet. Ask Fai to change them, or use **Replace conditions** to start over with rows — replacing cannot be undone.
- **I cannot find Add step.** **Add step** is in **Focus** view. **Overview** is a read-only map: hovering a step highlights its path and clicking a step card switches you to Focus, where the structural controls live.
- **Always test before publishing.** Use **Test** in the workflow toolbar and confirm the record in your CRM, especially for Update actions, before you **Publish** and **Share** the workflow.