Customers
Manage customer information and relationships
Overview
Customers are the core entity representing clients who receive pool services. They can be residential or commercial, and include contact information, service addresses, and billing details.
List Customers (Cursor-Based)
Retrieve a cursor-based paginated list of customers with optional filtering and search. Maximum of 100 records per request. Uses a selector argument that wraps both filters and search.
query InfiniteCustomers(
$selector: CustomersSelector
$sort: [CustomersSort]
$first: Int
$after: String
$last: Int
$before: String
) {
infiniteCustomers(
selector: $selector
sort: $sort
first: $first
after: $after
last: $last
before: $before
) {
edges {
node {
id
firstName
lastName
email
phoneNumber
status
streetAddress
city
state
tags
isCommercial
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
# Variables
{
"first": 20,
"selector": {
"search": "Smith",
"filters": {
"status": { "eq": "ACTIVE" }
}
}
}Response:
{
"data": {
"infiniteCustomers": {
"edges": [
{
"node": {
"id": "cust_456",
"firstName": "John",
"lastName": "Smith",
"email": "john@example.com",
"phoneNumber": "555-0123",
"status": "ACTIVE",
"streetAddress": "123 Main St",
"city": "Austin",
"state": "TX",
"tags": ["Premium"],
"isCommercial": false
},
"cursor": "eyJpZCI6ImN1c3RfNDU2In0="
}
],
"pageInfo": {
"hasNextPage": true,
"endCursor": "eyJpZCI6ImN1c3RfNDU2In0="
},
"totalCount": 150
}
}
}List Customers (Offset-Based)
Retrieve an offset-based paginated list of customers. Maximum of 100 records per request. Uses limit and offset for pagination.
query PaginatedCustomers(
$selector: CustomersSelector
$sort: [CustomersSort]
$limit: Int
$offset: Int
) {
paginatedCustomers(
selector: $selector
sort: $sort
limit: $limit
offset: $offset
) {
items {
id
firstName
lastName
email
phoneNumber
status
streetAddress
city
state
}
totalCount
}
}
# Variables
{
"limit": 20,
"offset": 0,
"selector": {
"filters": {
"status": { "eq": "ACTIVE" },
"zone": { "eq": "North" }
}
}
}Response:
{
"data": {
"paginatedCustomers": {
"items": [
{
"id": "cust_456",
"firstName": "John",
"lastName": "Smith",
"email": "john@example.com",
"phoneNumber": "555-0123",
"status": "ACTIVE",
"streetAddress": "123 Main St",
"city": "Austin",
"state": "TX"
}
],
"totalCount": 45
}
}
}Get Single Customer
There is no dedicated single-customer query. To retrieve a single customer by ID, use infiniteCustomers with an id filter in the selector.
query GetCustomerById($selector: CustomersSelector) {
infiniteCustomers(selector: $selector, first: 1) {
edges {
node {
id
firstName
lastName
email
phoneNumber
alternatePhoneNumber
streetAddress
city
state
zipCode
billingAddress
billingCity
billingState
billingZipCode
status
tags
isCommercial
notes
salesNotes
zone
paymentMethod
billingMethod
billingFrequency
quickbooksId
stripeId
customFields
}
}
}
}
# Variables
{
"selector": {
"filters": {
"id": { "eq": "cust_456" }
}
}
}Response:
{
"data": {
"infiniteCustomers": {
"edges": [
{
"node": {
"id": "cust_456",
"firstName": "John",
"lastName": "Smith",
"email": "john@example.com",
"phoneNumber": "555-0123",
"alternatePhoneNumber": "555-0124",
"streetAddress": "123 Main St",
"city": "Austin",
"state": "TX",
"zipCode": "78701",
"billingAddress": null,
"billingCity": null,
"billingState": null,
"billingZipCode": null,
"status": "ACTIVE",
"tags": ["Premium"],
"isCommercial": false,
"notes": "Prefers morning appointments",
"salesNotes": null,
"zone": "North",
"paymentMethod": "CREDIT_CARD",
"billingMethod": "EMAIL",
"billingFrequency": "MONTHLY",
"quickbooksId": "QB-12345",
"stripeId": "cus_abc123",
"customFields": null
}
}
]
}
}
}Customer Lookup (Autocomplete)
Search for customers by name or other fields using the autocomplete lookup query. Useful for search-as-you-type interfaces.
query InfiniteCustomerLookup(
$query: String!
$first: Int
$after: String
) {
infiniteCustomerLookup(
query: $query
first: $first
after: $after
) {
edges {
node {
id
firstName
lastName
streetAddress
city
state
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
# Variables
{
"query": "Smi",
"first": 10
}Response:
{
"data": {
"infiniteCustomerLookup": {
"edges": [
{
"node": {
"id": "cust_456",
"firstName": "John",
"lastName": "Smith",
"streetAddress": "123 Main St",
"city": "Austin",
"state": "TX"
},
"cursor": "eyJpZCI6ImN1c3RfNDU2In0="
},
{
"node": {
"id": "cust_789",
"firstName": "Sarah",
"lastName": "Smithson",
"streetAddress": "789 Elm Dr",
"city": "Dallas",
"state": "TX"
},
"cursor": "eyJpZCI6ImN1c3RfNzg5In0="
}
],
"pageInfo": {
"hasNextPage": false,
"endCursor": "eyJpZCI6ImN1c3RfNzg5In0="
}
}
}
}Search
The search field within the CustomersSelector accepts a string (max 600 characters) and searches across: firstName, lastName, streetAddress, city, state, zipCode, phoneNumber, alternatePhoneNumber, tertiaryPhoneNumber, and fourthPhoneNumber. Multi-word searches will also attempt first + last name combinations.
query SearchCustomers($selector: CustomersSelector, $first: Int) {
infiniteCustomers(selector: $selector, first: $first) {
edges {
node {
id
firstName
lastName
phoneNumber
streetAddress
city
state
}
}
totalCount
}
}
# Variables
{
"first": 20,
"selector": {
"search": "John Smith"
}
}Response:
{
"data": {
"infiniteCustomers": {
"edges": [
{
"node": {
"id": "cust_456",
"firstName": "John",
"lastName": "Smith",
"phoneNumber": "555-0123",
"streetAddress": "123 Main St",
"city": "Austin",
"state": "TX"
}
}
],
"totalCount": 1
}
}
}Create Customer
Create a new customer record.
mutation CreateCustomer($input: CreateCustomerInput!) {
createCustomer(input: $input) {
id
firstName
lastName
email
status
}
}
# Variables
{
"input": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"phoneNumber": "555-0200",
"streetAddress": "456 Oak Ave",
"city": "Austin",
"state": "TX",
"zipCode": "78702",
"status": "ACTIVE",
"tags": ["Residential"]
}
}Response:
{
"data": {
"createCustomer": {
"id": "cust_457",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"status": "ACTIVE"
}
}
}Update Customer
Update an existing customer.
mutation UpdateCustomer($id: ID!, $input: UpdateCustomerInput!) {
updateCustomer(id: $id, input: $input) {
id
firstName
lastName
email
phoneNumber
status
}
}
# Variables
{
"id": "cust_456",
"input": {
"phoneNumber": "555-0999",
"notes": "Updated contact preference: email only"
}
}Response:
{
"data": {
"updateCustomer": {
"id": "cust_456",
"firstName": "John",
"lastName": "Smith",
"email": "john@example.com",
"phoneNumber": "555-0999",
"status": "ACTIVE"
}
}
}Update Customer Quoted Prices
Update service pricing (quoted prices) for a customer. Each quoted price associates a service type with a price. Pass the full list of quoted prices — existing entries will be replaced. Entries with a price of 0 or less are ignored. typeId is optional and refers to a service type ID.
mutation UpdateCustomerQuotedPrices(
$customerId: ID!
$input: UpdateCustomerQuotedPricesInput!
) {
updateCustomerQuotedPrices(
customerId: $customerId
input: $input
) {
id
customerId
typeId
price
serviceTypeDisplay
}
}
# Variables
{
"customerId": "cust_456",
"input": {
"quotedPrices": [
{ "typeId": "type_weekly", "price": 150.00 },
{ "typeId": "type_opening", "price": 350.00 },
{ "typeId": "type_closing", "price": 300.00 }
]
}
}Response:
{
"data": {
"updateCustomerQuotedPrices": [
{
"id": "1",
"customerId": "cust_456",
"typeId": "type_weekly",
"price": 150.00,
"serviceTypeDisplay": "Weekly Service"
},
{
"id": "2",
"customerId": "cust_456",
"typeId": "type_opening",
"price": 350.00,
"serviceTypeDisplay": "Pool Opening"
},
{
"id": "3",
"customerId": "cust_456",
"typeId": "type_closing",
"price": 300.00,
"serviceTypeDisplay": "Pool Closing"
}
]
}
}Bulk & Export Operations
Creating, updating, deleting, exporting, emailing, or texting many customers at once is handled asynchronously through the Bulk Operations API. These jobs run in the background and report progress you can poll. See the Bulk Operations guide for the full workflow.
Field Reference
| Field | Type | Description |
|---|---|---|
id | ID! | Unique identifier |
firstName | String(nullable) | Customer's first name |
lastName | String! | Customer's last name |
quickBooksName | String(nullable) | Name used in QuickBooks |
customerSinceDate | Date(nullable) | Date the customer relationship started |
streetAddress | String(nullable) | Street address |
city | String(nullable) | City |
state | String(nullable) | State/Province |
zipCode | String(nullable) | Postal code |
billingAddress | String(nullable) | Billing street address |
latitude | Number(nullable) | Latitude coordinate |
longitude | Number(nullable) | Longitude coordinate |
phoneNumber | String(nullable) | Primary phone number |
phoneNumberLabel | String(nullable) | Label for primary phone (e.g. Home, Work) |
phoneNumberEnableSMS | Boolean(nullable) | Whether SMS is enabled for primary phone |
alternatePhoneNumber | String(nullable) | Alternate phone number |
alternatePhoneNumberLabel | String(nullable) | Label for alternate phone |
alternatePhoneNumberEnableSMS | Boolean(nullable) | Whether SMS is enabled for alternate phone |
tertiaryPhoneNumber | String(nullable) | Third phone number |
tertiaryPhoneNumberLabel | String(nullable) | Label for third phone |
tertiaryPhoneNumberEnableSMS | Boolean(nullable) | Whether SMS is enabled for third phone |
fourthPhoneNumber | String(nullable) | Fourth phone number |
fourthPhoneNumberLabel | String(nullable) | Label for fourth phone |
fourthPhoneNumberEnableSMS | Boolean(nullable) | Whether SMS is enabled for fourth phone |
email | String(nullable) | Primary email address |
alternateEmail | String(nullable) | Alternate email address |
tertiaryEmail | String(nullable) | Third email address |
fourthEmail | String(nullable) | Fourth email address |
billingEmail | String(nullable) | Email for billing communications |
billingCCEmail | String(nullable) | CC email for billing communications |
status | String(nullable) | Customer status (e.g. ACTIVE, INACTIVE, LEAD) |
tags | [String](nullable) | Tags for categorization |
paymentMethod | String(nullable) | Preferred payment method |
billingMethod | String(nullable) | Billing method |
contactMethod | String(nullable) | Preferred contact method |
zone | String(nullable) | Service zone |
reportNotification | String(nullable) | Report notification preference |
paymentType | String(nullable) | Payment type |
pool | CustomerPoolEntity(nullable) | Associated pool information |
bodiesOfWater | [BodyOfWaterEntity](nullable) | Associated bodies of water |
service | CustomerServiceEntity(nullable) | Service configuration |
salesNotes | String(nullable) | Sales-related notes |
notes | String(nullable) | Internal notes |
readyForInvoice | Boolean(nullable) | Whether customer is ready for invoicing |
isCommercial | Boolean(nullable) | Whether this is a commercial customer |
billingCity | String(nullable) | Billing city |
billingState | String(nullable) | Billing state |
billingZipCode | String(nullable) | Billing postal code |
billingFirstName | String(nullable) | Billing contact first name |
billingLastName | String(nullable) | Billing contact last name |
useAddressForBilling | Boolean(nullable) | Whether to use service address for billing |
billingFrequency | String(nullable) | Billing frequency |
stripeId | String(nullable) | Stripe customer ID |
quickbooksId | String(nullable) | QuickBooks customer ID |
quotedPrices | [CustomerQuotedPriceEntity](nullable) | Quoted prices for the customer |
customFields | JSON(nullable) | Custom field values |
nextScheduledApt | AppointmentEntity(nullable) | Next scheduled appointment |
Filter Reference
Available fields for the CustomerFilter object within CustomersSelector.filters.
| Field | Type | Description |
|---|---|---|
id | IdFilter(nullable) | Filter by customer ID |
status | EnumFilter(nullable) | Filter by status |
isCommercial | BooleanFilter(nullable) | Filter by commercial flag |
readyForInvoice | BooleanFilter(nullable) | Filter by invoice readiness |
zone | StringFilter(nullable) | Filter by service zone |
city | StringFilter(nullable) | Filter by city |
state | StringFilter(nullable) | Filter by state |
zipCode | StringFilter(nullable) | Filter by postal code |
tags | ScalarListFilter(nullable) | Filter by tags |
customerSinceDate | DateTimeFilter(nullable) | Filter by customer since date |
deleted | BooleanFilter(nullable) | Filter by deleted status (defaults to false) |