Skip to content

Overview#

Class name: ContactPointWriteComponent Friendly name: Write Contact Points Category: MoveData: NPC Purpose: Writes a person's email, phone and address as ContactPointEmail, ContactPointPhone and ContactPointAddress records on their account, reusing any record that already holds the value

Class description#

Nonprofit Cloud keeps a person's contactable values in three related objects rather than on the person record, and those objects are what its reporting, list building and Marketing Cloud sync read. This component maintains them.

It runs once per person the notification resolves, from the contact post-upsert flow, after the person record has been saved and their account id is known. The person record is left alone: their Email, Phone and address fields keep behaving exactly as they always have, one value each, overwritten by the next donation that carries a different one. Contact points accumulate instead, so a supporter who gives a second email address ends up with two records rather than one replaced one.

A value that already has a contact point is reused, and a value that does not gets one. What decides "already has" is the match key: person, contact point type, and the normalised value. That key is the record's identity, which is why the component normalises rather than comparing verbatim. Platforms send the same address in different casing and the same phone number spaced and bracketed differently, and a duplicate contact point is not cosmetic: consent records attach to contact points, so a second copy of a value would split a supporter's permissions across two rows.

The whole component is off until an organisation turns Write Contact Points on. Nothing here throws, either. A contact point is an enrichment of a donation rather than the donation itself, so a rejected write is reported through Error and the donation stands.

Input parameters#

Person and parent#

Parameter API name Type Description
Parent Account ID ParentId Id The account the contact points belong to. Never a contact: ParentId references Account and Individual only. No id, nothing written.
Anonymous Account ID AnonymousAccountId String The organisation's shared anonymous person account. When the parent is that account, nothing is written.
Activity Date Time ActivityDateTime Datetime Stamped as ActiveFromDate when a record is created, in the running user's timezone. Today's date when absent.
List of Log Entries LogList List The flow's log collection. Every contact point created, matched or skipped is appended to it and handed back.

Email#

Parameter API name Type Description
Email Address Email String The person's email address. Blank writes nothing.
Email Type EmailUsageType String The platform's own email type: home, work or other. Mapped to UsageType where Salesforce has an equivalent.

Phone#

Parameter API name Type Description
Telephone Number Phone String The person's phone number, written as the platform sent it. Blank writes nothing.
Phone Type PhoneType String Stored in ContactPointPhone.PhoneType. Home or Mobile, the only two values Salesforce allows on that field.
Phone Type (payload) PhoneUsageType String The platform's own phone type: home, work or other. Maps to UsageType, not to PhoneType, because there is no Mobile in home, work or other.
Phone Is SMS Capable IsSmsCapable Boolean Sets IsSmsCapable on new phone contact points. One value for every number the organisation writes.
Phone Minimum Digits MinimumPhoneDigits Decimal Numbers with fewer digits than this are skipped as unusable. Zero or absent checks nothing.

Address#

Parameter API name Type Description
Address Name AddressName String Name is required on ContactPointAddress and has no default. No name, no address contact point.
Address Type AddressType String Billing or Shipping. There is no Mailing.
Address Usage Type AddressUsageType String The platform's own address type, where one is sent. Falls back to the organisation's default.
Address Street Street String
Address City City String
Address State State String
Address Postal Code PostalCode String
Address Country Country String
Address Quality Index AddressQualityIndex Decimal The platform's completeness score for this address, 0.00 to 1.00. Only JustGiving, Benevity and EventMaster send one.
Address Quality Threshold AddressQualityThreshold Decimal The organisation's minimum address quality. Most organisations have set none.

Shared#

Parameter API name Type Description
Usage Type Default UsageTypeDefault String The organisation's default UsageType, used on all three contact point types and only where the platform sent no type of its own.

Output parameters#

Parameter API name Type Description
Success Success Boolean (required) False when a write failed. The donation still succeeds.
Error Error String Null unless a write failed or a configured type value was rejected. The flow appends it to the pipeline's error collection.
List of Log Entries LogList List (required) One entry per contact point created, matched, updated or skipped.
Json Output of Log Entries LogJson String JSON-encoded version of the same log entries.
Record IDs RecordList List of String Every contact point this person's donation created, matched or updated. The pipeline adds them to the notification's Records section.
Email Contact Points Created EmailsCreated Integer Records created, not records matched.
Phone Contact Points Created PhonesCreated Integer
Address Contact Points Created AddressesCreated Integer
Phone Skipped Reason PhoneSkippedReason String Why no phone contact point was written when there was a number to write.
Address Skipped Reason AddressSkippedReason String Why no address contact point was written when there was an address to write.

Where the values come from#

The Write Contact Points element in the contact post-upsert flow supplies every input, so a value that looks wrong in a notification's log can be traced to either a setting or the payload:

