API referenceCustomers

Customers

Manage customers, contacts, locations, and tags via the Zoop MCP tool layer.

This page covers every MCP tool for reading and writing customer data in Zoop.

A customer record holds core identity fields plus two child resources: contacts (the people you call or email) and locations (the service addresses you dispatch to). Tags are shared labels — one library per account — that you attach to customers to organize them.

All tools in this group require the read:customers or write:customers scope (a scope is a named permission you grant your API token — see Scopes). Contacts, locations, and tags share those two scopes; they do not have their own.

If you are new to the MCP layer, start at MCP overview to learn how to connect and authenticate.


Scopes

ScopeGrants access to
read:customerscustomers.search, customers.get, contacts.list, contacts.get, locations.list, locations.get, tags.list, tags.get
write:customerscustomers.create, customers.update, customers.archive, contacts.create, contacts.update, contacts.archive, locations.create, locations.update, locations.archive, tags.create, tags.update, tags.delete

write:customers does not imply read:customers. If your integration needs to both read and write, request both scopes.


Customers

customers.search

Search customers by name, phone, tag, or status. Returns a list of matching customers, each with an id you can pass to other tools. If you only have a name or phone number and need the customer's id, start here.

body
q

Free-text search string. Matches against name and phone fields.

body
status

Filter by status. One of active, inactive, or archived.

body
type

Filter by customer type. One of individual or company.

body
tag

Filter to customers that have this tag assigned. Pass the tag id, not the tag name.

body
cursor

Pagination cursor. Copy the nextCursor value from a previous response to fetch the next page. Omit this on your first request.

body
limit

Number of results per page. Integer between 1 and 100.

Example

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "customers.search",
    "arguments": {
      "q": "Nguyen",
      "status": "active",
      "limit": 20
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{"rows":[{"id":"a1b2c3d4-...","display_name":"Hana Nguyen","customer_type":"individual","status":"active","primary_phone":"+15105550123","primary_email":"hana@example.com"}],"nextCursor":null,"hasMore":false}"
      }
    ]
  }
}

customers.get

Fetch one customer's full record by id, including their contacts and locations.

body
id

The customer's id. Get this from customers.search.


customers.create

Create a new customer. The customer_type field determines which other fields are required.

  • For individual: first_name is required.
  • For company: company_name is required.

You cannot change customer_type after creation, so pick the right one up front.

body
customer_type

individual or company.

body
first_name

Given name. Required when customer_type is individual.

body
last_name

Family name. Optional for individuals.

body
company_name

Business name. Required when customer_type is company.

body
phone

Phone number in any common format (e.g. (510) 555-0199). Zoop converts it to E.164 format (e.g. +15105550199) and stores both the original and the converted value.

body
email

Email address.

body
notes_internal

Internal notes visible only to your team. Max 10,000 characters.

body
primary_contact

For company customers only. Creates an initial contact at the same time as the company record.

body
location

Creates an initial service location at the same time as the customer record. See location fields below for the full field list.

Example

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "customers.create",
    "arguments": {
      "customer_type": "individual",
      "first_name": "Marco",
      "last_name": "Reyes",
      "phone": "(510) 555-0199",
      "email": "marco@example.com",
      "location": {
        "address_line1": "482 Oak Street",
        "city": "Oakland",
        "state": "CA",
        "postal_code": "94607"
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{"id":"c9d8e7f6-...","customer_type":"individual","customer_number":"C-0042","display_name":"Marco Reyes","status":"active","primary_phone":"+15105550199","primary_email":"marco@example.com","created_at":"2026-06-13T18:00:00.000Z"}"
      }
    ]
  }
}

customers.update

Update fields on an existing customer. This is a partial update — only the fields you include in the request are changed; everything else stays as-is. You cannot change customer_type.

body
id

The customer to update.

body
first_name

Updated given name (individuals only).

body
last_name

Updated family name (individuals only).

body
company_name

Updated business name (companies only).

body
phone

Updated phone number.

body
email

Updated email address.

body
notes_internal

Updated internal notes.

body
is_vip

Mark or unmark the customer as VIP.

body
do_not_contact

Flag the customer as do-not-contact.

body
do_not_contact_reason

Reason for the do-not-contact flag. Max 500 characters. Pass null to clear.

body
source

How the customer was acquired (e.g. referral, google). Max 64 characters.


customers.archive

Archive a customer. Archived customers are hidden from default list views, but their record and all related history (jobs, invoices, notes) is fully preserved. Zoop does not hard-delete customers — use this instead.

body
id

The customer to archive.


Contacts

A contact is a person associated with a customer — someone you can call or email. Every customer has at least one contact, and the first one added becomes the primary contact automatically.

Every contact belongs to one customer. All contact tools require the parent customer_id so Zoop knows which customer you are working with.


contacts.list

List all contacts for a customer. The primary contact is returned first, then contacts in creation order.

body
customer_id

The parent customer.


contacts.get

Fetch one contact by id.

body
id

The contact's id.

body
customer_id

The parent customer's id. Required — contacts are scoped to a customer.


contacts.create

Add a contact to a customer. first_name and last_name are required. Phone numbers are automatically converted to E.164 format.

If you set is_primary: true and a primary contact already exists, Zoop promotes your new contact and demotes the previous primary — no extra call needed.

