How to get an array of subscribed contacts from Wix Contacts CRM

Hi,

I’m currently querying

import { contacts } from “wix-crm-backend”;

I have an array of returned items but I’m struggling to filter this to only “SUBSCRIBED” contacts from the field
emailSubscriptions.subscriptionStatus

My code is below and working apart from returning only the id

Backend Code:

import { contacts } from "wix-crm-backend";

export async function getSubscribedContacts(options) {
  try {
   
    const queryResults = await contacts.queryContacts().find(options);
    
    console.log("Raw Contacts Data: ", JSON.stringify(queryResults.items, null, 2));

    const allContacts = queryResults.items.map(contact => {
    
      console.log("Full Contact Object: ", JSON.stringify(contact, null, 2));

    
      const subscriptionStatus = contact.info.extendedFields.emailSubscriptions?.subscriptionStatus;

     
      return {
        id: contact._id,
        subscriptionStatus: subscriptionStatus,
        emailSubscriptions: contact.info.extendedFields.emailSubscriptions?.subscriptionStatus
      };
    });

  
    console.log("All Contacts Subscription Status: ", allContacts);

    return allContacts; 
  } catch (error) {
    console.error("Error fetching contacts:", error);
    throw new Error("Failed to query contacts.");
  }
}

Frontend code:

import { getSubscribedContacts } from "backend/contacts.jsw";

$w.onReady(async function () {
  const options = {
    suppressAuth: true
  };

  try {
    const results = await getSubscribedContacts(options);
    console.log("Subscribed Contacts: ", results);
  } catch (error) {
    console.error("Error fetching subscribed contacts:", error);
  }
});

So I have an array of contacts but I’m struggling to achieve only subscribed. I can sort this by using a tag but want to access the subscriptionStatus “SUBSCRIBED” to show only this match.

I only get the _id returned from the backend code without the other two fields

 return {
        id: contact._id,
        subscriptionStatus: subscriptionStatus,
        emailSubscriptions: contact.info.extendedFields.emailSubscriptions?.subscriptionStatus
      };

Is this possible? Thanks for any help provided

To understand the issue, I would verify that the extendedFields object is defined in every contact object returned by queryContacts()

It is possible it is undefined and therefore not being returned in the array of contacts created by .map(). You can use Wix Logs in combination with console.log() to understand what is occuring.

Try to get this to work…

import { contacts } from "wix-crm-backend";

export async function getSubscribedContacts(options) {
  try {
    const queryResults = await contacts.queryContacts().find(options);
    
    console.log("Raw Contacts Data: ", JSON.stringify(queryResults.items, null, 2));

    // Iterate through contacts and log their structure
    const allContacts = queryResults.items.map(contact => {
      // Log the entire contact object to understand its structure
      console.log("Full Contact Object: ", JSON.stringify(contact, null, 2));

      // Safely access extendedFields and emailSubscriptions to check if they exist
      const extendedFields = contact.info?.extendedFields;
      if (!extendedFields) {
        console.warn(`Contact ID ${contact._id} has no extendedFields`);
      }

      const emailSubscriptions = extendedFields?.emailSubscriptions;
      if (!emailSubscriptions) {
        console.warn(`Contact ID ${contact._id} has no emailSubscriptions`);
      }

      const subscriptionStatus = emailSubscriptions?.subscriptionStatus;

      return {
        id: contact._id,
        subscriptionStatus: subscriptionStatus,
        emailSubscriptions: subscriptionStatus,
      };
    });

    console.log("All Contacts with Subscription Status: ", allContacts);

    // Filter to only include contacts with a "SUBSCRIBED" status
    const subscribedContacts = allContacts.filter(contact => contact.subscriptionStatus === "SUBSCRIBED");

    console.log("Subscribed Contacts: ", subscribedContacts);

    return subscribedContacts; 
  } catch (error) {
    console.error("Error fetching contacts:", error);
    throw new Error("Failed to query contacts.");
  }
}

1 Like

@CODE-NINJA, thanks for the reply.

I’m actually convinced you can’t access the emailSubscriptions.subscriptionStatus

It returns null for this on every item. I’ve simplified the code to show what I mean. Extended field seems to be the last field you can access.

export async function getSubscribedContacts(options) {
  try {
    const queryResults = await contacts.queryContacts().find(options);
    const items = queryResults.items;
    const firstItem = items[0];

    console.log("Check First Item: ", firstItem);
    
    const checkextandedFields = firstItem.info.extendedFields;

console.log("Check Extended Fields: ", checkextandedFields);


    const checkEmailSubscriptions = firstItem.info.extendedFields.emailSubscriptions;


console.log("Check Email Subscriptions: ", checkEmailSubscriptions);

    return items;
  } catch (error) {
    console.error(error);
    
  }
}

console.log below

Check Extended Fields:

{…}

jsonTableCopy JSON

contacts.displayByLastName:
“…”

emailSubscriptions.deliverabilityStatus:
“INACTIVE”

members.membershipStatus:
“APPROVED”

emailSubscriptions.subscriptionStatus:
“SUBSCRIBED”

contacts.displayByFirstName:
“…”

contacts.jsw

Line 64

Check Email Subscriptions: null

We will do a TIME-JUMP back to the past…

