Zapier Integration API

Last updated: July 11, 2026

This page documents the public API surface that powers the HelmIQ integration on Zapier: how a user connects their account, every trigger, action, and search the integration exposes, and how the API reports errors and rate limits. It is the reference for the published Zapier Platform app and for anyone wiring HelmIQ into a custom Zap.

Overview

HelmIQ is a CRM built for advisory and dealmaking teams. The Zapier integration lets any of the 9,000+ apps on Zapier read from and write to a connected HelmIQ organization: create and edit contacts, companies, deals, tasks, and notes; look up existing records; and fire Zaps when records change. Every request acts inside the connecting user's active organization and is strictly scoped to that organization on the server, so one connection can never see or touch another firm's data.

All endpoints live under https://helmiq.net/api/integrations/zapier. Requests carry a Bearer access token obtained through OAuth (below). Triggers are GET; actions and searches are POST with a JSON body. Responses are JSON.

Authentication

The integration authenticates with OAuth 2.0 authorization code + PKCE (S256)against HelmIQ's OAuth 2.1 server. It is a public client with no client secret: HelmIQ advertises token_endpoint_auth_methods_supported: ["none"], so PKCE is the proof-of-possession instead of a secret.

Connecting an account

  1. The user clicks Connect in Zapier. Zapier redirects the browser to the authorize endpoint with a generated code_challenge and code_challenge_method=S256.
  2. The user signs in to HelmIQ and approves the connection. HelmIQ redirects back to Zapier with an authorization code.
  3. Zapier exchanges the code (plus the PKCE code_verifier) for an access token and refresh token at the token endpoint.
  4. Zapier calls the connection-test endpoint to label the connection, for example Salt Creek Advisory (jack@firm.com).

Endpoints

OAuth endpoints
FieldTypeNotes
GET /api/oauth/authorizeauthorizeParams: response_type=code, client_id, redirect_uri, state, scope, code_challenge, code_challenge_method=S256.
POST /api/oauth/tokentokengrant_type=authorization_code with code + code_verifier + client_id + redirect_uri. Returns access_token, token_type, expires_in, refresh_token, scope.
POST /api/oauth/tokenrefreshgrant_type=refresh_token with refresh_token + client_id. Returns a rotated access + refresh pair (refresh tokens are single-use).
GET /api/integrations/zapier/meconnection testReturns { ok, orgId, orgName, userId, userEmail }. Not gated on feature flags, so a firm can connect before enabling actions/triggers.

The granted scope is mcp:read mcp:write. Every authenticated request sends Authorization: Bearer <access_token>. Access tokens are short-lived (about one hour); Zapier refreshes them automatically on a 401.

Triggers

Triggers are polling. Each is a GET that returns records changed (or created) since an opaque cursor. The response shape is { ok, items: [...], cursor }. Zapier passes the returned cursor back on the next poll via ?cursor=<opaque>; omit it on the first poll. The cursor encodes a stable (timestamp, id) tuple so rows that share a millisecond are never skipped. Pages hold up to 50 items. Triggers require the firm's zapierTriggersEnabled flag. Soft-deleted records are never returned.

Contact Updated

GET https://helmiq.net/api/integrations/zapier/triggers/contact-updated

Fires for each contact whose updatedAt advanced since the cursor. Cursor key: (updatedAt, id) ascending.

Contact Updated output
FieldTypeNotes
idstringContact id (primary key).
firstNamestring | nullGiven name.
lastNamestring | nullFamily name.
emailstring | nullPrimary email, lowercased on write.
titlestring | nullJob title.
companyIdstring | nullLinked company id, when known.
createdAtstring (ISO 8601)Creation timestamp.
updatedAtstring (ISO 8601)Last-modified timestamp.

Deal Updated

GET https://helmiq.net/api/integrations/zapier/triggers/deal-updated

Fires for each deal whose updatedAt advanced since the cursor. Cursor key: (updatedAt, id) ascending.

Deal Updated output
FieldTypeNotes
idstringDeal id (primary key).
namestringDeal name.
stagestringPipeline stage (e.g. prospect).
dealTypestringDeal type (e.g. ma_sellside).
valuenumber | nullDeal value.
companyIdstring | nullLinked company id.
createdAtstring (ISO 8601)Creation timestamp.
updatedAtstring (ISO 8601)Last-modified timestamp.

New Deal

GET https://helmiq.net/api/integrations/zapier/triggers/new-deal

Fires for each newly created deal. Cursor key: (createdAt, id) ascending. Supports an optional ?format=arraymode that returns a bare array of the newest deals (no cursor) for Zapier's generic "Retrieve Poll" webhook, which dedups by each item's id.

