<!-- BusyBot node reference — https://busybot.net/tools/keap/ -->

> Node: Keap (`keap`) · Action (binary) · v1
> Category: Sales · Credentials: Keap OAuth2 (`keapOAuth2Api`)
> Updated: 2026-08-16

# Keap

> Manage Keap CRM contacts, companies, orders, emails, and files

## Overview

The Keap tool provides comprehensive CRM operations against the Keap (Infusionsoft) REST API v1. It supports 8 resources: Company (create, getAll), Contact (upsert, delete, get, getAll), Contact Note (create, delete, get, getAll, update), Contact Tag (create, delete, getAll), Ecommerce Order (create, delete, get, getAll), Ecommerce Product (create, delete, get, getAll), Email (createRecord, deleteRecord, getAll, send with binary attachments), and File (delete, getAll, upload with binary data). File upload reads binary data from upstream items. Email send supports attaching binary data from upstream items. Uses OAuth2 Bearer token authentication with automatic refresh.

**Category:** Sales  
**Tool Name:** `keap`  
**Version:** 1

**Appearance:** Icon: `lucide-CircleDollarSign` | Color: `#39C049`

## Node Type

**Action (Binary)** — handles file/binary data operations

## Input / Output

| Direction | Port(s) |
|-----------|--------|
| Input | `Input` |
| Output | `Output`, `Error` |

## Credentials

