Link external records to people and companies
Idempotently link external platform records (e.g. Stripe customers) to Modem people and companies.
Each person / company block states the facts the platform knows about that side of the record — not an assertion of how to resolve it. Modem owns all matching.
Person — matched by platform identity first, then by exact email. A bare email matches an existing person but never creates one (it may be a shared inbox like billing@); creation requires displayName.
Company — matched by the first hit, in trust order:
- an existing platform
identitylink for this block - the resolved person’s current company
- stated
domains, then the domain derived fromperson.email(free-mail domains such as gmail.com carry no signal) - an exact, case-insensitive
namematch - otherwise created — only when
nameis stated
When both a person and a company resolve, their membership is recorded.
If the platform identity and a natural key (email/domain) resolve to different entities, the identity match wins and nothing is merged; the other entity is returned as conflictWith — a probable duplicate to reconcile deliberately.
Removing a stored link is not part of this endpoint; it will ship separately.
/identifyAuthorizationBearer token · headerrequiredOrganization API key (modem_…).
Idempotency-KeystringReplays a successful mutating request for 24 hours. Reuse with a different payload returns 409.
application/jsonidentificationsobject[]requiredShow propertiesHide properties
objectcompanyobjectShow propertiesHide properties
descriptionstringdomainsstring[]Natural keys. Matches (or create-seeds) a company by domain. A free-mail domain never matches, creates, or is stamped (no signal), but does not suppress name-keyed creation.
identityobjectPlatform pointer. Matched identity-first; upserted onto the matched/created company.
Show propertiesHide properties
platformstringrequiredSource platform slug (e.g. "stripe", "salesforce").
platformIdstringrequiredThe record id on the platform (e.g. a Stripe customer id like "cus_…").
logoUrlstring<uri>namestringEnables creation when no match is found.
overwritestring[]Scalar fields to overwrite even when already populated. Default is set-once.
personobjectShow propertiesHide properties
avatarUrlstring<uri>descriptionstringdisplayNamestringEnables creation when no match is found.
emailstring<email>Natural key. Matches a person by exact, lowercased email. Never creates on its own.
identityobjectPlatform pointer. Matched identity-first; upserted onto the matched/created person.
Show propertiesHide properties
platformstringrequiredSource platform slug (e.g. "stripe", "salesforce").
platformIdstringrequiredThe record id on the platform (e.g. a Stripe customer id like "cus_…").
overwritestring[]Scalar fields to overwrite even when already populated. Default is set-once.
OK
resultsobject[]requiredShow propertiesHide properties
objectcompanyobject | nullrequiredShow propertiesHide properties
conflictWithstring | nullrequiredA probable duplicate surfaced as a merge candidate: the platform identity resolved to id, but the natural key (email/domain) is owned by this different existing entity. identify never merges — it's left for a deliberate merge. Null when there was no conflict.
Show propertiesHide properties
stringnullcreatedbooleanrequiredidstringrequirednullmembershipLinkedbooleanrequiredpersonobject | nullrequiredShow propertiesHide properties
conflictWithstring | nullrequiredA probable duplicate surfaced as a merge candidate: the platform identity resolved to id, but the natural key (email/domain) is owned by this different existing entity. identify never merges — it's left for a deliberate merge. Null when there was no conflict.
Show propertiesHide properties
stringnullcreatedbooleanrequiredidstringrequirednullInvalid request (validation failed).
codestringrequiredMachine-readable error code, e.g. NOT_FOUND.
dataanyOptional structured error details.
definedbooleanrequiredTrue if the error was declared in the procedure contract.
messagestringrequiredHuman-readable error message.
statusintegerrequiredHTTP status code.
Missing or invalid API key.
codestringrequiredMachine-readable error code, e.g. NOT_FOUND.
dataanyOptional structured error details.
definedbooleanrequiredTrue if the error was declared in the procedure contract.
messagestringrequiredHuman-readable error message.
statusintegerrequiredHTTP status code.
The API key does not have access to this resource.
codestringrequiredMachine-readable error code, e.g. NOT_FOUND.
dataanyOptional structured error details.
definedbooleanrequiredTrue if the error was declared in the procedure contract.
messagestringrequiredHuman-readable error message.
statusintegerrequiredHTTP status code.
The requested resource was not found.
codestringrequiredMachine-readable error code, e.g. NOT_FOUND.
dataanyOptional structured error details.
definedbooleanrequiredTrue if the error was declared in the procedure contract.
messagestringrequiredHuman-readable error message.
statusintegerrequiredHTTP status code.
The request conflicts with an existing resource or idempotency operation.
codestringrequiredMachine-readable error code, e.g. NOT_FOUND.
dataanyOptional structured error details.
definedbooleanrequiredTrue if the error was declared in the procedure contract.
messagestringrequiredHuman-readable error message.
statusintegerrequiredHTTP status code.
Rate limit exceeded.
codestringrequiredMachine-readable error code, e.g. NOT_FOUND.
dataanyOptional structured error details.
definedbooleanrequiredTrue if the error was declared in the procedure contract.
messagestringrequiredHuman-readable error message.
statusintegerrequiredHTTP status code.
Internal server error.
codestringrequiredMachine-readable error code, e.g. NOT_FOUND.
dataanyOptional structured error details.
definedbooleanrequiredTrue if the error was declared in the procedure contract.
messagestringrequiredHuman-readable error message.
statusintegerrequiredHTTP status code.