REST Integration
CRM ships optional REST configuration for the contact entity. Resource
plugin ID: entity:crm_contact; config in
config/optional/rest.resource.entity.crm_contact.yml. For JSON:API, see
JSON:API Integration.
For generic REST module setup (authentication plugins, formats), see the Drupal REST module documentation.
Quick start
- Enable the REST module after CRM is enabled so optional CRM config can install.
- Grant CRM entity permissions (for example
view any crm contact,create any crm contact,edit any crm contact) and REST permissions. - Call the
entity:crm_contactresource using Drupal’s standard entity REST routes (for examplerest.entity.crm_contact.GETwith the contact ID and?_format=json). - Inline
entitydata on configured contact-method fields followscrm.serialization.settings(default:emails,telephones,addresses). Adjust at/admin/config/crm/serialization/settingswhen Serialization is enabled. - REST write verbs that accept inline
entitydata followcrm.rest.settings(default:POST,PATCH,DELETE). Adjust at/admin/config/crm/rest/settings.
ddev drush pm:enable rest -y
ddev drush cache:rebuild
Requirements
- CRM module enabled.
- Drupal core REST module enabled (
drush pm:enable rest). - Authentication configured for your site (cookie auth is in the default optional config; production sites typically use additional auth plugins).
What you get
When configured, the contact entity supports REST operations:
| Method | Formats |
|---|---|
| GET | json, xml |
| POST | json, xml |
| PATCH | json, xml |
| DELETE | json, xml |
Default optional config uses cookie authentication. Adjust formats and auth in
the REST UI at Admin > Configuration > Web services > REST or by editing the
REST resource config entity (rest.resource.entity.crm_contact).
Inline contact methods
GET responses for crm_contact embed the full crm_contact_method entity data
inline for configured contact method reference fields. By default, CRM enables
emails, telephones, and addresses. Each field value includes the standard
reference metadata (target_id, target_uuid, primary) plus an entity key
containing the normalized contact method fields (for example the email address,
telephone number, or address value). The crm_contact back-reference is omitted
from embedded methods to avoid circular serialization.
Inline read normalization is controlled by
crm.serialization.settings and applies to CRM contact serialization in
general, not only REST. POST and PATCH may include entity to create or
update contact methods, or send reference metadata only to link existing methods
without changing their field values. Inline write processing runs only on
REST contact-resource routes for verbs enabled in crm.rest.settings.
Contact method items on POST and PATCH
Each item in emails, telephones, addresses, or another configured field
is either reference-only or inline. entity is not required to link an
existing method.
| Intent | Required on the item | Optional |
|---|---|---|
| Link existing method (no field changes) | target_id or target_uuid |
primary |
| Create new method | entity (with type and bundle fields) |
primary |
| Update existing method fields | entity |
target_id, target_uuid, or entity.id/entity.uuid; primary |
Omitting entity does not update email, telephone, or address values on the
crm_contact_method entity—only the contact’s reference list and primary
flags change. See ContactRestTest::testReferenceOnlyPayloadsStillWork().
Reference-only example
Link a pre-existing email when creating a contact (POST):
{
"type": [{ "target_id": "organization" }],
"name": [{ "value": "Reference Contact" }],
"emails": [
{
"target_id": "42",
"primary": true
}
]
}
Add a telephone by reference on PATCH without touching other contact-method
fields (omit emails and addresses):
{
"telephones": [
{
"target_id": "99",
"primary": true
}
]
}
POST example (inline entity)
{
"type": [{ "target_id": "organization" }],
"name": [{ "value": "Springfield Power Plant" }],
"emails": [
{
"primary": true,
"entity": {
"type": [{ "target_id": "email" }],
"email": [{ "value": "burns@springfield.example" }]
}
}
]
}
PATCH and list replacement
For PATCH (and POST when the field is present), behavior depends on whether a configured contact-method field appears in the payload:
- Field omitted: That field’s stored references are left unchanged.
- Field present: The submitted list is authoritative for that field. Methods
previously on the contact for that field but not in the new list are deleted
(when the current user has delete access); otherwise the request fails with
403 Access denied. - Reference-only items: To keep or relink methods without editing method
data, send
target_idortarget_uuidand optionalprimary; omitentity. - Ownership: Nested
crm_contactvalues on methods are ignored; ownership comes from the contact resource being written. - Bundles: Only fields that exist on the contact’s bundle and are listed in
contact_method_fieldsparticipate. Custom reference fields must referencecrm_contact_methodentities and be added to serialization settings to use inline behavior.
PATCH example updating one method, adding another, and removing a third (the removed method entity is deleted):
{
"emails": [
{
"target_id": "42",
"primary": false,
"entity": {
"type": [{ "target_id": "email" }],
"email": [{ "value": "updated@example.com" }]
}
},
{
"primary": true,
"entity": {
"type": [{ "target_id": "email" }],
"email": [{ "value": "new@example.com" }]
}
}
]
}
CRM settings
CRM separates contact-method normalization from optional REST write behavior.
| Config | Path | Controls |
|---|---|---|
crm.serialization.settings |
/admin/config/crm/serialization/settings |
Which contact fields embed or accept inline entity data |
crm.rest.settings |
/admin/config/crm/rest/settings |
Which REST write verbs run inline contact-method persistence |
rest.resource.entity.crm_contact |
Core REST UI | Which methods, formats, and authentication the REST resource exposes |
Contact serialization settings
Default config config/install/crm.serialization.settings.yml controls
reusable inline contact-method behavior for CRM contact serialization. It is
installed with CRM. The settings form and menu links are available only while
Serialization remains enabled.
contact_method_fields — sequence of contact field machine names. Only
fields that reference crm_contact_method entities are eligible (discovered per
bundle). Default: emails, telephones, addresses. Fields not listed stay in
standard entity-reference form in API payloads.
REST write settings
Default config config/install/crm.rest.settings.yml controls which REST
write verbs accept inline contact-method entity data. The settings form and
menu links are available only while REST remains enabled.
verbs — sequence of HTTP methods (POST, PATCH, DELETE) that enable
inline write processing. Matched against the current request method via
ContactRestWriteService::isInlineWriteEnabled(), which also requires the
matched route to belong to the entity.crm_contact REST resource. Default: all
three write verbs. Does not remove methods from the core REST resource;
when a verb is disabled here, write requests skip inline processing for that
verb. Read normalization is unaffected.
Enabling REST alongside CRM
Enable REST after CRM is enabled so optional config can install in the same
modules_installed batch.
- Enable the REST module (optional CRM REST resource config installs automatically when CRM is already enabled):
ddev drush pm:enable rest -y
- Clear caches:
ddev drush cache:rebuild
- Grant permissions — REST requires appropriate entity permissions plus REST-specific permissions from the REST module.
Optional config installation is driven by
InstallOptionalConfigHooks::modulesInstalled() when REST is enabled, not by a
manual config import.
Implementation
Inline contact-method read behavior is implemented by
ContactSerializationService (crm.contact_serialization). REST write behavior is
implemented by ContactRestWriteService (crm.contact_rest_write). Both are
orchestrated by ContactNormalizer (crm.normalizer.contact, priority 20).
The core entity:crm_contact REST plugin uses Drupal’s standard serialization
pipeline.
Request flow
flowchart TB
subgraph read [Serialize — ContactSerializationService]
direction TB
N1[ContactNormalizer.normalize]
F1[getEnabledContactMethodFields]
E1[embed]
N1 --> F1 --> E1
end
subgraph write [POST PATCH denormalize — ContactRestWriteService]
direction TB
N2[ContactNormalizer.denormalize]
V1[isInlineWriteEnabled]
C1[ensureContactTargetContext]
B1[getContactBundle]
P1[processInlineContactMethods]
IP[ContactMethodItemService: per-item create / update / link]
R1[internal: deleteRemovedMethods]
N2 --> V1 --> C1 --> B1 --> P1 --> IP --> R1
end
FormN[SerializationSettingsForm] --> read
FormR[RestSettingsForm] --> write
Services
| Service ID | Responsibility |
|---|---|
crm.contact_serialization |
Field discovery and inline embed on normalize; also drives the serialization settings form |
crm.contact_rest_write |
REST route/verb gating, context resolution, inline contact-method persistence, and removal on POST/PATCH |
crm.contact_method_item |
Per-item contact method create/update/link for REST write payloads |
ContactRestWriteService delegates per-item create/update/link logic to
ContactMethodItemService (crm.contact_method_item).
Extending behavior
- Prefer changing
crm.serialization.settingsfor site-specific field lists. - Prefer changing
crm.rest.settingsfor REST write-verb gating. - Replace or decorate
crm.contact_serialization,crm.contact_rest_write, andcrm.contact_method_item(seecrm.services.yml) when altering normalization or persistence logic. - Functional coverage:
tests/src/Functional/Rest/ContactRestTest.php(inline GET/POST/PATCH, reference-only payloads, verb gating, bundle validation). - Kernel coverage:
tests/src/Kernel/Service/ContactRestWriteServiceTest.php(ContactRestWriteServiceandContactMethodItemServicewith real entities). - Unit coverage:
tests/src/Unit/Service/ContactRestWriteServiceTest.php(mocked orchestration, item processing, and removal branches).
Limitations
- Only
crm_contacthas optional REST config in CRM core; other CRM entities are not pre-configured. - The optional REST resource config lists a dependency on the
usermodule only; the REST module must still be enabled explicitly. - No custom CRM REST endpoints beyond the standard entity resource plugin.
- Inline write processing runs only on REST contact-resource routes.