Input Supplied from
ParentId Record.Id, the person account the stage has just upserted
AnonymousAccountId Config_AnonymousContactId
ActivityDateTime The donation date, or the person's created date when the donation carries none
Email, Phone The payload's own values, the same ones the person record is mapped from
EmailUsageType, PhoneUsageType The payload's EmailType and PhoneType
Street, City, State, PostalCode, Country The payload's MailingAddress_ fields
AddressQualityIndex MailingAddress_QualityIndex, the platform's score
AddressName Config_ContactPointAddressName, parsed by movedata__NamePatternParserComponent
AddressType, PhoneType, UsageTypeDefault, IsSmsCapable, MinimumPhoneDigits The matching contact point settings
AddressQualityThreshold Config_ContactAddressQualityIndex, shared with the contact mapping flow

AddressUsageType is the one input the package's own flow does not wire, because no platform sends an address type today. Address contact points therefore take the organisation's default.

Behaviour#

Matching and reuse#

Each type is matched on its own normalised value, within that one person's records:

Type How two values are treated as the same
Email Trimmed and case-folded. Nothing else: dots and plus-addressing are real distinctions at some providers, and treating two mailboxes as one address is worse than holding two records.
Phone The digits, and only the digits. Spaces, brackets and dashes fold away; a country code or a trunk zero does not.
Address Street, postcode and country, lower-cased with punctuation, spaces and newlines removed. City and state are deliberately outside the key.

So (07700) 900 123 and 07700900123 are one phone record, and 021 123 456 and +64 21 123 456 are two. The second pair is one New Zealand mobile to a reader, and that is a known and accepted cost: deciding that a country code and a trunk zero describe the same subscriber is a question every notifier and every extension has, so it is answered centrally rather than inside this component. Until it is, an organisation whose platforms send both forms holds two phone contact points for the one number.

City and state sitting outside the address key is what lets a correction land. A platform that sends the same street and postcode with a corrected city updates the address record the organisation already holds instead of adding a second one, and those two fields are the only source values a matched record is ever updated with.

What is written once and never again#

IsPrimary, ActiveFromDate, the address Name, and the AddressType, PhoneType, UsageType and IsSmsCapable values are set when a record is created and left alone afterwards. A later donation has nothing new to say about them, and re-applying a default would overwrite a choice the organisation's team or another system made.

A matched phone number is not rewritten into the format this platform happens to send, for the same reason. For an organisation running two platforms that would flip the stored value back and forth on every donation, which is the overwrite behaviour contact points exist to avoid.

IsPrimary is true only on the first record of its type for that person. A second email address therefore arrives non-primary, and the first record is untouched.

The address quality gate#

An address scoring below the organisation's threshold is not written. The condition is the person record's own, condition for condition: the skip needs a threshold set, a score present, and the score below the threshold. A missing threshold passes and a missing score passes, which is almost all traffic.

The gate sits above the match, so a below-threshold address cannot update an address the organisation already holds any more than it can add a new one. That follows from reusing the person record's gate rather than from a separate choice, and it is the behaviour the person record has had all along.

When nothing is written#

Each of these is a log line naming the reason, not an error:

Nothing written Reason
Anything at all No ParentId, or the parent is the anonymous account. A fundraiser on the same notification is a different account and still gets theirs.
Phone Fewer digits than the configured minimum.
Address No street, postcode or country, so there is nothing to match the record on.
Address Below the quality threshold.
Address No address name pattern configured, and Name is required on the object.

One invocation, many people#

Existing contact points are read once per invocation, one query per object, and matched in memory. Records created earlier in the same invocation are matched against too, so two requests for the same person in one notification cannot each create the same contact point.

What ends up on the notification#

Every created, matched, updated and skipped contact point is a log entry on the notification, naming the record and the value it matched on. The ids of the records touched go back as RecordList, matched and updated records as well as created ones, so a consultant can see which record a donation attached to and open it from the notification's Records section.

Error handling#

Configured type values#

All four type settings are checked against the field's active picklist values in that organisation, and a value the field does not carry is dropped, reported in Error, and the contact point written without it.

That check matters most where it is least obvious. AddressType and UsageType are unrestricted picklists, so Salesforce would accept a typo, store it, and then quietly leave the record out of every filtered report. PhoneType is restricted, so a typo there would reject every phone contact point outright. Both failure modes are someone's setting to fix, which is why they are reported rather than silently ignored.

A type the platform sent that the organisation's picklist does not carry falls back to the default without an error, because only a setting is someone's mistake.

Failed writes#

Records are inserted and updated with partial success allowed, so one rejected contact point neither rolls the donation back nor is reported against the other people on the same notification. A rejection sets Success to false, appends the reason to Error and writes a log line. Missing field-level access on the three objects is the common cause; see permission sets.

The flow element also carries a fault connector, so an unexpected failure is caught and recorded rather than failing the stage.

Dependencies#

Required objects#

  • ContactPointEmail, ContactPointPhone, ContactPointAddress: the Nonprofit Cloud contact point objects
  • Account: the parent of every contact point written

Required classes#

  • movedata.MoveDataLogEntry: the pipeline's log entry type
  • movedata__NamePatternParserComponent: resolves the address name pattern before this component runs
Ask MoveData AI
Ask about setup, configuration, or troubleshooting
How can I help you with MoveData today?