Calling createContact() with logged in user returns 403 error

Hi @motiejus-juodelis ,

Ok sure, but please be careful on other pages, this is already a live site, just hidden this page though. https://www.atticus.ph/get-a-quote. So in that page, Select the following for filters: Position = Consultant, Currency = USD, Experience = Senior. A record will appear in the repeater, click the “Send me a Quote” button for a lightbox to appear.

Now on this part, this is where I encounter the error. What I am trying to test is actually for existing contacts. (I use my email to test), so what you can do is submit the form once, and then re-submit the form using the same email, and you will encounter the error. This use case is what I am testing because in this page some users will be able to submit more than once.

Let me know if any questions.

Hi @paoloabarcelona I was able to replicate the issue. Looks like we are getting this when we try to update existing site member contacts, otherwise it works fine. I’ll double check with product people if it is a bug or expected behaviour and let you know.

Thanks for pointing this to us!

Hi @motiejus-juodelis ,

No problem - and thank you! Finally some action from you guys, I’ll be glad to wait f or any resolutions on this one.

Thanks!

I saw the following information in the browser debug console when trying to run createContact from the backend using wix-crm-backend. I find the RequiredRole: Anonymous User quite interesting.

wixCodeNamespacesAndElementorySupport.min.js:3 Uncaught (in promise) Error: server responded with 403 - {“message”:“permission_denied, details: {"RequiredRole":"Anonymous User","GivenIds":"50a4b8aa-1eea-…, 50a4b8aa-1eea-…"}”,“details”:{“givenids”:“50a4b8aa-1eea-…, 50a4b8aa-1eea-…”,“requiredrole”:“Anonymous User”,“error”:“permission_denied”,“category”:“api_error”}}
at Object. (wix-crm-backend/src/contacts.ts:58:11)
at Generator.throw ()
at rejected (wix-crm-backend/dist/src/contacts.js:5:65)
at bound (domain.js:301:14)
at runBound (domain.js:314:12)
at tryCatcher (node_modules/bluebird/js/main/util.js:26:23)
at Promise._settlePromiseFromHandler (node_modules/bluebird/js/main/promise.js:510:31)
at Promise._settlePromiseAt (node_modules/bluebird/js/main/promise.js:584:18)
at Async._drainQueue (node_modules/bluebird/js/main/async.js:128:12)
at Async._drainQueues (node_modules/bluebird/js/main/async.js:133:10)
at Immediate.Async.drainQueues (node_modules/bluebird/js/main/async.js:15:14)
at runCallback (timers.js:789:20)
at tryOnImmediate (timers.js:751:5)
at processImmediate [as _immediateCallback] (timers.js:722:5)

Hi @daddyofx ,
Thanks for pointing this out as well, I am getting same error! Checking with product people too and let will update you,

Thanks

Folks:

I have been dealing with this for a while and have a ton of correspondence with the crm team.

Basically the focus for the CRM function is that you manage contacts on the dashboard not on your web page. This is an issue which leads to an API that is difficult to use and has cross dependencies between wix-users and wix-crm that are not managed.

On the issue of the Anonymous User test. This is designed that way because the createContact function is supposed to be used to create contacts on pages were the user is not a member or logged in - basically for mailing lists etc. However the functionality has expanded to include update without thinking about what constitutes an update when you don’t have the key field (‘_id’) of the record (which happens if the user information is added in the dashboard) or, if you do, the key field is ignored by the API.

My proposal to the team is that they need to follow REST API CRUD practises and provide Create, Read, Update and Delete services in the api. These services could be restricted with logged in user privileges where Read, Update and Delete are used (you don’t want random users accessing or changing other people’s records).

Now what this means is that the wix-crm api should really require the user to be a site member before being able to make any changes to their site held information. This would be easy for site admins.

This then raises the question of how wix-users should play with wix-crm.

