Contact API - Read
Use the read endpoints to retrieve contacts from your account. You can list contacts with filtering, sorting, and pagination, or retrieve a single contact by ID.
Endpoints
Section titled “Endpoints”| Method | URL | Description |
|---|---|---|
| GET | https://e1.envoke.com/v1/contacts |
Return a list of contacts. |
| GET | https://e1.envoke.com/v1/contacts/{ID} |
Return a single contact. |
Authentication is via HTTP Basic Auth using your API ID and API Key.
List Contacts
Section titled “List Contacts”GET https://e1.envoke.com/v1/contactsReturns a JSON array of contacts. Supports filtering, sorting, pagination, and field selection. With no sort, the order is not defined; use sort when you page through results.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 10 |
Maximum number of results to return. Hard cap is 100 unless ?fields= or ?result_type=kvp is also set (see note below). limit=1 returns a single object, not an array (see below). |
skip |
integer | 0 |
Number of records to skip. Use with limit and sort to paginate. |
fields |
string | (all) | Comma-separated list of fields to include in each result, e.g. id,email. Use names from the Fields reference. |
result_type |
string | (array) | Set to kvp to receive an object of contact ID to email address ({ "1001": "alto.lead@example.com" }). |
filter[{field}] |
string | — | Filter on a field. See Filterable and sortable fields and Filter Operators. |
sort[{field}] |
string | — | Sort on a field. Value: asc or desc (case-insensitive), or 1 or -1. Repeat for a secondary sort. |
Note — limit behaviour with ?fields= and ?result_type=kvp: When either of these parameters is present, the default limit increases to 10,000 (returning every record up to that number if no explicit limit is specified) and a ?limit= value above 100 is honoured as-is.
Note — limit=1 returns an object. With limit=1 the response is the matching contact as a bare JSON object, not an array of one. result_type=kvp has no effect when limit=1; fields still applies. When nothing matches, the response is [].
Note — filters without filter[...]. When the request has no filter parameter, every query parameter other than limit, skip, sort, fields, and result_type is treated as a filter, so ?email=alto.lead%40example.com is the same as ?filter[email]=alto.lead%40example.com. Any other parameter is rejected as an unknown filter field (for example Field 'key_field' is not valid for filtering). When filter is present, the other parameters are ignored as filters. Use filter[...] for custom fields: dots and spaces in a bare parameter name are not preserved.
Filterable and sortable fields
Section titled “Filterable and sortable fields”| Field | Filter operators | Sortable |
|---|---|---|
id, external_id, remote_id, first_name, last_name, title, email, company, phone, address_1, address_2, city, country, province, postal_code, website, language, feedback |
equals, (ne) |
Yes |
date_created |
equals, (ne), (gt), (lt), (gte), (lte) |
Yes |
consent_status |
equals, (ne) |
Yes |
consent_description |
equals, (ne) |
No |
consent_effective_date, consent_expiry |
equals, (ne), (gt), (lt), (gte), (lte) |
No |
custom_fields.{name} |
equals, (ne), (regex) |
Yes |
interests |
equals, (ne) |
No |
autoresponders |
equals, (ne) |
No |
countryis matched against the stored 2-letter code, so filter withCA, notCanada.custom_fields.{name}uses the custom field’s name exactly as defined in your account, including case (names contain only letters, digits, and underscores):filter[custom_fields.Voice_Part]=Alto.intereststakes the exact name (case-sensitive) of a tag, subscription, or mandatory consent item, and matches contacts that have it;(ne)matches contacts that don’t. IDs are not accepted. An unknown name givesInvalid 'interest' provided: 'Volunteers'.autorespondersworks the same way with a nurture campaign name. An unknown name givesInvalid 'autoresponder' provided: 'Welcome'.- Date fields:
date_createdandconsent_effective_datevalues are read as UTC unless they include a time zone.consent_expiryis compared as a plainYYYY-MM-DDdate. See Dates.
Filter Operators
Section titled “Filter Operators”| Operator | Example | Description |
|---|---|---|
| (none) | ?filter[city]=Toronto |
Equals. |
(ne) |
?filter[city][(ne)]=Toronto |
Not equal. |
(regex) |
?filter[custom_fields.Season_Ticket_Number][(regex)]=^HCS-ST- |
Matches a MySQL regular expression. Custom fields only. |
(gt) |
?filter[date_created][(gt)]=2026-01-01T00:00:00Z |
Greater than. Date fields only. |
(lt) |
?filter[date_created][(lt)]=2026-07-01T00:00:00Z |
Less than. Date fields only. |
(gte) |
?filter[consent_effective_date][(gte)]=2026-09-01 |
Greater than or equal. Date fields only. |
(lte) |
?filter[consent_expiry][(lte)]=2027-03-31 |
Less than or equal. Date fields only. |
(or) |
?filter[(or)][0][email]=a%40example.com&filter[(or)][1][email]=b%40example.com |
Matches if any of the grouped conditions match. |
(not) |
?filter[(not)][consent_status]=Express |
Matches if the grouped conditions do not all match. |
Note: URL-encode brackets in production requests (e.g. %5B for [).
Each operator also has a $ spelling ($ne, $regex, $gt, $or, and so on) that behaves the same way. These docs use the (op) spelling.
The (in) operator is not supported on any contacts field. To match one of several values, use (or).
Combining conditions.
- Separate filters are ANDed:
?filter[city]=Toronto&filter[consent_status]=Express. - Two operators on one field are ANDed, which gives a range:
?filter[date_created][(gte)]=2026-01-01&filter[date_created][(lt)]=2026-07-01. - A query string cannot repeat a key, so to use the same field twice, put each condition in its own numbered group:
?filter[0][interests]=Volunteer&filter[1][interests]=Alumnimatches contacts with both tags. Groups inside(or)are numbered the same way. (not)wraps everything inside it:?filter[(not)][city]=Toronto&filter[(not)][language]=frexcludes contacts who are in Toronto and French-speaking.
Note: (ne) on a custom field matches only contacts that have a value for that field; contacts with no value are not returned.
Errors
Section titled “Errors”Invalid list parameters return HTTP 400 with every problem listed at once:
{ "success": false, "result_text": "Validation failed, see 'result_data' -> 'errors'", "result_data": { "errors": [ "Field 'nickname' is not valid for filtering", "'REGEXP' is not a valid operator for the field 'email'" ] }}| Error | Cause |
|---|---|
Invalid 'limit': 'ten' |
limit is not a whole number. |
Invalid 'skip': 'abc' |
skip is not a whole number. |
Invalid 'sort': 'email' |
sort was sent without a field, e.g. ?sort=email. |
'up' is not a valid sort direction |
Sort direction is not asc, desc, 1, or -1. |
Field 'interests' is not valid for sorting |
The field cannot be sorted, or does not exist. |
Field 'nickname' is not valid for filtering |
The field does not exist. Also returned for an unrecognised operator: ?filter[email][(foo)]=x gives Field '(foo)' is not valid for filtering. |
'REGEXP' is not a valid operator for the field 'email' |
The operator is not supported on that field. The message names the SQL operator: REGEXP for (regex), IN for (in), and >, <, >=, <= for the range operators. |
Invalid date format in filter condition for date_created |
A date filter value could not be read as a date. |
Invalid 'interest' provided: 'Volunteers' |
No tag, subscription, or mandatory consent item has that exact name. |
Invalid 'autoresponder' provided: 'Welcome' |
No nurture campaign has that exact name. |
Field 'email' requires a scalar value |
An operator is given a list or group where a single value is expected, e.g. filter[email][(ne)][]=alto.lead@example.com. |
Field '0' is not valid for filtering |
A field is given a list without an operator, e.g. filter[email][]=alto.lead@example.com; the list position is read as the field name. |
Response
Section titled “Response”A JSON array of contact objects. Returns an empty array ([]) if no contacts match.
[ { "id": "1001", "external_id": "9f2c4e7a1b3d5f6a8c0e2b4d6f8a1c3e", "date_created": "2026-10-05T14:03:11+00:00", "remote_id": "", "first_name": "Maya", "last_name": "Okafor", "title": "Section Lead", "email": "alto.lead@example.com", "company": "", "phone": "416-555-0142", "address_1": "48 Harbour Lane", "address_2": "Unit 5", "city": "Toronto", "country": "CA", "province": "ON", "postal_code": "M5V 0Z9", "website": "", "language": "en", "feedback": "", "consent_status": "Express", "consent_description": "Signed up at the spring concert", "consent_effective_date": "2026-09-12T18:30:00+00:00", "consent_expiry": "", "custom_fields": { "Voice_Part": "Alto", "Season_Ticket_Number": "HCS-ST-0417" }, "interests": ["Volunteer", "Alumni", "Concert News"], "autoresponders": [] }]consent_expiry is "" when the consent does not expire or when expiry does not apply to the contact (see Consent fields).
Get a Single Contact
Section titled “Get a Single Contact”GET https://e1.envoke.com/v1/contacts/{ID}Returns a single contact object (the same shape as one item in the list response). Replace {ID} with the Envoke contact ID. fields can be used to select fields; filter, sort, and paging parameters are ignored.
If the ID is not found, the API returns an empty array ([]).
Examples
Section titled “Examples”Return the first page of contacts
Section titled “Return the first page of contacts”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts"Returns up to 10 contacts. Use limit (up to 100) and skip to page through the rest.
Paginate through results
Section titled “Paginate through results”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?sort%5Bid%5D=asc&limit=100&skip=100"Sort by email
Section titled “Sort by email”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?sort%5Bemail%5D=asc"Find a contact by email
Section titled “Find a contact by email”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Bemail%5D=alto.lead%40example.com"Find a contact by remote ID
Section titled “Find a contact by remote ID”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Bremote_id%5D=HCS-0002"Exclude a city
Section titled “Exclude a city”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Bcity%5D%5B%28ne%29%5D=Toronto"Filter by a custom field value
Section titled “Filter by a custom field value”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Bcustom_fields.Voice_Part%5D=Alto"With (regex), contacts whose season ticket number starts with HCS-ST-:
curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Bcustom_fields.Season_Ticket_Number%5D%5B%28regex%29%5D=%5EHCS-ST-"Contacts with a tag, and without it
Section titled “Contacts with a tag, and without it”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Binterests%5D=Volunteer"curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Binterests%5D%5B%28ne%29%5D=Volunteer"Contacts with two tags
Section titled “Contacts with two tags”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5B0%5D%5Binterests%5D=Volunteer&filter%5B1%5D%5Binterests%5D=Alumni"Either of two email addresses
Section titled “Either of two email addresses”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5B%28or%29%5D%5B0%5D%5Bemail%5D=alto.lead%40example.com&filter%5B%28or%29%5D%5B1%5D%5Bemail%5D=tenor.remote%40example.com"Everyone without express consent
Section titled “Everyone without express consent”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5B%28not%29%5D%5Bconsent_status%5D=Express"Contacts created in the first half of 2026
Section titled “Contacts created in the first half of 2026”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Bdate_created%5D%5B%28gte%29%5D=2026-01-01T00:00:00Z&filter%5Bdate_created%5D%5B%28lt%29%5D=2026-07-01T00:00:00Z"Consent given after a date
Section titled “Consent given after a date”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?filter%5Bconsent_effective_date%5D%5B%28gt%29%5D=2026-09-01T00:00:00Z"Return only specific fields
Section titled “Return only specific fields”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?fields=id,email,consent_status"Response:
[ { "id": "1001", "email": "alto.lead@example.com", "consent_status": "Express" }, { "id": "1002", "email": "tenor.remote@example.com", "consent_status": "Express" }]With fields set and no limit, this returns up to 10,000 contacts.
Return as a key-value map
Section titled “Return as a key-value map”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?result_type=kvp"Response:
{ "1001": "alto.lead@example.com", "1002": "tenor.remote@example.com" }Look up one contact’s ID by email
Section titled “Look up one contact’s ID by email”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts?email=alto.lead%40example.com&fields=id&limit=1"Response (an object, because limit=1):
{ "id": "1001" }Get a single contact by ID
Section titled “Get a single contact by ID”curl -X GET -gs -u "$API_ID:$API_KEY" \ "https://e1.envoke.com/v1/contacts/1001"Note: The curl examples above are formatted for readability. All requests must use properly encoded URLs.
Full reference, OpenAPI description, and Postman collection: developers.envoke.com.

