Skip to content

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

  1. Enable the REST module after CRM is enabled so optional CRM config can install.
  2. Grant CRM entity permissions (for example view any crm contact, create any crm contact, edit any crm contact) and REST permissions.
  3. Call the entity:crm_contact resource using Drupal’s standard entity REST routes (for example rest.entity.crm_contact.GET with the contact ID and ?_format=json).
  4. Inline entity data on configured contact-method fields follows crm.serialization.settings (default: emails, telephones, addresses). Adjust at /admin/config/crm/serialization/settings when Serialization is enabled.
  5. REST write verbs that accept inline entity data follow crm.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_id or target_uuid and optional primary; omit entity.
  • Ownership: Nested crm_contact values 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_fields participate. Custom reference fields must reference crm_contact_method entities 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.

  1. Enable the REST module (optional CRM REST resource config installs automatically when CRM is already enabled):
ddev drush pm:enable rest -y
  1. Clear caches:
ddev drush cache:rebuild
  1. 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.settings for site-specific field lists.
  • Prefer changing crm.rest.settings for REST write-verb gating.
  • Replace or decorate crm.contact_serialization, crm.contact_rest_write, and crm.contact_method_item (see crm.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 (ContactRestWriteService and ContactMethodItemService with real entities).
  • Unit coverage: tests/src/Unit/Service/ContactRestWriteServiceTest.php (mocked orchestration, item processing, and removal branches).

Limitations

  • Only crm_contact has optional REST config in CRM core; other CRM entities are not pre-configured.
  • The optional REST resource config lists a dependency on the user module 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.