Contacts

Our most robust API is our contacts query. It supports a wide variety of query capabilities for data around our contact model.

POST /contacts

Body Structure

FIELDTYPEDescription

primaryIdentifier

string

main field to identify contact by

value

string

property of the primary identifier

primary identifier types

enum PrimaryIdentifier {
  WALLET_ADDRESS = 'walletAddress',
  EMAIL = 'email',
  PHONE_NUMBER = 'phoneNumber',
  DISCORD = 'discord',
}

Return

CODERETURN

200

Duplicate Contact

201

Inserted Contact

400

Primary Identifier specified correctly

GET /contacts

Supported query params

Query Param NameTypeExample

limit

number

15 (default), max 100

page

number

0 (default)

sort

Object

See below

where

Object

See below

Sort Types & Examples

The types for our sort parameter is as follows:

enum SortDirection {
  ASC,
  DESC,
}

interface ContactSortInput
  extends Record<string, SortDirection | undefined> {
  numberOfNFTs?: SortDirection;
  balance?: SortDirection;
  createdAt?: SortDirection;
  email?: SortDirection;
  twitterHandle?: SortDirection;
  discordUsername?: SortDirection;
}

We currently only support passing in a single sort value. If you pass in multiple, your results may vary or be inconsistent. A good example (and the default for the table in our UI) can be found below:

{ createdAt: 'DESC' }

Where Types & Examples

The types for our where clause parameter is as follows:

interface TimestampRange {
  start: number;
  end: number;
}

interface NumberOfContractNFTs {
  contractAddress: string;
  numberOfHolds: number;
}

interface Trait {
  id: string;
  value: string | Array<string>;
}

interface CustomField {
  fieldId: string;
  value: string | boolean;
}

interface ContactWhereInput {
  // Contact filters
  id_eq?: string;
  id_in?: Array<string>;

  email_eq?: string;
  email_neq?: string;
  email_isNull?: boolean;
  email_isNotNull?: boolean;

  phoneNumber_eq?: string;
  phoneNumber_neq?: string;
  phoneNumber_isNull?: boolean;
  phoneNumber_isNotNull?: boolean;

  discordUsername_eq?: string;
  discordUsername_neq?: string;
  discordUsername_isNull?: boolean;
  discordUsername_isNotNull?: boolean;

  twitterHandle_eq?: string;
  twitterHandle_neq?: string;
  twitterHandle_isNull?: boolean;
  twitterHandle_isNotNull?: boolean;

  createdAt_eq?: number;
  createdAt_neq?: number;
  createdAt_gt?: number;
  createdAt_gte?: number;
  createdAt_lt?: number;
  createdAt_lte?: number;
  createdAt_between?: TimestampRange;

  filledForm_eq?: string;
  filledForm_neq?: string;

  tag_eq?: string;
  tag_neq?: string;

  smsSent_eq?: string;
  smsSent_neq?: string;

  smsOptOut_eq?: boolean;
  smsOptOut_neq?: boolean;

  // List filters
  listId_eq?: string;
  listId_neq?: string;

  // Contact custom field filters
  customField_eq?: CustomField;
  customField_neq?: CustomField;
  customField_isNull?: CustomField;
  customField_isNotNull?: CustomField;

  // Wallet filters
  walletAddress_eq?: string;
  walletAddress_neq?: string;
  walletAddress_in?: Array<string>;

  balance_eq?: number;
  balance_neq?: number;
  balance_gt?: number;
  balance_gte?: number;
  balance_lt?: number;
  balance_lte?: number;

  xmtpEnabled_eq?: boolean;
  xmtpEnabled_neq?: boolean;
  xmtpEnabled_isNull?: boolean;
  xmtpEnabled_isNotNull?: boolean;

  // NFT filters
  contractAddress_eq?: string;
  contractAddress_neq?: string;

  tokenId_in?: Array<string>;

  amount_eq?: number;
  amount_neq?: number;
  amount_gt?: number;
  amount_gte?: number;
  amount_lt?: number;
  amount_lte?: number;

  trait_eq?: Trait;
  trait_neq?: Trait;
  trait_in?: Trait;
  trait_nin?: Trait;

