Skip to content

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.


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.


GET https://e1.envoke.com/v1/contacts

Returns 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.

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.

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
  • country is matched against the stored 2-letter code, so filter with CA, not Canada.
  • 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.
  • interests takes 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 gives Invalid 'interest' provided: 'Volunteers'.
  • autoresponders works the same way with a nurture campaign name. An unknown name gives Invalid 'autoresponder' provided: 'Welcome'.
  • Date fields: date_created and consent_effective_date values are read as UTC unless they include a time zone. consent_expiry is compared as a plain YYYY-MM-DD date. See Dates.
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]=Alumni matches contacts with both tags. Groups inside (or) are numbered the same way.
  • (not) wraps everything inside it: ?filter[(not)][city]=Toronto&filter[(not)][language]=fr excludes 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.

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.

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 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 ([]).


Terminal window
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.

Terminal window
curl -X GET -gs -u "$API_ID:$API_KEY" \
"https://e1.envoke.com/v1/contacts?sort%5Bid%5D=asc&limit=100&skip=100"
Terminal window
curl -X GET -gs -u "$API_ID:$API_KEY" \
"https://e1.envoke.com/v1/contacts?sort%5Bemail%5D=asc"
Terminal window
curl -X GET -gs -u "$API_ID:$API_KEY" \
"https://e1.envoke.com/v1/contacts?filter%5Bemail%5D=alto.lead%40example.com"
Terminal window
curl -X GET -gs -u "$API_ID:$API_KEY" \
"https://e1.envoke.com/v1/contacts?filter%5Bremote_id%5D=HCS-0002"
Terminal window
curl -X GET -gs -u "$API_ID:$API_KEY" \
"https://e1.envoke.com/v1/contacts?filter%5Bcity%5D%5B%28ne%29%5D=Toronto"
Terminal window
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-:

Terminal window
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-"
Terminal window
curl -X GET -gs -u "$API_ID:$API_KEY" \
"https://e1.envoke.com/v1/contacts?filter%5Binterests%5D=Volunteer"
Terminal window
curl -X GET -gs -u "$API_ID:$API_KEY" \
"https://e1.envoke.com/v1/contacts?filter%5Binterests%5D%5B%28ne%29%5D=Volunteer"
Terminal window
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"
Terminal window
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"
Terminal window
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”
Terminal window
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"
Terminal window
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"
Terminal window
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.

Terminal window
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" }
Terminal window
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" }
Terminal window
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.