Flow · 13 steps
A person, a supplier, and a right-to-erasure request
The CRM is the one registry every picker reads: a purchase order names a supplier by id, an order names its customer by id, and neither holds a name of its own. This walk registers a person, registers a company with the supplier role and makes the person its main contact, shows the parties list a picker searches, edits both, archives and restores the company, and then honours an erasure request — the interesting part. A person's identifying details are sealed in the journal under a key held outside it; erasing destroys the key in the same transaction as the fact, so history is never rewritten, every document that named the psn_ id still resolves, and what those events said is gone.
Services involved
A filled dot is a read; a ring is a call that changes something.
- CRM Manager:
https://crm.getmanifold.io
Every call carries Authorization: Bearer $MANIFOLD_TOKEN unless its step says otherwise
(getting a token). Run a flow in your sandbox, not your live
organization: see Before you start.
1 Register a person
Email is required and unique per organization after normalisation. reference is the caller's key for the act — an import row, a checkout, a minted id — and a retry with it is acknowledged with the person already registered. The service seals the profile before the fact is written; the journal never sees the email.
POST /people CRM Manager
{
"reference": "crm-walk:ada",
"email": "ada@northwind.example",
"firstName": "Ada",
"lastName": "Lovelace",
"phone": "+61 400 000 001",
"roles": [
"customer"
],
"tags": [
"trade"
]
}Answers 201. What the answer holds: the operation's example.
From your answer, later steps send id as {personId}.
Records crm.person-created.
2 Read the person back
The profile is unsealed on read while the key exists: the name, email, phone and saved addresses come back in the clear, beside the settings the journal keeps openly (tags, roles, marketing).
GET /people/{personId} CRM Manager
What the answer holds: the operation's example.
3 Register a supplier
A company with the supplier role is what a purchase order's supplier picker offers. Locations carry a billing and a shipping address under a name each; a company is not a natural person, so its details stay in the clear.
POST /companies CRM Manager
{
"reference": "crm-walk:northwind",
"name": "Northwind Metalworks",
"roles": [
"supplier"
],
"paymentTermsDays": 30,
"locations": [
{
"name": "Works",
"shippingAddress": {
"address1": "1 Foundry Lane",
"city": "Geelong",
"countryCode": "AU"
}
}
]
}Answers 201. What the answer holds: the operation's example.
From your answer, later steps send id as {companyId}.
Records crm.company-created.
4 Make the person its main contact
A contact is a person already registered, with a title at the company; main: true displaces any previous main contact. Idempotent by content — saying it twice appends once.
PUT /companies/{companyId}/contacts/{personId} CRM Manager
{
"personId": "{personId}",
"title": "Accounts",
"main": true
}What the answer holds: the operation's example.
Records crm.company-contact-set.
5 Search the parties a purchase order may name
One list of pickable rows — id, kind, display name, roles, status — narrowed to the companies that supply. This is the read behind every supplier and customer picker.
GET /parties?kind=company&role=supplier CRM Manager
What the answer holds: the operation's example.
6 Correct the person's details
Every field is optional; a field sent replaces what was there and null clears one. The event carries the whole record, sealed again under the same key.
PUT /people/{personId} CRM Manager
{
"phone": "+61 400 000 002",
"locale": "en-AU"
}What the answer holds: the operation's example.
Records crm.person-updated.
7 Change the supplier's terms
Company details are replaced field by field the same way; contacts are their own facts and are untouched by this.
PUT /companies/{companyId} CRM Manager
{
"paymentTermsDays": 45,
"tags": [
"preferred"
]
}What the answer holds: the operation's example.
Records crm.company-updated.
8 Archive the supplier
It leaves the default listings and every picker, keeps its id, history and contacts. A purchase order that named it still resolves through ids=.
DELETE /companies/{companyId} CRM Manager
What the answer holds: the operation's example.
Records crm.company-archived.
9 Restore it
Back to active; a retry on an active company appends nothing.
POST /companies/{companyId}/restore CRM Manager
What the answer holds: the operation's example.
Records crm.company-restored.
10 Remove the contact
The person stays registered; only the link to the company goes.
DELETE /companies/{companyId}/contacts/{personId} CRM Manager
What the answer holds: the operation's example.
Records crm.company-contact-removed.
11 Honour an erasure request
a channel's customer redaction. The key that sealed every profile this person ever had is destroyed in the same transaction the fact is written in; the events stay, unreadable. reference is the request being honoured, and the idempotency key. Needs the admin job.
POST /people/{personId}/erase CRM Manager
{
"reference": "crm-walk:erase-ada",
"reason": "subject-request"
}What the answer holds: the operation's example.
Records crm.person-erased.
12 The tombstone
The row still answers — the id is referenced elsewhere — as status: erased, profile: null, tags dropped, marketing unsubscribed, named "Erased person". The email is released: the same address may register a new person who shares nothing with this one.
GET /people/{personId} CRM Manager
What the answer holds: the operation's example.
13 A document that named them still renders
ids= resolves any status, so an order whose customer was erased shows "Erased person" rather than a broken reference.
GET /parties?ids=%7BpersonId%7D CRM Manager
What the answer holds: the operation's example.
What a recorded fact is: Facts and the journal. Why a repeated step changes nothing: Idempotency and retries.