Well, if a user, who has provided their information for mailing list purposes which was added to the crm using wix-crm.createContact(), decides they want to alter their information then they should become a site member and register. This means that the wix-user API register() or promptLogin(contactInfo, {‘mode’:‘signup’}) functions should look for an existing CRM entry with a corresponding primary email address and allow the the user to register an account against that CRM record. This should be done using email validation and would ensure unique CRM records are managed in the wix-crm.

At the moment if you register a site member using wix-users you will generate at least one duplicate record that is not connected to the original crm contact.

Now when it comes to sending emails to the user this means you may have two contactIds one obtained from the createContact() function and one created from the register() function. Because these ids are from wix-crm you should be able to use either to send email BUT you should choose the correct API based on whether the user is logged in or not.

If the user is logged in then you MUST use the wix-users.emailUser() function to send triggered emails. If you are not logged in then you MUST use the wix-crm.emailContact() function.

Now the last thing is that neither wix-users or wix-crm gives you a way to access the contact record (the CRUD API doesn’t exist). So the only way to do that is to keep contact data in your own data collection and duplicate information in the crm. This isn’t a huge issue unless data you keep data in your data collection is changed in the CRM using the dashboard. Now you are in a situation where you need to manually fix the data collection. This problem aside, there is a benefit in having data managed in the site app, you can and should, manipulate contact data in backend code which is more secure.

So how would I deal with site users at this point in time given these challenges?

I would not use the wix-crm API except for perhaps the wix-crm.emailContact() function.

Since the wix-users api provides for register and login and that API manages user information in the CRM I would use wix-users register() with a dummy password to create an initial crm record and just make the user a site member. They don’t need to know they are a member if all you are doing is capturing email information for mailing list purposes.

Put other member information that you want to track in your own data collection. Now you have a crm id that you can use for triggeredEmails. If the user comes back and wants to change their information then they are presented with the login screen and can use recover password to complete their site membership and access their data.

Steve

Hi Steve,

So pointing out that the API is difficult to use, what could be a possible solution you can provide? By the way, we don’t have site users. Most of them are anonymous who just signs up to our contact form (for news letters)

@paoloabarcelona Hi there OK I hav e some feedback for you.

I have looked at your site and noted a couple of things. At the moment it doesn’t allow new email addresses to be used for quoting. All you do is take and update a pre-existing record.

I think you should think through the requirements a little more. Here is a proposal that should get you passed the current hurdle.

IF all you want to do is use the email for sending triggered emails for quotes then there is a simple solution.

ONLY createContact if you don’t already have a record of the email in your ContactList data collection. Otherwise the contactId that you have stored for the email in your local data collection should do the trick.

ere is a proposal for the code you need. Notice I have tried to simplify the code by leveraging the power of Promises. Let me know if this makes sense.

