# 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

| FIELD             | TYPE   | Description                        |
| ----------------- | ------ | ---------------------------------- |
| primaryIdentifier | string | main field to identify contact by  |
| value             | string | property of the primary identifier |

primary identifier types

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

`Return`

| CODE | RETURN                                 |
| ---- | -------------------------------------- |
| 200  | Duplicate Contact                      |
| 201  | Inserted Contact                       |
| 400  | Primary Identifier specified correctly |

`GET /contacts`

#### Supported query params

| Query Param Name | Type   | Example               |
| ---------------- | ------ | --------------------- |
| 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:

```typescript
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:

```javascript
{ createdAt: 'DESC' }
```

#### Where Types & Examples

The types for our `where` clause parameter is as follows:

```typescript
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:

```javascript
// 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!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.holder.xyz/holder-api/~/revisions/gDfIZFl5wGxV0PcmHORf/api-reference/contacts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
