Contacts, parent companies and your own fields
The people at customers and vendors are contacts of their own: each with a first and last name, a role, e-mail, phone and mobile, the language to write to them in and whether they want sales e-mails. A customer or vendor may belong to a parent company, and an organization adds fields of its own to its companies, contacts and deals. Imported records keep the id they had in the system they came from. The design is ADR-0085. Nothing here posts, and none of it is a key detail a second person approves: these records describe who is who, they never decide an amount, a tax or who is paid.
Contacts
GET /v1/contacts ?entity&party_id&q&email&erased
POST /v1/contacts {entity, party, kind?, first_name?, last_name, role?, email?, phone?, mobile?, language?, email_opt_out?, custom?}
GET /v1/contacts/{contact_id}
PATCH /v1/contacts/{contact_id} what changes ("" clears a field but the last name)
POST /v1/contacts/{contact_id}/erase
A contact is a person at one customer or vendor of one company (party, by code or id; kind
when a code names both). It never moves to another party. The card of a customer or vendor lists
its contacts (contacts), and the first ones can come with the party when it is created
(contacts of POST /v1/entities/{entity}/parties, a name there is split: the last word is the
last name). The list of a party is no longer changed by PATCH of the party (422): each contact is
added, changed and erased by itself.
Invoices go by default to the contacts whose role says billing or accounting (Buchhaltung,
comptabilité, contabilità …), after the e-invoice address and before the customer's e-mail
address; every contact's address is one a document may be sent to. email_opt_out is for sales
and marketing e-mails; invoices and reminders still go.
Erasing (POST …/erase) empties the name, role, e-mail, phones and own fields for good: the
person asked to be forgotten, or the relationship ended. The row stays, so that the deals and
activities that named it still say that someone was there (its name reads ""). Only a person
erases (an agent: 403 contact_erase_agent), and an erased contact never changes again. The audit
trail of a contact records which fields changed, never their values, so that erasing a contact
erases it everywhere. The database keeps it too: a contact is never deleted, never moved to another
party, and once erased stays erased (migration 0125).
Deals and activities name a contact
A deal with a customer names its contact person among the customer's contacts (contact_id of
POST /v1/opportunities and PATCH; "" clears it); its contact_name, contact_email and
contact_phone are then the contact's, and the deal keeps no copy of them. A deal with a prospect
still names the person on the deal (contact_name …). A new customer on the deal drops a contact
that is not one of its people. A won deal still takes a contact.
An activity names the person it was with (contact_id of POST /v1/activities); one of a deal
takes the deal's contact by default. GET /v1/activities?contact_id= is a person's timeline.
Parent company
parent (code or id) of a customer or vendor names the company it belongs to: a party of the same
company and kind, never itself or one of its own subsidiaries (a loop; the database refuses it too).
The card shows the parent and the subsidiaries; GET /v1/entities/{entity}/parties?parent= lists
a company's subsidiaries. "" clears it.
Your own fields
GET /v1/custom-fields ?record&retired
POST /v1/custom-fields {record, key, label, type, choices?, position?} (admin)
PATCH /v1/custom-fields/{field_id} {label?, choices?, position?, retired?} (admin)
A field is for company (customers and vendors), contact or deal records, with a key the API
uses and a label the screens show, of a type: text (up to 1,000 characters), number (kept
exactly, as its text: "25.5"), date (2026-10-23), yes_no (true or false) or choice (one of
its choices, kept in the choice's own spelling). The key and type never change. Up to 25 fields are
in use per kind of record. On a new field, Suggest with AI proposes the type and, for a choice, the
choices that fit its label; nothing is saved until you save.
A record's values are in its custom, by key: custom of a party, a contact or a deal, on create
and change. A change sends only the keys that change; null removes a value. An unknown key or a
value of the wrong type is refused (422 custom_field_unknown, custom_field_value).
A field is retired, never deleted (PATCH {retired: true}): it leaves the forms and takes no
new values; the values records hold stay readable. A choice a record holds stays in the list (409
custom_field_choice_in_use).
Merging duplicates
A customer entered twice is merged into the one that stays
(POST /v1/entities/{entity}/parties/{party}/merge with {"into": "C-CH"}, on the card Merge
into…), but only while it has no business records of its own: no document, payment, order,
subscription, price list, mandate or other record of the books names it, no won deal and no request
waiting for approval (409 party_merge_has_records). Its contacts, deals, activities and
subsidiaries move, its own field values fill the ones the customer that stays has none of, and it
stays as an inactive customer with merged_into on its card, for good. Vendors are not merged.
A contact entered twice is merged into another contact of the same customer or vendor
(POST /v1/contacts/{contact_id}/merge with {"into": "<id>"}): the one that stays takes the
details it has none of, its deals and activities move, and the duplicate is erased with
merged_into_id. Only a person merges (403 merge_agent);
ADR-0089 has the rules.
Imported records
A record imported from another system (a Salesforce export) keeps its id there in the import's own
links (external_record, ADR-0084),
so that an import run again finds the record it made.
Test copies and exports
A masked test copy replaces each contact's name, e-mail and phones with mocks and masks the text of
its own fields. The organization's export has the contacts (crm_contact) and the fields
(custom_field) with what each column means.
Who does what
| Who | What |
|---|---|
Anyone with read |
read contacts and fields |
Anyone with draft (an agent too) |
add and change contacts, set parent companies, own field values |
A person with draft |
erase a contact, merge duplicates |
admin |
add, change and retire fields |