$w("#submitForm").onClick( (event) => {
 /***********************************
 * TODO: Check ContactList table if email exists. 
 * If it doesn't, create a new contact in wixCRM
 * Get the ID of the new contact in wixCRM, create new row in the ContactList Table
 * Then start sending the email.. populate the fields by the ID. 
 ************************************/
 // check if there is passed data, if there's none, close this lightbox
 let bValid = true,
 guestName = $w("#name").value,
 guestCompany = $w("#companyName").value,
 guestEmail = $w("#email").value,
 guestPhone = $w("#phone").value,
 jobPost = null;
 // hide initially
 $w("#errorMessage").hide(); 
 // empty required fields?
 if (guestCompany === '' || guestEmail === '') {
     bValid = false;
 }
 // Any errors?
 if (!bValid) {
     // show error messages..
     $w("#errorMessage").show();
     return false;
 }
 // Any item Data?
 if (itemData.data && itemData.data.itemId) {
     wixData.get("JobList", itemData.data.itemId)
     .then( (results) => {
         // initialize
         jobPost = results; //see item below
         console.log(guestEmail);
         // check if contact exists
         // We return the result so we can propagate exceptions to the outer promise handler 
         // and use a single catch handler.
         return wixData.query("ContactList")
         .eq("email", guestEmail)
         .find()
         .then( (contactList) => {
 // We will use a Promise trick to allow for more simple code
 // keepMyPromise allows one of multiple responses to be sent to the next then
 // in sequence
             let keepMyPromise = Promise.resolve({});
             let contacts = contactList.items;
 // We should only see zero or one record with the guestEmail address
             if (contacts.length === 0) {
 // We haven't seen this one before so let's try add it to our CRM
 // then we can send a triggered email to the user
 // Set up keepMyPromise which, if successful should create the contact AND
 // save a new record to the "ContactList". The save result will be a new
 // "ContactList" item which we need to send our email
                 keepMyPromise = wixCRM.createContact({
                     "name": guestName,
                     "emails": [guestEmail],                             
                     "company": guestCompany,
                     "phones": [guestPhone],
                     "interest_area": jobPost.title,
                 })
                 .then((contactId) => {
 // CRM update successful
 // create new contactList entry
                     let newRow = {
                         title: guestEmail,
                         contact_id: contactId,
                         name: guestName,
                         phone: guestPhone,
                         company: guestCompany,
                         email: guestEmail
                     };
 // create a new SentQuoteList row. Return the promise so it can be used in
 // The outer .then() handler
                     return wixData.save("ContactList", newRow);
                 });
             } else if (contacts.length === 1) {
 // We have a record for this email already so we don't need to do anything 
 // other then pass the "ContactList" item to the email processing code
 // This requires a Promise
                 let existingContactRecord = contacts[0];
                 keepMyPromise = Promise.resolve(existingContactRecord);
             }
 // We don't need another else as we will end up returning an empty object if either of the
 // Previous tests fail
             return keepMyPromise; // Pass the contact item (if there is one) to our email handler
         })
         .then( (contactItem) => {
 // Data should have been successfully saved 
             if (Object.values(contactItem).length === 0) {
 // We didn't get a record from the previous processing - this can only happen if
 // the createContact fails due to a duplication
 // we find more than one record with the same email address in the "ContactList"
 // data collection - which cannot happen!
 // We will throw an error here to force the catch in the outer promise handler
                 throw Error('ERROR: CRM Record for email already exists OR internal server error');
             }
 
 // We have valid data
 // now we can send out the email
 // Get the contact id from the contact item
             let contactId = contactItem.contactId;
             if (!contactId) {
 // We will throw an error here which will force catch in our 
 // outer promise handler
                 throw Error("ERROR: ContactList record missing a CRM contact ID");
             }
 // We have a contactId so we can send the email! Return the promise result 
 // to the outer promise handler so we have a single catch to worry about

 // newsletter_signup below is the title of the triggered email
             return wixCRM.emailContact("quote_email", contactId, {
                 "variables": {
                     "title": jobPost.title,
                 }
             }).then(() => {
 // do something after the email was sent
                  let newSentRow = {
                        title: jobPost.title,
                        item_id: jobPost._id,
                        contact_id: contactId
                  };
 // create a new SentQuoteList row.
 // We return the save because that way we can delegate any exception
 // catches to the outer promise handler
                  return wixData.save("SentQuoteList", newSentRow)
                  .then( (results) => {
 // do redirect, or close the lightbox or show a message
                       console.log('sent details saved, email sent. (new contact)');
                 } );    
             }); 
         });
     })
     .catch( (error) => {
          let errorMsg = error.message;
          console.log("error: "+errorMsg);
          if (error.stack) {
              console.log("stack: "+error.stack);
          }
     });
  }

});

Hopefully this helps sort things out.

Steve

@stcroppe ok thanks! Will have a try and let you know how it’ll go.

@stcroppe Hi Steve,

This works - I am able to get through the creation/identification of existing contact successfully but now encountering this issue in the part where we send the email:

