ACTION_ID: ackdb_company_relationship NAME: ackDB: Manage relationships CATEGORY: Integrations CREDITS: 0 Manage Company relationships and Contact reporting lines from ONE action. The first dropdown selects which target the row changes; do not look for a separate Contact relationship action. PREREQUISITE: ackDB tenant connection — the user must create it in the Floqer UI (Connections page: tenant Base URL + API key; Base URL is per-tenant, e.g. https://acme.api.ackdb.ai); an agent cannot. Without it the action fails with "No ackDB connection found". Confirm it exists before configuring. INDEX: 1. Choose the target and operation 2. Inputs 3. Outputs 4. How to configure 5. Key notes 6. Where it fits in a workflow 7. When to use / not use ============================================================================== 1. CHOOSE THE TARGET AND OPERATION ============================================================================== relationship_target (target dropdown, required) - "company" Company relationship (DEFAULT for saved-workflow compatibility) - "contact" Contact reporting line These are different grains: COMPANY A relationship between two ackDB company entities, such as a subsidiary and parent. Endpoints may be exact entity IDs, domains, or full company URLs. Company also supports merging duplicate entities. CONTACT A reporting line between two contacts AT THE SAME COMPANY. Each endpoint must be the exact 26-character Contact affiliation ULID. A Person ID names the human across companies and is NOT accepted here. operation (target-dependent dropdown, required) relationship_target = "company" - "create" Assert a typed child-to-parent Company relationship. - "end" Record when a relationship that was true stopped. - "retract" Mark the original assertion as false. Requires a reason. - "merge" Irreversibly fold a duplicate into the Company that survives. relationship_target = "contact" - "contact_create" Create a reporting-line episode. - "contact_end" End a reporting-line episode. - "contact_retract" Mark a reporting-line assertion as false. The `contact_` prefix is part of the configured operation value. The output normalises it back to create, end, or retract. Only configure inputs for the chosen branch; the builder hides the other branch's fields. ============================================================================== 2. INPUTS ============================================================================== Public API callers use the snake-cased DISPLAY NAMES below. The merge keys are `primary_company_survives` and `secondary_company_merged_and_closed`, not the internal template IDs. --- Company create (operation = "create") --- child_company (string, required) Exact ackDB entity ULID, bare domain, or full company URL for the child. An unknown valid domain may create a graph-only Company. child_company_name (string, optional) Display name used only if ackDB creates a graph-only child Company. parent_company (string, required) Exact ackDB entity ULID, bare domain, or full company URL for the parent. An unknown valid domain may create a graph-only Company. parent_company_name (string, optional) Display name used only if ackDB creates a graph-only parent Company. --- Contact create (operation = "contact_create") --- report_contact_id (string, required for this branch) Exact Contact affiliation ULID for the direct report. Do not pass a Person ID, name, email, domain, or LinkedIn URL. manager_contact_id (string, required for this branch) Exact Contact affiliation ULID for the manager. The manager and report must be different contacts at the same company. --- Shared create fields --- relationship_type (target-dependent dynamic dropdown, required) An enabled standard or custom type from the selected target's tenant registry. Company and Contact relationship types are separate. Disabled types cannot be used for a new assertion. effective_from (datetime, optional) ISO-8601 timestamp with an offset for when the relationship became true. It cannot be in the future. Blank uses one stable execution timestamp retained across retries. provenance_source (string, optional; default "floqer") Where the assertion came from. This is audit provenance, not an ackDB source-pack key. external_record_id (string, optional) Durable identifier for this assertion in the source system. evidence_url (string, optional) Valid HTTP(S) URL supporting the assertion. observed_at (datetime, optional) ISO-8601 timestamp with an offset for when the evidence was observed. It cannot be in the future. --- End / retract --- relationship_id (string, required) Exact ackDB relationship ULID returned by create or a relationship read. Used by Company end/retract and Contact contact_end/contact_retract. effective_to (datetime, optional; end only) ISO-8601 timestamp with an offset for when the relationship stopped being true. It must follow effective_from and cannot be in the future. Blank lets ackDB use its current time. reason (string) Optional audit note for end. REQUIRED for retract because retract says the original assertion was false; end says it was true and later stopped. --- Company merge (operation = "merge") --- primary_company_survives (string, required) Exact domain, full URL, or entity ULID for the Company that survives. secondary_company_merged_and_closed (string, required) Exact domain, full URL, or entity ULID for the duplicate folded into the primary and closed. This cannot be undone. ============================================================================== 3. OUTPUTS ============================================================================== OUTPUT NAMES ARE snake_case. The action emits one flat union; fields that do not apply to the selected branch are blank, false, or an empty JSON string. success (boolean) True for a completed mutation or successful idempotent no-op. operation (string) create, end, retract, or merge. Contact operations do not retain the `contact_` prefix here. relationship_id (string) Relationship episode ID. Blank for merge or failure before resolution. created (boolean) Create only. True for a new assertion; false for a successful exact retry. changed (boolean) End/retract only. True when lifecycle state changed; false for a successful repeated no-op. state (string) Relationship state: active, ended, or retracted. relationship_type (string) Relationship type key returned by ackDB. effective_from (string) Stored episode start timestamp. effective_to (string) Stored episode end timestamp. Blank while active or after retraction. --- Company relationship outputs --- child_entity_id (string) Canonical child Company entity ID. parent_entity_id (string) Canonical parent Company entity ID. --- Contact relationship outputs --- contact_company_entity_id (string) Company that owns both Contact affiliations. report_contact_id (string) Canonical report Contact ULID. Use this—not report_person_id—as a later reporting-line endpoint. report_person_id (string) Global human behind the report; informational only. manager_contact_id (string) Canonical manager Contact ULID. Use this—not manager_person_id—as a later reporting-line endpoint. manager_person_id (string) Global human behind the manager; informational only. --- Merge outputs --- merged (boolean) True when the secondary Company was folded into the primary. primary_found (boolean) Whether the surviving Company matched exactly. secondary_found (boolean) Whether the duplicate Company matched exactly. primary_entity_id (string) Surviving Company's canonical ackDB entity ID. secondary_entity_id (string) Merged-away Company's former ackDB entity ID. transferred (string) JSON counts for transferred data and relationship reconciliation, including relationshipsRepointed, relationshipDuplicatesConsolidated, and selfRelationshipsRetracted. --- Outcome diagnostics --- error_code (string) ackDB machine error code for a rejected operation. Blank on success. error (string) Human-readable failure. Blank on success. message (string) Row-friendly outcome summary. `transferred` is a JSON STRING. Parse it in a format_data_using_js_expression step before reading its counters. ============================================================================== 4. HOW TO CONFIGURE ============================================================================== For Public API configuration, resolve cascading options in this order: POST /api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/options/relationship_target body: {} POST .../options/operation body: { "context": { "relationship_target": "contact" } } POST .../options/relationship_type body: { "context": { "relationship_target": "contact" } } The last two calls must carry the target. Otherwise they default to Company and can return the wrong branch's choices. Type options return `{ value, label, extras }`; Company includes `extras.parent_to_child_label`, Contact includes `extras.manager_to_report_label`, and both include `extras.description`. Create a Company relationship: { "inputs": { "relationship_target": "company", "operation": "create", "child_company": "{{input.child_domain}}", "child_company_name": "{{input.child_name}}", "parent_company": "{{input.parent_domain}}", "parent_company_name": "{{input.parent_name}}", "relationship_type": "subsidiary_of", "effective_from": "{{input.effective_from}}", "provenance_source": "floqer" } } Create a Contact reporting line. Obtain both Contact IDs from ackDB Lookup or an earlier ackDB action; do not substitute Person IDs: { "inputs": { "relationship_target": "contact", "operation": "contact_create", "report_contact_id": "{{input.report_contact_id}}", "manager_contact_id": "{{input.manager_contact_id}}", "relationship_type": "reports_to", "effective_from": "{{input.effective_from}}", "provenance_source": "floqer" } } End a Contact reporting line: { "inputs": { "relationship_target": "contact", "operation": "contact_end", "relationship_id": "{{input.relationship_id}}", "effective_to": "{{input.effective_to}}", "reason": "Manager changed" } } Retract a false Company assertion: { "inputs": { "relationship_target": "company", "operation": "retract", "relationship_id": "{{input.relationship_id}}", "reason": "Source record was matched to the wrong company" } } Merge duplicate Companies — primary survives, secondary closes: { "inputs": { "relationship_target": "company", "operation": "merge", "primary_company_survives": "acme.com", "secondary_company_merged_and_closed": "acme-corp.com" } } Company create/end/retract also pass through directly for API integrations: POST /api/v1/ackdb/entity-relationships POST /api/v1/ackdb/entity-relationships/{relationshipId}/end POST /api/v1/ackdb/entity-relationships/{relationshipId}/retract Contact hierarchy READS pass through under `/api/v1/ackdb`. Contact create/end/retract do not; use this action's Contact branch. ============================================================================== 5. KEY NOTES ============================================================================== - ONE ACTION, TWO TARGETS. `ackdb_company_relationship` is the action ID for both Company and Contact relationships. Never add or search for an `ackdb_contact_relationship` action. - CONTACT MEANS AFFILIATION. Contact reporting endpoints require exact Contact ULIDs. A Person ID is useful for identity but cannot say which company stint participates in the reporting line. - SAME-COMPANY INVARIANT. ackDB rejects a Contact reporting line when the manager and report affiliations belong to different companies. - EPISODES, NOT OVERWRITES. Create starts a time-bounded assertion. End says it stopped being true; retract says it was false. Do not retract a normal manager change. - IDEMPOTENT LIFECYCLE. An exact repeated create returns created = false. Repeated end/retract returns changed = false. Both are successful. - TYPE REGISTRIES DO NOT MIX. Company and Contact relationship types come from different tenant registries. Resolve relationship_type after setting relationship_target. - COMPANY DOMAIN MATCHING IS EXACT. Floqer lowercases the hostname and strips scheme, path, query, fragment, credentials, and port from a full URL. It deliberately preserves `www.` because ackDB treats hostname labels as distinct identities. Company create does not use fuzzy search. - MERGE IS IRREVERSIBLE. Confirm the surviving and duplicate Companies with the user before configuring or running merge. Never infer direction from row order. Existing `ackdb_merge_record` workflows remain compatible, but new workflows should use Company + merge here. - EXPECTED BUSINESS REJECTIONS ARE DATA. Validation, not-found, conflict, and semantic rejections complete with success:false plus error_code, error, and message. Transport failures, rate limits, and server errors remain workflow errors after retry. - DIRECT PUBLIC ACKDB WRITES. Company relationship create/end/retract also exist on the generic ackDB passthrough. Contact relationship mutations do not: use this action's Contact branch so actor, retry, audit, and predefined outputs stay consistent. Relationship-type POST/PATCH remains in ackDB's data-model UI. ============================================================================== 6. WHERE IT FITS IN A WORKFLOW ============================================================================== Company graph: company source/enrichment -> resolve child + parent -> ackdb_company_relationship (company/create) -> gate on success Contact reporting line: people source -> ackdb_entity_lookup for each affiliation -> verify both Contact IDs and same Company -> ackdb_company_relationship (contact/contact_create) -> gate on success Lifecycle correction: hierarchy read / stored relationship_id -> decide end vs retract -> ackdb_company_relationship -> gate on success and changed ============================================================================== 7. WHEN TO USE / NOT USE ============================================================================== USE IT TO: - Record a parent/subsidiary or other enabled Company relationship. - Create or change an org-chart reporting line between exact Contacts. - End a relationship that used to be true. - Retract an assertion that was never true. - Merge a confirmed duplicate Company into its surviving record. DO NOT USE IT TO: - Set fields, owners, attributes, or identity links: use ackdb_update_record. - Write a timeline event: use ackdb_ingest. - Find Contact IDs from names, emails, LinkedIn URLs, or Person IDs: use ackdb_entity_lookup first. - Define/edit tenant-wide relationship types: use ackDB's data-model UI. - Read a hierarchy without mutating it: use the ackDB Public API reads. Last updated: 2026-08-25.