So when you are setting up the SAML Identity Provider the most common issue you can run into is that the Matching Method we use does not work.
This Guide is going to try to make it so you can actually pick the right option the first time and if that does not work then what you need in order to troubleshot the issue.
To Start with there is a bunch of different MLS's and they all might use different option. at the end of the article I am going to list the ones I have worked with and the different options they have used at the end of the article
To start with the Contact Matching area is at the end of the SAML Identity Provider area
The SAML Attribute Path * Is what we are matching to on the MLS side and we would be getting this from the MLS
The Matching Field * has a number of different options and they Match to different Areas of the Database
-
Account Number - This Matches to the Account Number of the Contact in the Database
-
Email - This Matches to the Email Address of the Contact in the Database
-
First Name- This Matches to the First Name of the Contact in the Database
- Last Name - This Matches to the same thing as the First Name only the Last Name instead
-
Custom field - This Matches to to the More Info Tab Additional Custom Fields Area - you will need to figure out what the field is using the API
-
Edition Field - This Matches to to the Real Estate Tab and then normally to the MLS ID - you will need to figure out what the field is using the API
Most of the Time we will be Matching off the Edition Field - and if so the Most common option to use is going to be
$.MLSId
However this is not always the case and to be very sure we need to do a little bit of testing
I am going to assume that you have Postman setup and working and there is a Working API key for the database you are in if not we have information on setting up Postman and Creating an API key here
Setting up Postman for testing GrowthZone API's
API/SSO Implementation Information for the GrowthZone Platform
Once you have Postman setup with the API Key we are going to make a Call to one of 2 different API end points
For the Real Estate Tab
/api/contacts/editionFields/{Contact ID}/V2/
For the Custom Fields Tab
/api/contacts/Contact ID}/MoreInfo/
Then you need to use the information it pulls to match to what the MLS is requesting you match to
This is going to require you to look at the results and figure out what the proper fields are and then prepend it with
$.
So lets say we are looking at the Real Estate Tab and the MLS Give you an exaimple of a User on there side and what they have for the matching information I'm going to use a real life exaimple that I had and they sent over the following information
There was some issues with this contact so they ended up not being a good option to use but we found a better one with Lily Howard
They wanted to match to the loginid and that was paz.34118 And the Contacts name was Lily Howard
I did a search for them in the Contact section of the database and opened there Real Estate Tab
I then searched for paz. using Ctrl+f on the page and found out that there was 3 different fields that contained that information
Normally We are looking for a MLS ID of some type but can be hard to tell what that field actually is on the backend of the database so we need to check with Postman
After looking around we figured out that is was the PMMLSID and it was not set to MLSId
so what the matching Field should be is
$.PMMLSID
We tested with the customer and in there testing it worked as expected!
The process would be the same for Custom Fields just make the correct call and try to match it up using the $.
TIPS
If you are having issues figuring out what information you are matching to I have found that finding an Active Contact in the Database and then Sending over the following can help the MLS send over the required information
Hello,
We have a Contact in the Database with the name {First Name Last Name}, We are matching them to a field on your side thats {What we have in the SAML Attribution Path} and we are matching this Field to {What Ever we have for the matching fields/s} if this is not correct let us know or please provide a Summary of what you are sending over in the SAML.
Thanks
This is the start of the Matching Fields I have seen in the Wild so far but they can also match to multiple fields or options but I have not really seen that as of yet.
Clareity
loginid Edition field $.PMMLSID
loginid Edition field $.MLSId
loginid Edition field $.MLSLoginId
loginid Edition field $.LockBoxListingAgent
loginid Account Number
FlexMLS
MemberLoginId Edition field $.MLSId
flexmls_user_name Account Number
flexmls_nrds_id Account Number
flexmls_user_name Edition Fields $.MemberLoginId
flexmls_tech_id Edition field $.MLSId
StellarMLS
MemberLoginId Edition field $.MLSId
loginid Account Number
This is really getting into the weeds but I noticed in working with various people that sometimes something that should match would not and or we would have duplicate content or fields and telling what to match to some times could only be figured out by getting in a meeting with the customer the MLS and I and then just spending an hour or more testing fields and users till we found the field that actually worked.
I asked about this and it looks like we don't match off of the fields persay but instead match to the JSON Blob on the backend, (this is why we have the $. at the start) and its does not map 1 to 1 on the fields you might have to try to translate the format of the json blob into what the fields on the database are, here is the blob have fun...
public int? NRDSMemberId { get; set; } // MemberId or OfficeId depending on the type of contact
public long? NRDSTimestamp { get; set; } // For data sync
public bool? IsSupplementalRecord { get; set; }
public bool? IsPOE { get; set; }
public bool? IsStateAssociation { get; set; }
public bool? SyncWithNRDS { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public NARStatusOption? NRDSStatus { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public NRDSAgentType? NrdsAgentType { get; set; }
public NARStateIds? LicensingState { get; set; }
public NARStateIds? PrimaryState { get; set; }
public string MemberSubclass { get; set; }
public DateTime? NRDSJoinDate { get; set; }
public DateTime? LocalJoinDate { get; set; }
public NARBusinessFields? NRDSPrimaryFieldOfBusiness { get; set; }
public List<NARBusinessFields> SecondaryFieldsOfBusiness{get; set; }
public int? NRDSOfficeId { get; set; }
[Hide, Obsolete("Looks like this was added but not needed", true)]
public string Status { get; set; }
public string MemberOccupationName { get; set; }
public int? OfficeContactDRId { get; set; }
public int OfficeNMSalesCount { get; set; } = 0;
public int? MemberMLSAssociationId { get; set; }
public int? OfficeManagerNRDSId { get; set; }
[Hide, Obsolete("Looks like this was added but not needed", true)]
public NRDSAddressTypes? PMPreferredMail { get; set; }
[Hide, Obsolete("Looks like this was added but not needed", true)]
public NRDSAddressTypes? PMPreferredPublication { get; set; }
public DateTime? PMStatusChangedDate { get; set; }
public NRDSFlag? PMPreviousNonMemberFlag { get; set; }
public NRDSYNFlag? PMOnRosterFlag { get; set; }
public DateTime? PMMLSOnlineStatusChangedDate { get; set; }
public int? PMAssociationPOEID { get; set; } //Primary member POE
public int? PMAssociationID { get; set; }
public char? PMArbitrationEthicsPending { get; set; }
public char? PMReinstatementCode { get; set; }
public DateTime? PMReinstatementDate { get; set; }
public DateTime? PMNationalDuesPaidDate { get; set; }
public DateTime? PMStateDuesPaidDate { get; set; }
public NRDSFlag? PMBillingDiscount { get; set; }
public IEnumerable<SupplementalMember> SupplimentalMembers { get; set; }
public int? POAssociationPOEID { get; set; }//Primary office POE
public int? POAssociationID { get; set; }
public string POCorporateName { get; set; }
public int? POFranchiseID { get; set; }
public int? POParentCompanyID { get; set; }
public string PODistrict { get; set; }
public string POTaxID { get; set; }
public string POCorporateLicense { get; set; }
public int? POMainID { get; set; }
public NRDSBranchType? POBranchType { get; set; }
public int? POBillingID { get; set; }
public string POContactUnlicensed { get; set; }
public DateTime? POStatusChangedDate { get; set; }
public NRDSYNFlag? POOnRoster { get; set; }
public string POMLSID { get; set; }
public long? POTimestamp { get; set; }
public DateTime? POMLSOnlineStatusChangedDate { get; set; }
public int? SOOfficeNRDSID { get; set; }
public NRDSStatusOption? SOStatus { get; set; }
public DateTime? SOStatusChangedDate { get; set; }
public int? SONMSalespersons { get; set; }
public long? SOTimestamp { get; set; }
public bool? SOSyncWithNRDS { get; set; }
public int? SOAssociationId { get; set; }
public List<NarSupplementalOffice> NarSupplementalOffices { get; set; }
[Hide] public int? NRDSPhysicalContactAddressId { get; set; }
[Hide] public int? NRDSMailingContactAddressId { get; set; }
[Hide] public int? NRDSContactPhoneId { get; set; }
[Hide] public int? NRDSCellContactPhoneId { get; set; }
[Hide] public int? NRDSContactEmailAddressId { get; set; }
[Hide] public int? NRDSPersonalEmailId { get; set; }
[Hide] public int? NRDSTeamEmailId { get; set; }
[Hide] public int? NRDSSharedEmailId { get; set; }
[Hide] public int? NRDSAddlContactPhoneId { get; set; }
[Hide] public int? NRDSFaxContactPhoneId { get; set; }
[Hide] public int? NRDSPersonWebAddressId { get; set; }
/// MLS Section
public bool? SyncWithMLS { get; set; }
public string MLSOfficeId { get; set; }
public string MLSId { get; set; }
public string MLSFirmId { get; set; }
public string MLSFirmName { get; set; }
[Hide] public bool? MLSParticipant { get; set; }
public string MLSOfficeLicenseNumber { get; set; }
public bool? MLSWaived { get; set; }
public DateTime? MLSJoinDate { get; set; }
public bool? CMLSMember { get; set; }
public DateTime? MLSTermDate { get; set; }
public string MLSAccessLevel { get; set; }
public string MemberStateLicense { get; set; }
[NARMapping("MLSStatusMapping")]
public string MLSStatus { get; set; }
public string IDPPassword { get; set; }
[NARMapping("MLSAgentTypeMapping")]
public string AgentType { get; set; }
public string OfficeBrokerMLSId { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable<string> OfficeManagerMLSId { get; set; }
public bool MLSIDXOfficeParticipation { get; set; }
public string MLSLoginId { get; set; }
[NARMapping("MLSUserClassMapping")]
public string MLSUserClass { get; set; }
[NARMapping("MLSOfficeTypeMapping")]
public string MLSOfficeType { get; set; }
[NARMapping("MLSPrimaryUserBoardMapping")]
public string MLSPrimaryUserBoard { get; set; }
public int? MLSContactPhoneId { get; set; }
public bool? MLSMailingList { get; set; }
public bool? MLSRoster { get; set; }
public bool? MLSBoardMember { get; set; }
public bool? MLSVisible { get; set; }
public bool? MLSFbsBillable { get; set; }
/// Lockbox Section
public bool? SyncWithLockbox { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public LockBoxStatusOption? LockBoxStatusOptions { get; set; }
public string LockBoxOfficeId { get; set; }
public string LockBoxAgentId { get; set; }
public string LockBoxListingAgent { get; set; }
public string LockBoxUserName { get; set; }
public string LockBoxExternalId { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public LockboxMemberType? LockboxMemberType { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public LockboxCardStatus? LockboxCardStatus { get; set; }
public string LockBoxPass { get; set; }
/// Misc
public bool? BrokerReciprocity { get; set; }
public string MemberClass { get; set; }
public string Remarks { get; set; }
public string LocalDesignations { get; set; }
public string RealtorOfficeType { get; set; }
[Hide, Obsolete("Looks like this was added but not needed", true)]
public string BranchType { get; set; }
public bool? DuesWaivedNAR { get; set; }
public bool? DuesWaivedState { get; set; }
public bool? DuesWaivedLocal { get; set; }
public bool StopMail { get; set; } = false;
public bool StopEmail { get; set; } = false;
public bool StopFax { get; set; } = false;
public bool StopJunkMail { get; set; } = false;
public string OfficeFormalName { get; set; }
public string AffiliationId { get; set; }
public int? AssistantContactRelationshipTypeId { get; set; }
public IEnumerable<NARCodeOfEthicsRecord> CodesOfEthics { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable<NARCustomFieldValue> CustomFieldValues { get; set; }
public List<int> NRDSOtherActiveAssociations { get; set; }
public NRDSPreferredMailOption? NRDSPreferredMailType { get; set; }
public NRDSPreferredMailOption? NRDSPreferredPublicationType { get; set; }
public NRDSPreferredPhoneOption? NRDSPreferredPhoneType { get; set; }
public NRDSPreferredPhoneOption? NRDSPreferredFaxType { get; set; }
public string NRDSLicenseNumber { get; set; }
public string MemberSalutation { get; set; }
public DateTime? MemberBirthDate { get; set; }
public DateTime? MemberOrientationDate { get; set; }
public string PMMLSID { get; set; }
public string PMMLSOnlineStatus { get; set; }
public string POMLSOnlineStatus { get; set; }
public string SparkIdError { get; set; }
public string MLSActive { get; set; }