New Deal output
FieldTypeNotes
idstringDeal id (primary key).
namestringDeal name.
stagestringPipeline stage (e.g. prospect).
dealTypestringDeal type (e.g. ma_sellside).
valuenumber | nullDeal value.
companyIdstring | nullLinked company id.
createdAtstring (ISO 8601)Creation timestamp.
updatedAtstring (ISO 8601)Last-modified timestamp.

New Reminder

GET https://helmiq.net/api/integrations/zapier/triggers/new-reminder

Fires for each newly created reminder (task). Cursor key: (createdAt, id) ascending.

New Reminder output
FieldTypeNotes
idstringTask (reminder) id.
titlestringReminder title.
descriptionstring | nullReminder body.
dueDatestring (ISO 8601) | nullDue date, when set.
statusstringStatus (e.g. pending).
contactIdstring | nullLinked contact id.
dealIdstring | nullLinked deal id.
createdAtstring (ISO 8601)Creation timestamp.
updatedAtstring (ISO 8601)Last-modified timestamp.

Actions

Actions are POSTwith a JSON body. The organization and author are always taken from the access token, never from the body, so a Zap can only write into the connected organization. Actions require the firm's zapierActionsEnabled flag. Every action is idempotent: a Zap that fires twice with the same input returns the existing record rather than stacking a duplicate. Success responses include { ok: true, ... , created: boolean }, where created=false means an existing record was returned.

Create Contact

POST https://helmiq.net/api/integrations/zapier/actions/create-contact

Create Contact inputs
FieldTypeNotes
emailstring (required)Must be a valid email.
firstNamestringOptional.
lastNamestringOptional.
titlestringOptional job title.
phonestringOptional.
companyNameCandidatestringOptional company name to link or create.
linkedinUrlstringOptional.

Idempotency: goes through the canonical contact-creation path. If a contact with the same email already exists in the organization, that contact is returned with created: false. Returns { ok, id, contactId, companyId, created }. A rejected payload (for example a role-account address that fails the shape gate) returns 422 with a reason.

Create Company

POST https://helmiq.net/api/integrations/zapier/actions/create-company

Create Company inputs
FieldTypeNotes
namestring (required)Cleaned (LLC/Inc. suffixes stripped) before dedup and write.
domainstringOptional.
websitestringOptional.

Idempotency: dedup key is (organizationId, cleaned name), case-insensitive. An existing company is returned with created: false. Returns { ok, id, companyId, created }.

Create Deal

POST https://helmiq.net/api/integrations/zapier/actions/create-deal

Create Deal inputs
FieldTypeNotes
namestring (required)Deal name.
stagestringDefaults to prospect.
dealTypestringDefaults to ma_sellside.
valuenumberOptional deal value.
ebitdanumberOptional.
companyIdstringOptional; verified to belong to the org (404 if not).
externalSourcestringUpstream system name; part of the dedup key.
externalIdstringUpstream record id; part of the dedup key.
descriptionstringOptional.

Idempotency: when both externalSource and externalId are supplied, the dedup key is (organizationId, externalSource, externalId); a resync of the same upstream record returns the existing deal with created: false (a concurrent duplicate is caught by a unique index and resolved the same way). Returns { ok, id, dealId, created }.

Create Task

POST https://helmiq.net/api/integrations/zapier/actions/create-task

Create Task inputs
FieldTypeNotes
titlestring (required)Task title.
descriptionstringOptional.
contactIdstringOptional; verified org-scoped (404 if not).
dealIdstringOptional; verified org-scoped (404 if not).
companyIdstringOptional; verified org-scoped (404 if not).
dueDatestring (date)Optional.

Idempotency: when a contactId is present, the dedup key is (organizationId, contactId, title, status=pending), case-insensitive; a matching open task is returned with created: false. Tasks with no contact are never deduped (two distinct standalone reminders sharing a title must both persist). Returns { ok, id, taskId, created }.

Add Note

POST https://helmiq.net/api/integrations/zapier/actions/add-note

Add Note inputs
FieldTypeNotes
contactIdstringProvide exactly one of contactId or contactEmail.
contactEmailstringResolved org-scoped, case-insensitive.
contentstring (required)The note body.
dealIdstringOptional; verified org-scoped.
meetingTitlestringOptional.
meetingDatestring (ISO 8601)Optional; parsed null-safe.
transcriptstringOptional full transcript.
externalDedupKeystringOptional caller idempotency key; derived from contact + date + content when absent.

