Skip to content
API PlatformDevelopers

Recipes · R4

Two-way CRM sync (HubSpot, Zoho, Salesforce…)

Push contacts from your CRM in bulk, keep single changes in step, and get told when contacts change here.

Goal: One source of truth for customer details, in both directions.

What starts it

Our webhook events

For the way back, subscribe to contact events; for the way in, call the import from your CRM's change feed.

Your values

Your https endpoint that updates the CRM.

Test mode (dry run): every step runs on your account, but nothing changes and nothing is sent.

  1. Step 1

    Check a batch before importing it

    POSTImport contacts in bulk

    With `dry_run=true` every row is checked and nothing changes. Drop it to import for real.

    curl -X POST 'https://mcp.wa-api.cloud/v1/contacts/import?dry_run=true' \
      -H "Authorization: Bearer $API_KEY" \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: crm-batch-2026-09-24' \
      -d '{
      "rows": [
        {
          "phone": "+15555550123",
          "name": "Jane Doe"
        },
        {
          "phone": "+15555550124",
          "name": "Sam Lee"
        }
      ],
      "on_existing": "update"
    }'
  2. Step 2

    Keep a single change in step

    POSTCreate or update a contact

    curl -X POST 'https://mcp.wa-api.cloud/v1/contacts' \
      -H "Authorization: Bearer $API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
      "phone": "+15555550123",
      "name": "Jane Doe",
      "attributes": {
        "crm_id": "HS-88120"
      }
    }'

    Passes on: contact_id (from id)

  3. Step 3

    Get told when contacts change here

    POSTCreate an endpoint

    curl -X POST 'https://mcp.wa-api.cloud/v1/webhooks/endpoints' \
      -H "Authorization: Bearer $API_KEY" \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: crm-sync-endpoint' \
      -d '{
      "url": "https://hooks.example.com/crm",
      "event_type_ids": [
        "contact.created/1",
        "contact.updated/1",
        "contact.deleted/1"
      ],
      "description": "CRM sync"
    }'

    Passes on: endpoint_id (from endpoint.id)

Good to know

  • Store the signing secret from the last step and check every delivery's signature.

  • Loops are easy to create in two-way syncs. Skip events your own import caused (compare updated_at).

Validated against the API spec on every build