  purchasedDate_eq?: number;
  purchasedDate_neq?: number;
  purchasedDate_gt?: number;
  purchasedDate_gte?: number;
  purchasedDate_lt?: number;
  purchasedDate_lte?: number;
  purchasedDate_between?: TimestampRange;

  numberOfNFTs_eq?: number;
  numberOfNFTs_neq?: number;
  numberOfNFTs_gt?: number;
  numberOfNFTs_gte?: number;
  numberOfNFTs_lt?: number;
  numberOfNFTs_lte?: number;

  // Discord filters
  discordJoinDate_eq?: number;
  discordJoinDate_neq?: number;
  discordJoinDate_gt?: number;
  discordJoinDate_gte?: number;
  discordJoinDate_lt?: number;
  discordJoinDate_lte?: number;
  discordJoinDate_between?: TimestampRange;

  discordJoinDateGuild_eq?: number;
  discordJoinDateGuild_neq?: number;
  discordJoinDateGuild_gt?: number;
  discordJoinDateGuild_gte?: number;
  discordJoinDateGuild_lt?: number;
  discordJoinDateGuild_lte?: number;
  discordJoinDateGuild_between?: TimestampRange;

  discordActivity_eq?: number;
  discordActivity_neq?: number;
  discordActivity_gt?: number;
  discordActivity_gte?: number;
  discordActivity_lt?: number;
  discordActivity_lte?: number;
  discordActivity_between?: TimestampRange;

  discordMsgNum_eq?: number;
  discordMsgNum_neq?: number;
  discordMsgNum_gt?: number;
  discordMsgNum_gte?: number;
  discordMsgNum_lt?: number;
  discordMsgNum_lte?: number;

  discordRole_eq?: string;
  discordRole_neq?: string;

  // Recursive filters
  AND?: Array<ContactWhereInput>;
  OR?: Array<ContactWhereInput>;
  NOT?: Array<ContactWhereInput>;

  // Special operators for compound NFT filters
  AND_nft?: Array<ContactWhereInput>;
  OR_nft?: Array<ContactWhereInput>;
  NOT_nft?: Array<ContactWhereInput>;

  // Special operators for compound NFT filter by numberOfHolds
  numberOfContractNFTs_eq?: NumberOfContractNFTs;
  numberOfContractNFTs_neq?: NumberOfContractNFTs;
  numberOfContractNFTs_gt?: NumberOfContractNFTs;
  numberOfContractNFTs_gte?: NumberOfContractNFTs;
  numberOfContractNFTs_lt?: NumberOfContractNFTs;
  numberOfContractNFTs_lte?: NumberOfContractNFTs;
}

This list is evolving, and some might even be missing, but the pattern should be fairly clear for our queries moving forward. Note that it is a recursive structure, but deeply nesting filters will slow down the query. We do not make any specific performance guarantees. Here are a few examples:

// Finds all contacts in your account that have a wallet balance greater than 1 ETH
// and have XMTP (wallet messaging) enabled.
{
  where: {
    AND: [
      {
        balance_gt: 1
      },
      {
        xmtpEnabled_eq: true
      }
    ]
  }
}

// NFT filters are a special case for our recursive AND/OR/NOT stucture. This
// will find all contacts in your account that own a Gold BoredApeYachtClub or
// a Cosmic Moonbirds.
{
  where: {
    OR: [
      {
        AND_nft: [
          {
            contractAddress_eq: "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"
          },
          {
            trait_eq: {
              id: "<our internal id for Fur from traits query>",
              value: "Solid Gold"
            }
          },
          {
            amount_gt: 0
          }
        ]
      },
      {
        AND_nft: [
          {
            contractAddress_eq: "0x23581767a106ae21c074b2276d25e5c3e136a68b"
          },
          {
            trait_eq: {
              id: "<our internal id for Body from traits query>",
              value: "Cosmic"
            }
          },
          {
            amount_gt: 0
          }
        ]
      }
    ]
  }
}

Need Help?

This is a complex query, so it is hard to account for every edge case in our documentation. If you find yourself trying to figure something out but it just isn't working, please reach out and let us know!

Last updated

Revision created on 1/5/2024