This also maybe could give you more informations…

And here, this one deals also with SUBSCRIPTION-STATUS…

In the past it was easier, now there are things like …

  1. ELEVATE-FUNCTIONS
  2. webMethod(Permissions.Anyone, () => {});
  3. and still old versions, like —>
const options = {
    suppressAuth: true
  };

Check all these options / functions to get maybe right permission.

You have your code in front of you → use more console-logs and inspect every little step.

And this one also maybe interesting…

Stephen,

You have stumbled across one of the most ludicrous things I have found in the Velo API, and I have found many.

A contact looks like this, as you know:

{
  "_createdDate": "Tue Dec 19 2023 12:19:40 GMT+0000 (Greenwich Mean Time)",
  "_id": "a8456cf4-e9d5-444f-97a1-4510f1a8b52f",
  "info": {
    "emails": [
      {
        "_id": "f5f3f90a-fcea-42cb-9b16-c4e78127d6f1",
        "tag": "UNTAGGED",
        "email": "ergger@yyyy.co.uk",
        "primary": true
      }
    ],
    "extendedFields": {
      "contacts.displayByLastName": "XXXXXX YYYYY",
      "invoices.vatId": "",
      "emailSubscriptions.deliverabilityStatus": "VALID",
      "members.membershipStatus": "APPROVED",
      "emailSubscriptions.subscriptionStatus": "NOT_SET",
      "emailSubscriptions.effectiveEmail": "xxxx@yyyyy.co.uk",
      "contacts.displayByFirstName": "YYYYY XXXXXX"
    },
    "name": {
      "first": "YYYYY",
      "last": "XXXXXX"
    },
    "picture": {
      "image": "https://images-wixmp-7ef3383b5fd80a9f5a5cc686.wixmp.com/171d9db8-7f64-446f-b290-37b9121d5b9c/1599229843526-IMG_1811.jpeg/v1/fill/w_320,h_320/file.jpg"
    },
    "profilePicture": "https://images-wixmp-7ef3383b5fd80a9f5a5cc686.wixmp.com/171d9db8-7f64-446f-b290-37b9121d5b9c/1599229843526-IMG_1811.jpeg/v1/fill/w_320,h_320/file.jpg"
  },
  "lastActivity": {
    "activityDate": "Fri Sep 27 2024 10:53:16 GMT+0100 (British Summer Time)",
    "activityType": "MEMBER_LOGIN",
    "date": "Fri Sep 27 2024 10:53:21 GMT+0100 (British Summer Time)",
    "description": "Logged in to your site",
    "icon": {
      "name": "UserJoin",
      "url": "https://wixmp-8be454c954980f083caba37c.wixmp.com/activity-log/UserJoin.png"
    }
  },
  "primaryInfo": {
    "email": "xgserg@yyyyyy.co.uk"
  },
  "revision": 48,
  "source": {
    "appId": "eb377299-86b4-4a86-a1b5-774a2d1d374b",
    "sourceType": "WIX_SITE_MEMBERS"
  },
  "_updatedDate": "Sun Sep 29 2024 08:21:52 GMT+0100 (British Summer Time)"
}

In the above JSON, you can see that the extended fields are not fully nested JSON objects and cannot be accessed as you have tried. You will find you can access them as follows:

firstItem.info.extendedFields.["emailSubscriptions.subscriptionStatus"]

Give this a try. If it works, then maybe you can persuade Code_Ninja to make a run at getting this referenced more clearly in the API.

Simon.

2 Likes

@SimonP, thank you for taking the time to comment on this. You solution has indeed worked! As you say this isn’t very clear from the API and could have saved a bit of troubleshooting.

For anyone else that it may help here is the final code I used to get a full list of people who have subscribed.

Backend

export async function getSubscribedContacts(options) {
  try {
    const queryResults = await contacts.queryContacts().limit(500).find(options);
    
    console.log("Raw Contacts Data: ", JSON.stringify(queryResults.items, null, 2));

    // Filter and map contacts in one step
    const subscribedContacts = queryResults.items
      .map(contact => {
        const subscriptionStatus = contact.info?.extendedFields?.["emailSubscriptions.subscriptionStatus"];
        return subscriptionStatus === "SUBSCRIBED" ? { id: contact._id, subscriptionStatus } : null;
      })
      .filter(contact => contact !== null);  // Remove null values

    console.log("Subscribed Contacts: ", subscribedContacts);

    return subscribedContacts;
  } catch (error) {
    console.error("Error fetching contacts:", error);
    throw new Error("Failed to query contacts.");
  }
}

Frontend

import { getSubscribedContacts } from “backend/contacts.jsw”;

$w.onReady(async function () {
    const options = {
        suppressAuth: true
    };

    try {
        const results = await getSubscribedContacts(options);
        console.log("Subscribed Contacts: ", results);
    } catch (error) {
        console.error("Error fetching subscribed contacts:", error);
    }
});

I hope this helps someone else in the future. Thanks to @SimonP
(@thomasj, Can the API be updated to show this more clearly?)

You are welcome Stephen. I have had an ENORMOUS amount of help on here over the last few years and it’s nice to be able to help someone else out for a change. :grinning:

1 Like

@thomasj
I think Thomas will know what to do…