This tool requires **Keap OAuth2** credentials.
See the [Credentials Guide](https://busybot.net/credentials/keap-oauth2-api/) for setup instructions.

### Resources

| Resource | Value |
|----------|-------|
| Company | `company` |
| Contact | `contact` |
| Contact Note | `contactNote` |
| Contact Tag | `contactTag` |
| Ecommerce Order | `ecommerceOrder` |
| Ecommerce Product | `ecommerceProduct` |
| Email | `email` |
| File | `file` |

### Operations

Which operations are available depends on the selected resource — see the parameter subsections below.

| Operation | Value | Description |
|-----------|-------|-------------|
| Create | `create` | Create a company, note, order or product — or add tags to a contact. |
| Get Many | `getAll` | Retrieve many records of the selected resource. |
| Create or Update | `upsert` | Create a new contact, or update if it already exists (upsert). |
| Delete | `delete` | Delete a contact, note, order, product or file — or remove tags from a contact. |
| Get | `get` | Retrieve a single contact, note, order or product. |
| Update | `update` | Update a note. |
| Create Record | `createRecord` | Create a record of an email sent. |
| Delete Record | `deleteRecord` | Delete an email record. |
| Send | `send` | Send an email. |
| Upload | `upload` | Upload a file. |

### Parameters

#### Company: Create

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Company Name | `string` | Yes | — | Name of the company to create. |
| Additional Fields | `collection` | No | `{}` | Optional fields sent with the new company. |
| — Email | `string` | No | — | Email address for the company. |
| — Notes | `string` | No | — | Free-text notes stored on the company record. |
| — Opt In Reason | `string` | No | — | Why the company opted in to marketing. |
| — Website | `string` | No | — | Company website address. |
| Address | `json` | No | `{}` | Company address as JSON: { "country_code": "US", "line1": "123 Main St", "locality": "City", "region": "CA", "zip_code": "90210" } |
| Fax | `json` | No | `{}` | Company fax as JSON: { "type": "FAX1", "number": "+1234567890" } |
| Phone | `json` | No | `{}` | Company phone as JSON: { "type": "PHONE1", "number": "+1234567890" } |

#### Company: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |
| Options | `collection` | No | `{}` | Extra settings applied to the search. |
| — Company Name | `string` | No | — | Company name to query on |
| — Order | `options` | No | — | Which field the results are sorted by. |
| | | | | Options: `datecreated` (Date Created), `id`, `name` |
| — Order Direction | `options` | No | — | Sort direction. |
| | | | | Options: `ascending` (ASC), `descending` (DESC) |
| — Fields | `string` | No | — | Comma-delimited list of Company properties to include in the response. |

#### Contact: Create or Update

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Duplicate Option | `options` | Yes | `email` | Duplicate checking option. If a match is found, the existing contact is updated. |
| | | | | Options: `email`, `emailAndName` (Email And Name) |
| Additional Fields | `collection` | No | `{}` | Optional fields sent with the contact. |
| — Anniversary | `string` | No | — | The contact's anniversary date. |
| — Company ID | `number` | No | `0` | ID of the company the contact belongs to. |
| — Contact Type | `string` | No | — | Contact type. Use GET /setting/contact/optionTypes to list available types. |
| — Family Name | `string` | No | — | The contact's family (last) name. |
| — Given Name | `string` | No | — | The contact's given (first) name. |
| — IP Address | `string` | No | — | Originating IP address recorded with the contact. |
| — Job Title | `string` | No | — | The contact's job title. |
| — Lead Source ID | `number` | No | `0` | ID of the lead source the contact came from. |
| — Middle Name | `string` | No | — | The contact's middle name. |
| — Opt In Reason | `string` | No | — | Why the contact opted in to marketing. |
| — Owner ID | `string` | No | — | User ID of the contact owner. Use GET /users to list available users. |
| — Preferred Locale | `string` | No | — | The contact's preferred locale. |
| — Preferred Name | `string` | No | — | The name the contact prefers to be called. |
| — Source Type | `options` | No | — | How the contact record originated. |
| | | | | Options: `API`, `IMPORT` (Import), `LANDINGPAGE` (Landing Page), `MANUAL` (Manual), `OTHER` (Other), `UNKNOWN` (Unknown) |
| — Spouse Name | `string` | No | — | Name of the contact's spouse. |
| — Timezone | `string` | No | — | IANA timezone string (e.g. America/New_York) |
| — Website | `string` | No | — | The contact's website address. |
| Addresses | `json` | No | `[]` | Contact addresses as JSON array: [{ "field": "BILLING", "country_code": "US", "line1": "123 Main St", "locality": "City", "region": "CA", "zip_code": "90210" }] |
| Emails | `json` | No | `[]` | Contact emails as JSON array: [{ "field": "EMAIL1", "email": "user@example.com" }] |
| Faxes | `json` | No | `[]` | Contact fax numbers as JSON array: [{ "field": "FAX1", "number": "+1234567890" }] |
| Phones | `json` | No | `[]` | Contact phones as JSON array: [{ "field": "PHONE1", "number": "+1234567890" }] |
| Social Accounts | `json` | No | `[]` | Social accounts as JSON array: [{ "type": "Facebook", "name": "username" }] |

#### Contact: Delete

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Contact ID | `string` | Yes | — | ID of the contact to delete. |

#### Contact: Get

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Contact ID | `string` | Yes | — | ID of the contact to retrieve. |
| Options | `collection` | No | `{}` | Extra settings applied to the lookup. |
| — Fields | `string` | No | — | Comma-delimited list of Contact properties to include. |

#### Contact: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |
| Options | `collection` | No | `{}` | Filters and sorting applied to the search. |
| — Email | `string` | No | — | Return contacts with this email address. |
| — Given Name | `string` | No | — | Return contacts with this given (first) name. |
| — Family Name | `string` | No | — | Return contacts with this family (last) name. |
| — Order | `options` | No | — | Which field the results are sorted by. |
| | | | | Options: `date` (Date), `email`, `id`, `name` |
| — Order Direction | `options` | No | — | Sort direction. |
| | | | | Options: `ascending` (ASC), `descending` (DESC) |
| — Since | `string` | No | — | Date to start searching from (LastUpdated) |
| — Until | `string` | No | — | Date to search to (LastUpdated) |

#### Contact Note: Create

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| User ID | `string` | No | — | The Keap user ID to create the note on behalf of. Use GET /users to list available users. |
| Contact ID | `string` | No | — | ID of the contact the note is attached to. |
| Additional Fields | `collection` | No | `{}` | Optional fields sent with the new note. |
| — Body | `string` | No | — | Body text of the note. |
| — Title | `string` | No | — | Title of the note. |
| — Type | `options` | No | — | What kind of interaction the note records. |
| | | | | Options: `appointment`, `call`, `email`, `fax`, `letter`, `other` |

#### Contact Note: Delete

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Note ID | `string` | Yes | — | ID of the note to delete. |

#### Contact Note: Get

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Note ID | `string` | Yes | — | ID of the note to retrieve. |

#### Contact Note: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |
| Filters | `collection` | No | `{}` | Narrow which notes are returned. |
| — Contact ID | `number` | No | `0` | Only return notes attached to this contact. |
| — User ID | `string` | No | — | Filter by user ID. Use GET /users to list available users. |

#### Contact Note: Update

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Note ID | `string` | Yes | — | ID of the note to update. |
| Additional Fields | `collection` | No | `{}` | The fields to change. Anything you leave out is left as it is. |
| — Body | `string` | No | — | Body text of the note. |
| — Contact ID | `number` | No | `0` | Move the note to this contact. |
| — Title | `string` | No | — | Title of the note. |
| — Type | `options` | No | — | What kind of interaction the note records. |
| | | | | Options: `appointment`, `call`, `email`, `fax`, `letter`, `other` |
| — User ID | `string` | No | — | The Keap user ID. Use GET /users to list available users. |

#### Contact Tag: Create

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Contact ID | `string` | Yes | — | The contact whose tags you are working with. |
| Tag IDs | `string` | Yes | — | Comma-separated tag IDs to apply. Use GET /tags to list available tags. |

#### Contact Tag: Delete

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Contact ID | `string` | Yes | — | The contact whose tags you are working with. |
| Tag IDs | `string` | Yes | — | Comma-separated tag IDs to remove. |

#### Contact Tag: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Contact ID | `string` | Yes | — | The contact whose tags you are working with. |
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |

#### Ecommerce Order: Create

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Contact ID | `string` | Yes | — | ID of the contact the order belongs to. |
| Order Date | `string` | Yes | — | ISO 8601 date string |
| Order Title | `string` | Yes | — | Title of the order. |
| Order Type | `options` | Yes | `offline` | Whether the order was placed online or offline. |
| | | | | Options: `offline`, `online` |
| Additional Fields | `collection` | No | `{}` | Optional fields sent with the new order. |
| — Lead Affiliate ID | `number` | No | `0` | ID of the affiliate credited with the lead. |
| — Promo Codes | `string` | No | — | Comma-separated promo codes |
| — Sales Affiliate ID | `number` | No | `0` | ID of the affiliate credited with the sale. |
| Shipping Address | `json` | No | `{}` | Shipping address as JSON: { "company": "Acme", "country_code": "US", "first_name": "John", "last_name": "Doe", "line1": "123 Main St", "locality": "City", "region": "CA", "zip_code": "90210" } |
| Order Items | `json` | No | `[]` | Order items as JSON array: [{ "description": "Widget", "price": 10.00, "product_id": 1, "quantity": 2 }] |

#### Ecommerce Order: Delete

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Order ID | `string` | Yes | — | ID of the order to delete. |

#### Ecommerce Order: Get

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Order ID | `string` | Yes | — | ID of the order to retrieve. |

#### Ecommerce Order: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |
| Options | `collection` | No | `{}` | Filters applied to the search. |
| — Since | `string` | No | — | Only return orders from this date onwards. |
| — Until | `string` | No | — | Only return orders up to this date. |
| — Paid | `boolean` | No | `false` | Only return paid orders. |
| — Order | `string` | No | — | Which field the results are sorted by. |
| — Contact ID | `number` | No | `0` | Only return orders belonging to this contact. |
| — Product ID | `number` | No | `0` | Only return orders containing this product. |

#### Ecommerce Product: Create

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Product Name | `string` | Yes | — | Name of the product to create. |
| Additional Fields | `collection` | No | `{}` | Optional fields sent with the new product. |
| — Active | `boolean` | No | `false` | Whether the product is active. |
| — Product Description | `string` | No | — | Long description of the product. |
| — Product Price | `number` | No | `0` | Price of the product. |
| — Product Short Description | `string` | No | — | Short description of the product. |
| — SKU | `string` | No | — | Stock-keeping unit code. |
| — Subscription Only | `boolean` | No | `false` | Whether the product is sold only as a subscription. |

#### Ecommerce Product: Delete

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Product ID | `string` | Yes | — | ID of the product to delete. |

#### Ecommerce Product: Get

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Product ID | `string` | Yes | — | ID of the product to retrieve. |

#### Ecommerce Product: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |
| Filters | `collection` | No | `{}` | Narrow which products are returned. |
| — Active | `boolean` | No | `false` | Only return active products. |

#### Email: Create Record

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Sent To Address | `string` | Yes | — | Address the email was sent to. |
| Sent From Address | `string` | Yes | — | Address the email was sent from. |
| Additional Fields | `collection` | No | `{}` | Optional fields stored on the email record. |
| — Clicked Date | `string` | No | — | When a link in the email was clicked. |
| — Contact ID | `number` | No | `0` | Contact the email record belongs to. |
| — Headers | `string` | No | — | Raw email headers. |
| — HTML Content | `string` | No | — | Base64 encoded HTML |
| — Opened Date | `string` | No | — | When the email was opened. |
| — Original Provider | `options` | No | `UNKNOWN` | Which system originally sent the email. |
| | | | | Options: `UNKNOWN`, `INFUSIONSOFT`, `MICROSOFT`, `GOOGLE` |
| — Original Provider ID | `string` | No | — | The sending system's own identifier for the email. |
| — Plain Content | `string` | No | — | Base64 encoded text |
| — Provider Source ID | `string` | No | — | Source identifier supplied by the provider. |
| — Received Date | `string` | No | — | When the email was received. |
| — Sent Date | `string` | No | — | When the email was sent. |
| — Sent From Reply Address | `string` | No | — | Reply-to address used for the email. |
| — Sent To BCC Addresses | `string` | No | — | BCC recipients of the email. |
| — Sent To CC Addresses | `string` | No | — | CC recipients of the email. |
| — Subject | `string` | No | — | Subject line of the email. |

#### Email: Delete Record

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Email Record ID | `string` | Yes | — | ID of the email record to delete. |

#### Email: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |
| Filters | `collection` | No | `{}` | Narrow which sent emails are returned. |
| — Contact ID | `number` | No | `0` | Only return emails for this contact. |
| — Email | `string` | No | — | Only return emails for this address. |
| — Since Sent Date | `string` | No | — | Only return emails sent on or after this date. |
| — Until Sent Date | `string` | No | — | Only return emails sent on or before this date. |

#### Email: Send

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| User ID | `string` | Yes | — | The Keap user to send the email on behalf of. Use GET /users to list available users. |
| Contact IDs | `string` | No | — | Comma-separated contact IDs to receive the email. |
| Subject | `string` | No | — | Subject line of the email. |
| Additional Fields | `collection` | No | `{}` | Optional fields sent with the email. |
| — Address Field | `string` | No | — | Email field of each Contact record to address the email to (e.g. EmailAddress1) |
| — HTML Content | `string` | No | — | HTML content encoded in Base64 |
| — Plain Content | `string` | No | — | Plain text content encoded in Base64 |
| Manual Attachments | `json` | No | `[]` | Manual attachments as JSON array: [{ "file_data": "base64...", "file_name": "doc.pdf" }] |
| Binary Attachment Properties | `string` | No | — | Comma-separated binary property names from upstream items to attach to the email. |

#### File: Delete

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| File ID | `string` | Yes | — | ID of the file to delete. |

#### File: Get Many

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Return All | `boolean` | No | `false` | Whether to return all results or only up to a given limit |
| Limit | `number` | No | `100` | Max number of results to return _(shown when Return All is `false`)_ |
| Filters | `collection` | No | `{}` | Narrow which files are returned. |
| — Contact ID | `number` | No | `0` | Only return files attached to this contact. |
| — Name | `string` | No | — | Filter by name. Use * for LIKE queries. |
| — Permission | `options` | No | `both` | Whose files to return. |
| | | | | Options: `user`, `company`, `both` |
| — Type | `options` | No | — | Restrict results to one kind of stored file. |
| | | | | Options: `application`, `attachment`, `contact`, `digitalProduct` (Digital Product), `fax`, `funnel`, `hidden`, `image`, `import`, `logoThumnail` (Logo Thumbnail), `reSampledImage` (Re Sampled Image), `styleCart` (Style Cart), `templateThumnail` (Template Thumbnail), `ticket`, `webform` |
| — Viewable | `options` | No | `both` | Restrict results by visibility. |
| | | | | Options: `public`, `private`, `both` |

#### File: Upload

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Binary File | `boolean` | No | `false` | Whether to upload binary data from the upstream item |
| Binary Property | `string` | No | `data` | Name of the binary property on the upstream item containing the file to upload _(shown when Binary File is `true`)_ |
| File Association | `options` | Yes | `contact` | What the uploaded file is attached to. |
| | | | | Options: `company`, `contact`, `user` |
| Contact ID | `string` | Yes | — | ID of the contact to attach the file to. _(shown when File Association is `contact`)_ |
| File Name | `string` | Yes | — | Filename including extension (used when binaryData is false) _(shown when Binary File is `false`)_ |
| File Data | `string` | Yes | — | Base64-encoded file content (used when binaryData is false) _(shown when Binary File is `false`)_ |
| Is Public | `boolean` | No | `false` | Whether the uploaded file is publicly readable. |

#### All Operations

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Keap Account | `credential` | No | — | Connect your Keap account via OAuth2. |
| Max Concurrency | `number` | No | `10` | Maximum number of items to process concurrently. |

## Output Data

Keap's response is **merged onto the input item's JSON** in every operation — your existing fields survive and Keap's fields are added alongside them, overwriting any key of the same name. Binary data on the input item is forwarded to the output item, including on the file and email operations that read binary from it.

| Operation | Output items per input item |
|-----------|-----------------------------|
| `getAll` (every resource) | **One per record returned**, so a single input item fans out across the whole result set. |
| `create`, `upsert`, `get`, `update`, `createRecord`, `upload` | One — the record Keap returned, merged onto the item. |
| `delete`, `deleteRecord`, `send` | One — a fixed acknowledgement merged onto the item. |

Deletes (contact, note, tag removal, order, product, email record, file) and `Email: Send` do not return a record. They merge this onto the item instead:

```json
{
  "success": true
}
```

Three behaviours are worth planning for:

- **A `getAll` that matches nothing produces no output items at all.** The input item does not pass through — that branch of the workflow simply goes quiet. If a downstream node must run regardless, do not depend on this node emitting something.
- **`Return All` changes how much is fetched, not the shape.** On, Keap is paged through until every record is collected. Off, one page is fetched, capped at `Limit` — which accepts values from 1 to 200 and defaults to 100.
- **An operation that returns no content leaves the item untouched.** Nothing is merged, so the item arrives downstream carrying only the fields it came in with. Test for a field you expect rather than assuming the call wrote something.

The record fields themselves are whatever Keap returns, under Keap's own property names — the node does not rename, nest or filter them. Those names are snake_case, which is also the form the `Fields` options take (`id,company_name,email_address`). Reference them downstream by expression, e.g. `{{ $json.id }}`.

## Usage Examples

- Create a new contact in Keap
- Upload a file to a Keap contact record
- Send an email with binary attachments through Keap
- List all companies in Keap CRM
- Create an ecommerce order in Keap

## Example Configuration

Create a company with an address and a phone number:

```json
{
  "type": "keap",
  "parameters": {
    "resource": "company",
    "operation": "create",
    "companyName": "{{ $json.company }}",
    "additionalFields": {
      "emailAddress": "{{ $json.email }}",
      "notes": "New potential client",
      "website": "{{ $json.website }}"
    },
    "addressesUi": {
      "country_code": "US",
      "line1": "123 Business Ave",
      "locality": "New York",
      "region": "NY",
      "zip_code": "10001"
    },
    "phonesUi": {
      "type": "PHONE1",
      "number": "{{ $json.phone }}"
    }
  }
}
```

Create a contact, or update the existing one when the email already exists:

```json
{
  "type": "keap",
  "parameters": {
    "resource": "contact",
    "operation": "upsert",
    "duplicateOption": "email",
    "additionalFields": {
      "givenName": "{{ $json.firstName }}",
      "familyName": "{{ $json.lastName }}",
      "jobTitle": "{{ $json.title }}",
      "sourceType": "API",
      "optInReason": "Website contact form"
    },
    "emailsUi": [
      {
        "field": "EMAIL1",
        "email": "{{ $json.email }}"
      }
    ],
    "phonesUi": [
      {
        "field": "PHONE1",
        "number": "{{ $json.phone }}"
      }
    ]
  }
}
```

List recently updated contacts, newest first, one output item per contact:

```json
{
  "type": "keap",
  "parameters": {
    "resource": "contact",
    "operation": "getAll",
    "returnAll": false,
    "limit": 50,
    "options": {
      "order": "date",
      "orderDirection": "descending",
      "since": "2026-01-01T00:00:00Z"
    }
  }
}
```

Attach a file that arrived from an upstream node to a contact record:

```json
{
  "type": "keap",
  "parameters": {
    "resource": "file",
    "operation": "upload",
    "binaryData": true,
    "binaryPropertyName": "data",
    "fileAssociation": "contact",
    "contactId": "{{ $json.contactId }}",
    "isPublic": false
  }
}
```

Send an email to several contacts with an upstream file attached:

```json
{
  "type": "keap",
  "parameters": {
    "resource": "email",
    "operation": "send",
    "userId": "{{ $json.keapUserId }}",
    "contactIds": "{{ $json.contactIds }}",
    "subject": "Your invoice",
    "additionalFields": {
      "addressField": "EmailAddress1",
      "htmlContent": "{{ $json.htmlBase64 }}"
    },
    "attachmentsBinary": "data"
  }
}
```

### Error Handling

| Mode | Behavior |
|------|----------|
| **stop** | Halts workflow on first error |
| **continue** | Skips failed items, passes successful ones through |
| **errorPort** | Routes failed items to Error output port |

## Tips

Manage Keap CRM data including contacts, companies, tags, orders, emails with attachments, and file uploads.

### Common Patterns

**Pattern 1: Complete Contact Management Workflow**
1. **Create/Update Contact**: Use upsert operation with email duplicate checking
2. **Add Tags**: Use contactTag create to categorize contacts
3. **Add Notes**: Use contactNote create for activity tracking
4. **Upload Files**: Attach documents to contact records

**Pattern 2: E-commerce Integration**
1. **Create Products**: Set up product catalog
2. **Create Orders**: Process customer orders with line items
3. **Send Confirmation**: Use email send with order details
4. **Track Activity**: Create email records for sent communications

**Pattern 3: Company and Contact Relationship**
1. **Create Company**: Establish company record
2. **Create Contacts**: Link contacts to company using companyId
3. **Manage Communications**: Send emails and track interactions
4. **Document Management**: Upload and organize files by association

### Key Implementation Notes

1. **Collection Parameters**: Always use flat object structure
2. **JSON Fields**: Use proper JSON format for complex fields like addresses and emails
3. **ID References**: Many operations require existing IDs from previous operations
4. **Pagination**: Use `returnAll: false` with `limit` for large datasets
5. **Binary Data**: Choose between `binaryData: true` (from upstream) or `binaryData: false` (manual upload)
6. **Duplicate Handling**: Use `duplicateOption` for contact upsert operations to control matching logic