Idempotency: dedup key is (organizationId, source=zapier, externalDedupKey). A re-delivery returns the existing note with created: false, duplicate: true and a 200 (so a re-fired Zap shows green). An unmatched contact or deal returns 404. Returns { ok, noteId, contactId, created, duplicate }.

Edit Contact

POST https://helmiq.net/api/integrations/zapier/actions/edit-contact

Edit Contact inputs
FieldTypeNotes
contactIdstring (required)Must belong to the connected org (404 if not).
firstNamestringOptional; only supplied fields are updated.
lastNamestringOptional.
titlestringOptional.
phonestringOptional; normalized to the org phone region.
emailstringOptional; lowercased on write.
linkedinUrlstringOptional.

Partial update (PATCH semantics): only the fields present in the body are touched. Changing the email to one another contact in the org already holds returns 409 duplicate_email with the colliding existingContactId. Returns { ok, contactId }.

Edit Deal

POST https://helmiq.net/api/integrations/zapier/actions/edit-deal

Edit Deal inputs
FieldTypeNotes
dealIdstring (required)Must belong to the connected org (404 if not).
namestringOptional; only supplied fields are updated.
stagestringOptional; a real change resets the days-in-stage clock.
valuenumberOptional.
ebitdanumberOptional.
descriptionstringOptional.
expectedCloseDatestring (date)Optional; an invalid date returns 400, empty string clears it.

Partial update: only supplied fields are touched. A deal in another org or a deleted deal returns 404. Returns { ok, dealId }.

Searches

Searches power Zapier's find-or-create pattern. Each is a POST that returns a bare JSON array of matches (up to 5), org-scoped. A search with no match returns 200 with an empty array [], which is what makes Zapier fall through to the paired Create action. A search with no usable input also returns 200 [] (it never errors), while a malformed payload returns 400. Searches require the zapierActionsEnabled flag.

Find Contact

POST https://helmiq.net/api/integrations/zapier/searches/find-contact

Find Contact inputs
FieldTypeNotes
emailstring (required)Matched case-insensitively, org-scoped.

Output: array of contacts with id, email, firstName, lastName, title, phone, companyId, linkedinUrl. Pairs with Create Contact.

Find Company

POST https://helmiq.net/api/integrations/zapier/searches/find-company

Find Company inputs
FieldTypeNotes
domainstringHigh-precision exact match (case-insensitive), preferred.
namestringContains-match fallback used only when no domain is supplied.

Output: array of companies with id, name, domain, website. Pairs with Create Company.

Find Deal

POST https://helmiq.net/api/integrations/zapier/searches/find-deal

Find Deal inputs
FieldTypeNotes
externalIdstringHigh-precision exact match, preferred.
namestringContains-match against deal name and codename, fallback when no externalId.

Output: array of deals with id, name, codeName, stage, value. Pairs with Create Deal.

Error handling

HTTP status codes
FieldTypeNotes
200successAction/trigger/search succeeded. For idempotent re-runs, the body carries created: false (and duplicate: true for notes).
400invalid_payloadThe JSON body failed validation. The body includes an issues array describing each problem.
401unauthorizedMissing, malformed, or expired Bearer token. Zapier refreshes the access token and retries automatically.
403flag_disabledThe connected org has not enabled this surface (zapierActionsEnabled / zapierTriggersEnabled). An admin enables it in HelmIQ settings.
404not_foundA referenced record (contact, company, deal) does not exist in the connected org. The body names which reference failed.
409duplicate_emailEdit Contact only: the new email is already used by another contact in the org; existingContactId points at it.
422rejectedCreate Contact only: the payload passed schema validation but failed a business-rule gate; reason names the rejection.
429rate_limitedToo many requests. See rate limits below. The response carries a Retry-After header.
500server errorAn unexpected write failure. Safe to retry; creates are idempotent.
503disabledThe platform Zapier killswitch is engaged. Transient; retry later.

Error bodies are JSON of the form { ok: false, error: "<code>", ... }. On a 401, Zapier transparently exchanges the refresh token for a new access token (refresh tokens are single-use and rotate on each refresh) and replays the request.

Rate limits

Limits are enforced per organization, per operation. Write actions are limited to 120 requests per minute (about 2 per second sustained). Read operations (triggers and searches) are limited to 600 requests per minute (about 10 per second sustained). Exceeding a limit returns 429 with a Retry-After header (seconds) plus X-RateLimit-Limit and X-RateLimit-Reset. Honor Retry-After before retrying.

Data isolation

Every request is scoped to the connected user's active organization. The organization and acting user are resolved from the access token on the server; they are never read from a request body or query parameter. A connection can never read or write another organization's contacts, companies, deals, tasks, or notes.

Contact

Questions about the integration: jack@helmiq.net.