> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dualentry.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Zapier Integration: Setup, Authentication, and Actions

> Connect DualEntry to Zapier with an organization API key to create, update, search, and trigger DualEntry records from thousands of other apps.

DualEntry ships a [Zapier](https://zapier.com/) Platform application that calls the [Public API v2](../../developers/openapi/resources-v2.json) using an organization API key. Once installed, Zaps - Zapier's automation workflows - create, update, poll, and search records across DualEntry and thousands of other apps. The connector is a Node.js package under `core/integrations/connectors/zapier/`; every action, trigger, and search is generated from a single `lib/schemas.js` manifest, so changes to that manifest re-shape the published Zapier surface.

## Prerequisites

Confirm the following before building Zaps:

* A Zapier account and permission to install or use the DualEntry integration. Distribution is private invite or a Zapier App Directory listing, depending on how your organization releases the app.
* A DualEntry organization-scoped Public API key with the [authentication](../../developers/guides/authentication) scopes your Zaps need. Create the key under **Organization Management → your organization → API Keys**.
* Agreement on which environment Zaps should call. The integration defaults `BASE_URL` to `https://api.dualentry.com` when unset; private deployments override `BASE_URL` on the Zapier app configuration.
* For polling triggers, tolerance for latency. Triggers send `GET` calls on Zapier's schedule rather than pushing real-time webhooks from DualEntry. See [Rate limiting](../../developers/guides/rate-limiting) for the limits Zapier polling has to respect.

## Install the DualEntry app and authenticate

The DualEntry Zapier app uses custom authentication: every request adds the `X-Api-Key` header with your Public API key, and Zapier validates the key against the API health endpoint before completing the connection.

1. Install the DualEntry Zapier app the way your organization provides it - directory install or invite link from whoever maintains the Zapier project.
2. When Zapier prompts for authentication, paste your Public API key.
3. Zapier runs the connection test against `GET {BASE_URL}/public/v2/health/`. A `200` response completes the connection.

If the test fails, confirm the key is valid in DualEntry, the base URL matches your environment, and outbound HTTPS from Zapier to your DualEntry host is allowed. The auth handler lives in `lib/auth.js`; the `X-Api-Key` injection happens in the connector's `beforeRequest` hook in `index.js`.

## How Zaps call DualEntry

Each Zapier action, trigger, and search corresponds to a specific call pattern on DualEntry's Public API. Knowing the pattern up front makes errors much easier to diagnose later.

| Mechanism          | Behavior                                                                                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API prefix         | All calls target `{BASE_URL}/public/v2/` (`DUALENTRY_VERSION` is `v2` in `lib/constants.js`).                                                                                                                                   |
| Creates            | `POST` to `/public/v2/<slug>/` (for example `invoices`, `bills`) with a JSON body built from the Zap's input fields.                                                                                                            |
| Updates            | `GET` the existing record, strip read-only fields (amounts, approval metadata, resolved names), merge Zap input overrides, then `PUT` the full payload. The merge is implemented in `lib/factories.js`'s `stripReadOnlyFields`. |
| Triggers (polling) | Repeated `GET` list calls; Zapier deduplicates using the platform's normal polling rules. See [Pagination](../../developers/guides/pagination) for how list endpoints page.                                                     |
| Searches           | `GET` with a narrow query (the factory uses `limit=1`-style lookup to return a single match).                                                                                                                                   |
| Errors             | `401` triggers Zapier's reauth flow; `403` is forbidden; `404` is not found; `422` surfaces the API's validation `detail` when present. See [Errors](../../developers/guides/errors) for the canonical model.                   |

<Info>
  Draft creates for transactional records (such as invoices and bills) support a `record_status` field with dynamic fields. When the user selects **Draft**, several fields become optional - Zapier rebuilds the form so you aren't forced to supply values the product doesn't require for drafts. The list of optional fields is configured per resource in `draftOptionalFields` in `schemas.js`.
</Info>

## Use dynamic dropdowns for IDs

Six hidden dropdown triggers fetch up to 1,000 rows each (paginated) to power `dynamic` field references such as `customerList.id.name`. Use these in Zap steps instead of hard-coding numeric IDs whenever possible so Zaps survive data changes - when a customer is renamed in DualEntry, the dropdown reflects it on the next refresh, but a hard-coded ID becomes a stale reference the next time the Zap runs.

| Trigger key          | Public API slug   | Company-scoped |
| -------------------- | ----------------- | -------------- |
| `companyList`        | `companies`       | No             |
| `customerList`       | `customers`       | Yes            |
| `vendorList`         | `vendors`         | No             |
| `accountList`        | `accounts`        | Yes            |
| `itemList`           | `items`           | No             |
| `classificationList` | `classifications` | Yes            |

Company-scoped dropdowns require the user to choose a company in the Zap step before the dropdown loads - they only show records that belong to the selected company.

## Published actions

The connector currently publishes 39 actions across 19 resources. Each resource's `published` object in `lib/schemas.js` toggles create, update, trigger, and search independently, so the published surface can change between releases.

| Resource (examples)                                                                                                                                              | Create         | Update | Polling trigger | Search                |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------ | --------------- | --------------------- |
| Invoice, Bill, Customer, Vendor, Sales Order, Customer Payment                                                                                                   | Yes            | Yes    | Yes             | Yes                   |
| Journal Entry, Direct Expense                                                                                                                                    | Yes            | -      | -               | Yes                   |
| Item, Purchase Order, Vendor Payment, Cash Sale, Customer Credit, Vendor Credit, Bank Transfer, Customer Deposit, Customer Refund, Vendor Refund, Classification | - (or partial) | -      | -               | Yes (where indicated) |

Capabilities that exist in `schemas.js` with `published: false` are not visible in Zapier and require flipping the flag and redeploying the app (`npm run deploy-zapier` in the connector package). For the canonical action matrix, see the connector's `README.md`.

## Operate safely in production

A few production hygiene rules cover most of the gotchas Zapier publishing rules and DualEntry's API model enforce.

1. Build and validate Zaps against a non-production DualEntry organization first. Map every required field, run end-to-end tests, then point at production.
2. Avoid consecutive search steps, and never end a Zap with only a search - Zapier's publishing rules reject those patterns.
3. Map `422` errors back to missing required fields or invalid enums. The middleware formats the API's `detail` arrays into a single message when possible.
4. For updates, remember the GET-merge-PUT pattern - omitted fields keep their existing API values, and supplied fields override after the merge. Supply complete arrays when changing line items, since `PUT` replaces the array wholesale rather than patching individual lines.

## Troubleshoot common failures

| Symptom                     | Cause                                                            | Resolution                                                                                                                                                |
| --------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Connection test fails       | Wrong API key, wrong `BASE_URL`, or the health route is blocked. | Regenerate the Public API key; set `BASE_URL` for private stacks; verify `GET /public/v2/health/` returns `200` from a browser or curl with the same key. |
| `401` / "Invalid API key"   | Key revoked or typo.                                             | Create a new key under Organization Management and update the Zapier connection.                                                                          |
| `403` on an action          | The key lacks permission for that record type or company.        | Use a key whose scopes match the Zap's operations, or adjust DualEntry role permissions.                                                                  |
| `422` validation errors     | Required fields missing, bad enums, or draft vs. posted rules.   | Use draft mode where applicable; populate dynamic dropdowns; read the error text for `loc` and `msg` fragments from the API response.                     |
| Update overwrote line items | `PUT` replaced arrays after the merge.                           | Supply complete line arrays when changing lines, or use create-only Zaps for additive workflows.                                                          |
| Dropdown empty              | No rows in DualEntry, or the company filter excludes them.       | Create master data first; pick the correct company for company-scoped lists (`customerList`, `accountList`, `classificationList`).                        |

## FAQ

The FAQ covers questions that come up most often during Zap design and connector maintenance. Maintainer-specific answers note the relevant package files.

### Does the Zapier app use OAuth or the same session as the DualEntry UI?

No. It uses a static API key on every call (`X-Api-Key`). OAuth for end users is a separate Public API surface (`/public/v2/oauth/...` in the Django app) and is not what this Zapier package implements.

### Where is the source of truth for field labels and required flags?

`lib/schemas.js` in the Zapier connector package. Operators rarely edit it; engineering or solutions engineering changes it and runs `npm test` and `npm run validate` (or `npm run verify-zapier` before merge).

### How do we add a new record type to Zapier?

Following the maintainer workflow: extend `scripts/zapier-config.json`, run `node scripts/generate-zapier-schemas.js`, merge the generated block into `lib/schemas.js`, hand-tune fields and `published` flags, then test and `zapier push` per `README.md`.

## Result

After connecting with a valid Public API key, Zaps call `/public/v2/` endpoints to create, update, poll, and search the published DualEntry resources, with dropdown-backed IDs and structured validation errors. To compare other integrations, return to [Integrations](./index). For Zapier platform concepts, see the [Zapier Platform documentation](https://docs.zapier.com/platform/platform-cli/docs). For connector maintenance detail, read the package `README.md` in `core/integrations/connectors/zapier/`.
