Contacts
Read enriched contact records from Cirrus Insight's Cortex data model — the "people" objects on accounts.
contacts:read covers all endpoints on this page. The account-scoped listing endpoint additionally requires accounts:read — reading contacts on a specific account implies visibility of the account.
Endpoints on this page
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1alpha1/contacts | List contacts visible to the org. Filter by search, email, account, or others. Details ↓ |
GET | /api/v1alpha1/contacts/{contactId} | Fetch one contact's full record — includes enrichment (title, LinkedIn URL, summary). Details ↓ |
GET | /api/v1alpha1/contacts/{contactId}/activity | Paginated activity timeline for one contact. Details ↓ |
GET | /api/v1alpha1/accounts/{accountId}/contacts | List contacts on one account. Requires accounts:read too. Details ↓ |
List contacts
GET /api/v1alpha1/contacts
Query parameters
| Parameter | Type | Description |
|---|---|---|
cursor | string | Standard keyset cursor. |
limit | number | Clamped 1–100, default 25. |
sort | string | updatedAt, createdAt, lastName, firstName. Prefix - for descending. Default -updatedAt. |
search | string | (optional) Substring match on first name, last name, or email. Case-insensitive. |
email | string | (optional) Exact match on primary email. Useful for lookup-by-email flows. |
accountId | string | (optional) Filter to one account. Equivalent to /accounts/{id}/contacts. |
Response
See the Contactmodel for the full field reference.
emailfilter is deliberately narrow — exact-match only. No wildcard, no regex. Prevents mass-scraping. Substring searches go throughsearch.account— lightweight{id, name}reference.nullwhen the contact isn't linked to an account (rare; most contacts are enriched with an account guess by domain).
Errors
Standard pagination errors.
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/contacts?search=guest&limit=25" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
"https://altus.cirrusinsight.com/api/v1alpha1/contacts",
headers={"X-Cirrus-Api-Key": token},
params={"search": "guest", "limit": 25},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var query = HttpUtility.ParseQueryString(string.Empty);
query["search"] = "guest";
query["limit"] = "25";
var uri = new UriBuilder("https://altus.cirrusinsight.com/api/v1alpha1/contacts")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();{
"items": [
{
"id": "cont_...",
"orgId": "org_...",
"firstName": "Sample",
"lastName": "Guest",
"displayName": "Sample Guest",
"email": "guest@example.com",
"phone": "+14155551234",
"title": "Director of Sales Ops",
"account": {
"id": "acct_...",
"name": "Acme Corporation"
},
"profileIconUri": null,
"createdAt": "2026-05-01T12:00:00Z",
"updatedAt": "2026-07-14T09:30:00Z"
}
],
"nextCursor": null
}Get a contact
GET /api/v1alpha1/contacts/{contactId}
Response
summary— LLM-generated. May be stale.sourceRefs.salesforce— Salesforce contact id + URL, when synced.
Only the primary email is exposed on the record. To look up a person by an alternate email address, query each candidate separately.
Errors
404 contact-not-found— id unknown or cross-org
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/contacts/cont_..." \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/contacts/{contact_id}",
headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
contact = response.json()using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var response = await client.GetAsync(
$"https://altus.cirrusinsight.com/api/v1alpha1/contacts/{contactId}");
response.EnsureSuccessStatusCode();
var contact = await response.Content.ReadAsStringAsync();{
"id": "cont_...",
"orgId": "org_...",
"firstName": "Sample",
"lastName": "Guest",
"displayName": "Sample Guest",
"email": "guest@example.com",
"phone": "+14155551234",
"title": "Director of Sales Ops",
"account": {
"id": "acct_...",
"name": "Acme Corporation"
},
"profileIconUri": null,
"linkedInUrl": "https://linkedin.com/in/sample-guest",
"summary": "Sample Guest is a Director of Sales Ops at Acme Corporation ...",
"sourceRefs": {
"salesforce": {
"id": "0031U00000abcXYZ",
"url": "https://acme.my.salesforce.com/0031U00000abcXYZ"
}
},
"createdAt": "2026-05-01T12:00:00Z",
"updatedAt": "2026-07-14T09:30:00Z"
}List activity
GET /api/v1alpha1/contacts/{contactId}/activity
Paginated activity timeline for one contact. Same shape as GET /api/v1alpha1/accounts/{id}/activity — meeting + email items filterable by type and since.
Query params: cursor, limit, sort (-occurredAt default), type (repeatable), since.
Errors
404 contact-not-found— id unknown or cross-org
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/contacts/cont_.../activity?type=meeting&limit=25" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/contacts/{contact_id}/activity",
headers={"X-Cirrus-Api-Key": token},
params={"type": "meeting", "limit": 25},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var query = HttpUtility.ParseQueryString(string.Empty);
query["type"] = "meeting";
query["limit"] = "25";
var uri = new UriBuilder(
$"https://altus.cirrusinsight.com/api/v1alpha1/contacts/{contactId}/activity")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();List contacts on an account
GET /api/v1alpha1/accounts/{accountId}/contacts
Convenience wrapper equivalent to GET /api/v1alpha1/contacts?accountId={id}.
Requires both contacts:read and accounts:read. Reading contacts by account implies visibility of the account. A partner with only contacts:read who calls this endpoint gets 403 insufficient-scope with requiredScope: "accounts:read".
Query params identical to GET /api/v1alpha1/contacts except accountId and email filters (nonsensical here). The search filter still applies.
Response
Same shape as GET /api/v1alpha1/contacts.
Errors
404 account-not-found— id unknown or cross-org
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/accounts/acct_.../contacts?limit=25" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/accounts/{account_id}/contacts",
headers={"X-Cirrus-Api-Key": token},
params={"limit": 25},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var query = HttpUtility.ParseQueryString(string.Empty);
query["limit"] = "25";
var uri = new UriBuilder(
$"https://altus.cirrusinsight.com/api/v1alpha1/accounts/{accountId}/contacts")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();