Directory and CRM
The directory family is the workspace's canonical registry of external parties — the people and organisations every other desk resolves its counterparties through. The crm family is the revenue desk built on top of it: deals, accounts, playbooks, and escalations. Scopes: directory.read / directory.write and crm.read / crm.write.
Awareness scoping — the Directory's core rule
Directory reads are awareness-scoped at the query layer. A record marked named-only is absent from every result for a seat without a grant — not redacted, not hinted at, not countable. Two agents can run the same directory.search and correctly receive different result sets. Design for it: an empty result means "nothing you are allowed to know about", never "nothing exists".
Query the Directory
curl -X POST https://os.cohortapp.com/api/v1/directory.search \
-H "Authorization: Bearer $COHORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"params": {"q": "acme", "limit": 10}}'
Reads: search, listPeople, listOrgs, getPerson, getOrg, listLists, listCaptures, listMerges, listAwarenessRequests, plus ask (a grounded question over the Directory) and exportVcard — both audited side-effecting reads.
Writes: createPerson / updatePerson, createOrg / updateOrg, contact channels (addChannel, removeChannel, setPrimaryChannel), links (linkParty, unlinkParty), lists (createList, addToList, removeFromList, shareList), and captures (proposeCapture, resolveCapture) — the intake lane that turns an observed counterparty into a record.
executeMerge, revertMerge, setAwareness, archivePerson, and
archiveOrg refuse an agent actor in the domain layer — the call creates a
review item for a human instead of mutating. Agents propose
(proposeMerge, requestAwareness); humans dispose. Expect a result that
says a review was filed, not a completed mutation.
Run the revenue desk
The crm family (44 methods) mirrors the app's deal surfaces exactly — including its holds.
| Task | Methods |
| --- | --- |
| Deal lifecycle | createDeal, updateDeal, moveStage, closeDeal, deleteDeal |
| Terms and playbooks | proposeTerms, updateHeldTerms, holdNextStep, runNextBestAction, savePlaybook, togglePlaybook, deletePlaybook, compilePlaybook (a pure compile preview — persists nothing) |
| Sharing and delegation | shareDeal, revokeDealShare, mintGuestToken, addCallGuest, delegate |
| Accounts and contacts | createAccount, updateAccount, archiveAccount, createContact, updateContact, deleteContact, linkContact |
| Record keeping | logInteraction, linkEmail, linkFiles, pinNote, redactInteraction, logDealDecision, taskFollowThrough |
| Escalations and targets | signEscalation, returnEscalation, setTarget |
| Reads | listDeals, getDeal, listEscalations, listAccounts, listContacts, listInteractions, listPlaybooks, getTargets, ask |
Playbook holds return as results
crm.proposeTerms is the enforcement path for concessions. When proposed terms cross a playbook line — too deep a discount, a non-standard term — the call succeeds: the deal is placed on hold, the interaction log records it, and the result carries the hold:
{
"ok": true,
"result": {
"dealId": "deal_…",
"held": true,
"playbookSlug": "discount-floor",
"reason": "Discount beyond 20% needs a signature.",
"marginImpactCents": -420000,
"ledgerId": "ab12cd34"
}
}
Report it exactly as it is — "held for signature — ledger #ab12cd34" — and never as a completed concession. A human releases the hold from the deal surface (or an agent updates the held terms with updateHeldTerms to bring them inside the line).
Two verbs are human-signature acts in the domain layer regardless of scope: signEscalation / returnEscalation (deciding an escalated deal) and setTarget's targetCents (setting a revenue target). An agent-plane call to these refuses — the same rule the app enforces.
Contact cards vs. the Directory
The older contact family (contact.add / update / delete / list) manages contact cards on org content, and contacts.upsert feeds the knowledge-plane contact registry. For external people and companies, the Directory is the canonical registry — prefer it; the other two remain for their in-app surfaces.