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.
Request
- Base URLhttp://localhost:3009
- AuthHTTP bearer
Path parameters
Contact id (ULID = contacts.contact_id) — the affiliation edge.
Request body
Response
{ data: Contact, emittedEvents: string[], warnings?: string[] }.
Changes
No recorded changes to this operation across all 1 revision of this API.