ACTION_ID: ackdb_update_record NAME: ackDB: Update company or contact CATEGORY: Integration CREDITS: 0 Set hand-owned static data on a single ackDB company or contact — optionally creating the record when it does not exist yet (upsert). ================================================================================ PREREQUISITE — USER ACTION REQUIRED ================================================================================ This action only works if the user has connected their ackDB tenant in Floqer. The connection cannot be made by an AI agent. The user must do this themselves before this action will run: 1. Sign in to Floqer at https://app.floqer.com 2. Open the Connections page from the sidebar 3. Click ackDB and enter the tenant Base URL and API key (Base URL is per-tenant, e.g. https://acme.api.ackdb.ai) If the user has not connected ackDB, this action fails with "No ackDB connection found for the user". Before configuring or running this action, confirm with the user that the ackDB connection exists. INDEX: 1. Inputs 2. Outputs 3. How to configure 4. Key notes 5. Where it fits in a workflow 6. When to use 7. When not to use ================================================================================ 1. INPUTS ================================================================================ ackdb_update_scope (dropdown, required) Scope. Picks the record type, which identifier field shows, and which fields the picker offers. Values: - { id: "company", name: "Company" } - { id: "contact", name: "Contact" } entity_ref (string, shown when scope = "company") Company (domain or entity ID). Its domain (e.g. acme.com) or its ackDB entity ID. Case-insensitive for domains. contact_ref (string, shown when scope = "contact") Contact (email or contact ID). Their email address or their ackDB contact ID. If an email matches several contacts the write FAILS and returns the ambiguous matches, so you can retry with a contact ID. create_if_missing (radio, optional) Create if missing (upsert). When the identifier matches nothing, create the record and then apply the picked fields to it. - Company: created from its DOMAIN. The identifier must be a valid domain (acme.com) — an entity ID that matches nothing cannot create. - Contact: created from its EMAIL, and requires parent_entity_ref. OFF by default: an unmatched record completes with found = false and writes nothing. parent_entity_ref (string, shown when scope = "contact") Parent company (domain or entity ID). Only used with create_if_missing on Contact scope — the company a newly created contact belongs to. Its domain (created too if it does not exist) or its entity ID. Ignored when the contact already exists. Contact creation is REJECTED without it. company_fields (dynamicJsonArray, shown when scope = "company") Fields to set. Pick each field and give it a value. Grouped into: - Attributes custom static fields (region, tier, …) - Core Fields name - Ownership account_owner — value is a member email or member ID - Identity Links external IDs (Stripe customer ID, Slack channel ID, …). The company's `domain` anchor is managed by ingest and is NOT settable here. Leave a value blank to CLEAR that field. contact_fields (dynamicJsonArray, shown when scope = "contact") Fields to set. Same shape. Grouped into: - Attributes custom static fields - Core Fields name, email, phone, title, department, LinkedIn - Ownership relationship_owner — member email or member ID - Identity Links external IDs Leave a value blank to CLEAR that field. ================================================================================ 2. OUTPUTS ================================================================================ ok (boolean) — Success. found (boolean) — Record Found. False when the identifier matched nothing and create_if_missing was off (nothing was written). created (boolean) — Record Created. True when the upsert minted the record. entityId (string) — Entity ID. contactId (string) — Contact ID. Contact scope only. applied (string) — Applied. JSON-stringified list of fields that were set. cleared (string) — Cleared. JSON-stringified list of fields blanked. skipped (string) — Skipped. JSON-stringified list of fields not written. errors (string) — Errors. JSON-stringified per-field errors. updatedAt (string) — Updated At. message (string) — Message. `applied`, `cleared`, `skipped` and `errors` are JSON STRINGS. Parse them in a format_data_using_js_expression step to read into them. ================================================================================ 3. HOW TO CONFIGURE ================================================================================ Resolve the field picker via Get Action Field Options before configuring: POST /api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/options/company_fields (body: {}) POST .../options/contact_fields (body: {}) Set static fields on a company, creating it if new: { "inputs": { "ackdb_update_scope": "company", "entity_ref": "{{input.domain}}", "create_if_missing": true, "company_fields": [ { "name": "region", "value": "{{input.region}}" }, { "name": "tier", "value": "Enterprise" }, { "name": "account_owner", "value": "jane@acme.com" } ] } } Update a contact by email, creating it under its parent company: { "inputs": { "ackdb_update_scope": "contact", "contact_ref": "{{input.email}}", "create_if_missing": true, "parent_entity_ref": "{{input.domain}}", "contact_fields": [ { "name": "title", "value": "{{input.job_title}}" }, { "name": "department", "value": "Engineering" } ] } } Clear a field — pass an empty value: { "name": "region", "value": "" } ================================================================================ 4. KEY NOTES ================================================================================ - STATIC DATA ONLY. This action sets properties of the RECORD. It does not record anything that happened — no timeline entry, no timestamp. Use ackdb_ingest for events. - BLANK VALUE = CLEAR. An empty value deliberately clears the field. Be careful mapping an optional column straight into a field: a row where that column is empty will WIPE the stored value rather than leave it alone. Gate with a filter or run_if when the column can be blank. - AMBIGUOUS EMAIL FAILS. A contact_ref email matching several contacts fails the write and returns the matches — it does not pick one. Resolve it first with ackdb_entity_lookup and pass the contactId instead. - WRITES ARE BEST-EFFORT PER CATEGORY. Attribute batches are all-or-nothing; everything else (core fields, ownership, identity links) applies independently. So a partial success is possible — the response reports exactly what was applied, cleared, skipped, and any per-field errors. Check `errors` rather than assuming `ok` means everything landed. - CREATE RULES ARE STRICT. With create_if_missing on: - a company is created from a DOMAIN — an entity ID that matches nothing is rejected, not created; - a contact needs BOTH an email identifier and parent_entity_ref. Without create_if_missing, an unmatched record is a no-op with found = false (not an error). - `domain` is not settable on a company — it is the anchor ingest resolves on, and is managed there. - Free — 0 credits. ================================================================================ 5. WHERE IT FITS IN A WORKFLOW ================================================================================ Backfill hand-owned fields onto companies from a spreadsheet: -> ackdb_update_record (company, create_if_missing ON) Enrich, then persist to ackDB: floqer_company_firmographics -> ackdb_update_record (company) Resolve then update, avoiding the ambiguous-email failure: ackdb_entity_lookup (contact) -> ackdb_update_record (contact_ref = "{{lookup.contactId}}") Events and properties together: ackdb_ingest (what happened) -> ackdb_update_record (who owns it) ================================================================================ 6. WHEN TO USE ================================================================================ Use ackdb_update_record to set data a HUMAN owns rather than data an event carries — region, tier, segment, account owner, custom attributes, external IDs — or to upsert a company/contact that should exist whether or not any event has referenced it yet. ================================================================================ 7. WHEN NOT TO USE ================================================================================ Recording something that happened (email, meeting, payment, signup) -> ackdb_ingest. Events are append-only and timestamped; this action cannot create one. (https://floqer.com/docs/action-detail/ackdb_ingest.txt) Reading a company or contact -> ackdb_entity_lookup (https://floqer.com/docs/action-detail/ackdb_entity_lookup.txt) Updating many records in one call -> Not supported. This action writes ONE record per row; fan out over rows instead. ================================================================================ This file is maintained manually. Last updated: 2026-07-13. Full interactive reference: https://floqer.com/docs/reference Action catalog: https://floqer.com/docs/action-catalog.txt