{message: “contactId does not match current session contact”, details: {errorcode: “-5003”}}
details: {errorcode: “-5003”}
errorcode: “-5003”
message: “contactId does not match current session contact”

Although the correct contact Id has been passed, it looks like the emailContact() is looking for a special type of promise?

@paoloabarcelona Hi Paolo:

Here is where you also need to understand how preview and published work ;-).

It can be difficult to test CRM and USER code because the preview environment and published environment are different.

When previewing code since you are logged into wix (you have to be to access the editor), then you will be prevented from using wix-crm.emailContact as it is only available to Visitors to the site (I don’t understand why completely but there is probably a reason). You should be able to use wix-users.emailUser however as it takes the same contactId to select a user email from the CRM database. To test the wix-crm.emailContact function you need to test using your published site. Just remember to NOT be logged in. Then it should work. Basically a good way to do this is to use wis-users.currentUser and determine if isLoggedIn is true or not. if it is true then use emailUser else use emailContact.

Hope that helps.
Steve

@stcroppe Hmm, that’s weird… I tested in an incognito browser where I am not logged into wix… any thoughts?

@paoloabarcelona If you use incognito browser and are using the Editor then you are still likely to be logged in. Incognito mode simply removes your browsing information and cookies when you close the incognito window removing any trace that you were there. However while you are accessing the web you will still have cookies loaded by the browser if the site needs it.
The important thing is that the cookies etc. are not saved to disk when you exit the browser and so they are discarded. But while the incognito window is open it still largely operates as a regular browser.

@stcroppe I know how it works - but thank you for explaining, I cleared everything in my browser, cookies, cache, everything - even history, still encountering that error.

@paoloabarcelona Hi Paolo: I walked through the lightbox in debug mode using chrome developer tools and got this result…

sent details saved, email sent. (new contact)


I then clicked on the Send Quote Button again and the same thing happened. So the logic necessary to send a quote as new visitor without a record in the database and the logic for a returning user with a pre-exiting record in the database seems to be working fine.

Now we know I am not logged in because I haven’t registered to your site. OK.

Have you tried to walk through the code using Chrome debug tools? I think Wix thinks you are still logged in or you are seeing some other error. Again don’t try to test this from the editor in Preview as it probably won’t work test using the Published page.

Steve

@stcroppe weird, I even tested with a new contact.

This doesn’t add up since we don’t provide logins… everyone is basically identified as guests.

@stcroppe can you please show a screenshot similar to what I sent?

@paoloabarcelona Here you go:


Here’s a recording of the debug session walking through the code.

Any follow up on this? Still do not understand why documentation is out of date that get a 403 error when run the “insertContact” for the second time with the exact same contact information.

From Documenation "Calling createContact() performs one of the following. (The contact information specified in the contactInfo parameter matches an existing contact if it contains an email address or phone number from an existing contact.)

  • If there is no matching existing contact, a new contact is created using the information specified using the contactInfo parameter.

  • If there is a matching existing contact, it is updated with the information specified using the contactInfo parameter. Any existing contact information that is not explicity overriden in the contactInfo parameter retains its existing value." ( https://www.wix.com/corvid/reference/wix-crm-backend.html#createContact ).

Maintaining another collection with the contact information is just counter-productive since would have to make sure listen if the contact was updated to update the email/phone. Why isn’t there a query for email/phone from the crm contacts table? There has to be some logic already built in since you can query the contact information from the Contact List section on the dashboard. There seems to be a rest endpoint this calls: https://www.wix.com/contacts-server/api/v1/metasite//queryContactsWithFilter?filterQuery=%7B%22conditions%22:,%22query%22:%22SEARCHTEXT%22%7D&sort=[%7B%22field%22:%22lastActivity%22,%22direction%22:%22descending%22%7D]

Please add your question as a new forum post with a link to refer back to this post if needed, instead of bumping up an old thread from 2018.

Add your used code in a code block along with any screenshots that will help explain your issue.

Old post being closed.