body
customer_id

The parent customer.

body
first_name

Given name.

body
last_name

Family name.

body
phone

Phone number. Converted to E.164 format; the original input is also stored.

body
email

Email address.

body
is_primary

Set to true to make this the primary contact. Defaults to false. The first contact on a customer is promoted automatically regardless of this field.

body
phone_can_sms

Whether the phone number can receive SMS. Defaults to true.

body
role

The contact's role. One of billing, service, decision_maker, or general.

body
title

Job title or role description (free text).


contacts.update

Update a contact's fields. This is a partial update — only the fields you include change; everything else stays as-is. You cannot move a contact from one customer to another.

body
id

The contact to update.

body
customer_id

The parent customer's id.

All other fields from contacts.create are accepted as optional updates.


contacts.archive

Archive a contact. Archived contacts are hidden from default views but their history is preserved.

body
id

The contact to archive.

body
customer_id

The parent customer's id.


Locations

A location is a service address tied to a customer — where you send a crew. Every customer can have multiple locations. Each location has a type (service, billing, mailing, or other), and the first location of each type becomes the primary for that type automatically.

Like contacts, locations require the parent customer_id on every call.

To promote a different location to primary, you must do it in two steps: first call locations.update on the current primary with is_primary: false, then call locations.update on the new one with is_primary: true. Trying to create a location with is_primary: true when a primary already exists for that type returns a conflict error.


locations.list

List all service locations for a customer. The primary location comes first, then in creation order.

body
customer_id

The parent customer.


locations.get

Fetch one location by id.

body
id

The location's id.

body
customer_id

The parent customer's id.


locations.create

Add a service location to a customer. address_line1, city, state, and postal_code are required.

body
customer_id

The parent customer.

body
address_line1

Street address line 1.

body
address_line2

Street address line 2 (unit, suite, etc.).

body
city

City name.

body
state

State or province code.

body
postal_code

ZIP or postal code.

body
country

ISO country code. Defaults to US.

body
location_type

One of service, billing, mailing, or other. Defaults to service.

body
label

A human-readable nickname for the location (e.g. Warehouse, Rental unit #3).

body
is_primary

Set to true only when no primary exists yet for this location_type. Defaults to false. See the warning above about promoting an existing location.

body
access_notes

Gate codes, alarm codes, or other access instructions for the crew.

body
parking_notes

Parking or vehicle instructions for the crew.

body
service_notes

Any other notes about the location relevant to service.

body
latitude

Latitude in decimal degrees (−90 to 90). Must be provided together with longitude if either is sent.

body
longitude

Longitude in decimal degrees (−180 to 180). Must be provided together with latitude if either is sent.

body
place_id

The Google Places place_id from address autocomplete, if available.


locations.update

Update a location's fields. This is a partial update — only the fields you include change; everything else stays as-is. You cannot move a location from one customer to another.

body
id

The location to update.

body
customer_id

The parent customer's id.

All other fields from locations.create are accepted as optional updates.


locations.archive

Archive a location. Archived locations are hidden from default views but their history is preserved.

body
id

The location to archive.

body
customer_id

The parent customer's id.


Tags

Tags are shared labels for your whole account. Each tag is a name-and-color pair you attach to customers to organize them (e.g. VIP, Seasonal, HOA). Tags belong to the account, not to individual customers, so none of the tag tools require a customer_id.

tags.delete permanently removes the tag and all its customer assignments. This cannot be undone — there is no archive for tags. Double-check the id before calling this.

Attaching and detaching tags from customers is not yet available as an MCP tool. See MCP overview — limitations for what is out of scope in v1.


tags.list

List all customer tags for this account, ordered by name. Tags are shared across the whole account, not tied to individual customers.

No input parameters.


tags.get

Fetch one tag by id.

body
id

The tag's id.


tags.create

Create a customer tag. The name must be unique within the tenant.

tags.create requires a token that identifies a specific user. API keys that are scoped only to the account (with no associated user) cannot call this tool. Use a user-bound API key or an OAuth token instead. See API keys if you are unsure which type you have.

body
name

Display name for the tag. Max 64 characters. Must be unique within the tenant.

body
color

Hex color in #RRGGBB format (e.g. #3B82F6). The server supplies a default when omitted.


tags.update

Rename or recolor a tag. This is a partial update — omit any field you do not want to change. Renaming to a name that already exists in the account returns a conflict error.

body
id

The tag to update.

body
name

New name. Max 64 characters.

body
color

New hex color in #RRGGBB format.


tags.delete

Permanently delete a tag. This removes the tag and all customer assignments. It cannot be undone.

body
id

The tag to delete.


Error reference

When a tool call fails, Zoop returns a JSON-RPC error result (isError: true). The kind field tells you what went wrong.

KindWhat it meansCommon cause
invalid_inputA field failed validation, or a required actor condition was not metMissing required field, wrong type, tags.create called with a tenant-key token
not_foundThe referenced customer, contact, location, or tag does not exist in this tenantStale or wrong id, record already archived
conflictThe request collides with existing stateDuplicate tag name, creating a second primary location of the same type
insufficient_scopeThe token does not hold the required scopeMissing read:customers or write:customers
internalUnexpected server or database errorA generic message is surfaced; details are never sent to the client

For the full error shape and HTTP status codes, see Errors.