ackDB

Update contact properties (with transition detection)

Only provided fields are written. Transitions become EVENTS (events are truth, state is cache):

  • title changing from one non-null value to a different one ⇒ contact.role_changed { from_title, to_title }
  • status: "former" ⇒ the affiliation ENDED: endedAt is stamped (body value or now) and contact.ended { reason: "manual" } is emitted — the manual sibling of a source-asserted job change.

Reactivating a former edge is 422: an ended stint is history and is never resurrected. A returning employee gets a NEW stint row from the normal resolution path.

linkedinUrl is NOT a contact column — it is a durable key of the contact's PERSON. Setting it here writes person_identity_links (normalized to linkedin.com/in/<slug>), so the value is visible to identity resolution instead of being an inert string. It is therefore deployment-wide and first-writer-wins: a LinkedIn already owned by a DIFFERENT human is 409 + a merge_candidates row, never a silent re-point. Send null to unlink it. Every affiliation of the same human shows the same LinkedIn — that is the point.

email is a PAIRED write (#472): the edge's context column AND the person's email_address durable key, applied together so the human stays findable by email. First-writer-wins like linkedin: a value owned by a DIFFERENT person is 409 + merge candidate, and a value already used by another contact at this company is 409 (one address, one edge). Role/shared inboxes (info@, support@, …) write the column only — a mailbox is not a human. null clears the column; the person's keys are never auto-removed (that is DELETE /persons/{id}/links/...). A CHANGED email that contradicts the company's domain links emits contact.domain_mismatch and returns a warnings[] entry.

put/api/v1/ackdb/entities/{entityId}/contacts/{contactId}

Request

  • Base URL
    http://localhost:3009
  • Auth
    HTTP bearer

Path parameters

entityIdstring required
contactIdstring required

Contact id (ULID = contacts.contact_id) — the affiliation edge.

Request body

namestring
emailstring
phonestring
titlestring
departmentstring
linkedinUrlstring
avatarUrlstring nullable

Photo URL — written to the edge's PERSON (http(s) only). null clears it.

isExternalboolean
status'active' | 'former' | 'disabled'

former ends the affiliation (stamps endedAt + emits contact.ended).

endedAtstring date-time

When the affiliation ended. Only read when status becomes former; defaults to now.

Response

{ data: Contact, emittedEvents: string[], warnings?: string[] }.

dataunknown required

Changes

No recorded changes to this operation across all 1 revision of this API.