---
id: WF-CRM-DQ-008
title: Offer and create a contact at portal sign-up
trigger: >-
  Platform registration asks CRM for existing contacts after the member verifies
  an e-mail address.
product_area: crm
module: data-quality
revision: 1
status: draft
design_intent: proposed
implementation_evidence: partially-observed
inference: []
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
implements:
  - CAP-CRM-DQ-006
moves:
  - STATE-CRM-DQ-001
crosses:
  - SVC-CRM-DQ-duplicates
  - SVC-CRM-ENT-entities
idempotency: >-
  The CRM create function recognizes matching registration within a minute and
  returns the contact already created.
concurrency: >-
  CRM functions establish workspace context and reject incompatible existing
  session scope. Primary e-mail constraints prevent conflicting active contact
  addresses.
hops:
  - 'n': 1
    actor_or_service: Platform security (sign-up)
    action: >-
      Platform invokes the constrained CRM candidate function with workspace and
      registration details.
    anchor: 'services-crm:db/migrations/0077_portal_sign_up_contacts.sql'
    seam: sync
    on_failure: The Platform response to a failed CRM call is unverified.
    left_inconsistent: No CRM matching write is required for this read.
    observer: member.
    provenance: unknown
    source: FND-CRM-DQ-051
  - 'n': 2
    actor_or_service: CRM candidate function
    action: >-
      Select at most three unlinked contacts above the score floor, prioritizing
      shared e-mail evidence.
    anchor: 'services-crm:db/migrations/0077_portal_sign_up_contacts.sql'
    seam: sync
    on_failure: Reject incompatible workspace session scope.
    left_inconsistent: Candidate selection creates no CRM records.
    observer: Platform.
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/integration/db/portal-sign-up-contacts.integration.spec.js#answers
      the three best contacts above the floor, the e-mail match first and a tie
      in contact-number order, with what Platform masks
  - 'n': 3
    actor_or_service: Platform security
    action: >-
      Platform chooses whether to link an offered contact or record declined
      suggestions.
    anchor: 'services-crm:db/migrations/0077_portal_sign_up_contacts.sql'
    seam: sync
    on_failure: Platform-owned failure handling remains unverified.
    left_inconsistent: Cross-service linking consistency cannot be established from CRM alone.
    observer: Platform.
    provenance: unknown
    source: FND-CRM-DQ-024
  - 'n': 4
    actor_or_service: CRM create function
    action: >-
      After decline, create the contact with registration attribution, ordinary
      captured history and a pair to the best declined contact.
    anchor: 'services-crm:db/migrations/0077_portal_sign_up_contacts.sql'
    seam: sync
    on_failure: >-
      Reject reserved primary e-mail or missing names and roll back the
      function’s writes.
    left_inconsistent: No partial contact or declined comparison commits.
    observer: Platform; staff later see the pair in review.
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/integration/db/portal-sign-up-contacts.integration.spec.js#records
      the declined pair, open, with origin portal_sign_up and the score and
      reasons the scorer gives it
  - 'n': 5
    actor_or_service: Duplicate detection
    action: >-
      Handle the new contact’s change event to rescore and group its
      registration-origin pair.
    anchor: >-
      services-crm:modules/duplicates/services/detection/duplicate-change-events.ts:detectForChangeEvents
    seam: event
    on_failure: Apply ordinary detection delivery recovery.
    left_inconsistent: Grouping may lag registration and its stored pair.
    observer: Reviewers.
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/integration/db/portal-sign-up-contacts.integration.spec.js#keeps
      the declined pair through detection pass over the new contact, which
      groups it
evidence:
  - >-
    services-crm at a8d1364f: db/migrations/0077_portal_sign_up_contacts.sql;
    modules/duplicates/tests/integration/db/portal-sign-up-contacts.integration.spec.js;
    docs/merging/duplicate-detection.md §15.
branches: []
controls: [TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Offer And Create A Contact At Portal Sign-Up

## What It Does

The CRM registration seam offers a constrained set of contacts and creates a new contact when the offer is declined. The candidate and create functions are verified; Platform’s selection, linking and visible failures are outside the pinned owner evidence.

## The Flow

```mermaid
flowchart TD
  H1["Platform invokes the constrained CRM candidate function with workspace and registration details."]
  H2["Select at most three unlinked contacts above the score floor, prioritizing shared e-mail evidence."]
  H1 --> H2
  H3["Platform chooses whether to link an offered contact or record declined suggestions."]
  H2 --> H3
  H4["After decline, create the contact with registration attribution, ordinary captured history and a pair to the best declined contact."]
  H3 --> H4
  H5["Handle the new contact’s change event to rescore and group its registration-origin pair."]
  H4 --> H5
```

## When Something Fails

CRM function failure rolls back its writes, and a recognized short-interval create retry returns the prior contact. Matching does not use workspace duplicate rules or their portal switch.

## Open Design Questions

The portal control’s intended role remains unresolved. The full registration browser proof lacks the required login fixtures and does not establish Platform linking behavior.
