List Contacts
Returns a paginated list of CRM contacts for a funnel.
GET /v1/funnels/{funnelId}/contacts
Authentication
Requires the x-perspective-api-key header. See Authentication for details.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
funnelId | string | Yes | The ID of the funnel to retrieve contacts for. |
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
page | integer | No | 0 | Page number, 0-based. Minimum: 0. |
limit | integer | No | 50 | Number of contacts per page. Range: 1–100. |
sortField | string | No | ps_converted_at | Field to sort by. One of: ps_converted_at, email, firstName, lastName. |
sortOrder | integer | No | -1 | Sort direction. -1 for descending, 1 for ascending. |
Example request
- cURL
- JavaScript
curl "https://api.perspective.co/v1/funnels/fnl_abc123/contacts?page=0&limit=20&sortField=ps_converted_at&sortOrder=-1" \
-H "x-perspective-api-key: $PERSPECTIVE_API_KEY"
const funnelId = 'fnl_abc123';
const params = new URLSearchParams({
page: '0',
limit: '20',
sortField: 'ps_converted_at',
sortOrder: '-1',
});
const response = await fetch(
`https://api.perspective.co/v1/funnels/${funnelId}/contacts?${params}`,
{
headers: {
'x-perspective-api-key': process.env.PERSPECTIVE_API_KEY,
},
}
);
const { data, meta } = await response.json();
Response
Returns { data: Contact[], meta: Pagination }.
The Contact object
This is the canonical Contact object reference. All other contact endpoints return the same shape — use this table when reading any contact response.
Top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique contact identifier. |
status | string | Yes | Contact lifecycle status (e.g. "lead", "converted"). |
email | string | No | Primary email address. |
phone | string | No | Primary phone number in E.164 format when available. |
firstName | string | No | Given name. |
lastName | string | No | Family name. |
name | string | No | Full display name when first/last are not split. |
fullAddress | string | No | Single-line address as captured. Preferred over the structured address fields when present. |
timezone | string | No | IANA timezone of the contact. Deprecated — use meta.ps_timezone instead. |
website | string | No | Contact website URL. |
birthday | string | No | Birthday as captured (typically an ISO date string). |
address | object | No | Structured address. Present only when at least one component is known. See Address fields. |
meta | object | No | Perspective-managed metadata. Read-only — cannot be set via the API. See meta fields. |
utmParams | object | No | UTM tracking parameters from the contact's first attributed visit. Read-only. Present only when at least one UTM value exists. See utmParams fields. |
properties | object | No | Custom contact properties keyed by property name. Values can be string, boolean, number, or null. Present only when the contact has at least one custom property. Read-only via direct field — use Update Contact Value to write custom properties. |
messages | object[] | No | Messages sent to this contact. See ContactMessage fields. |
Address fields
Nested inside the address object. All fields are optional.
| Field | Type | Description |
|---|---|---|
postalCode | string | Postal / ZIP code. |
city | string | City. |
state | string | State, province, or region. |
country | string | Country. |
street | string | Street line. |
houseNumber | string | House number. |
meta fields
Perspective-managed metadata captured during the funnel session. All fields are optional. Read-only — these values cannot be set via the API.
| Field | Type | Description |
|---|---|---|
ps_timezone | string | Timezone detected by Perspective (e.g. "Europe/Berlin"). |
ps_converted_at | string (ISO date-time) | When the contact converted (submitted the funnel). |
ps_first_seen_at | string (ISO date-time) | Timestamp of the contact's first known visit. |
ps_last_seen_at | string (ISO date-time) | Timestamp of the contact's most recent visit. |
ps_source | string | How the contact entered the system. One of: manual, funnel, zapier, make, external-api. |
ps_start_slug | string | Slug of the funnel page where the contact started. |
ps_end_slug | string | Slug of the funnel page where the contact converted. |
ps_start_page | string | URL or title of the start page. |
ps_result_page | string | URL or title of the result page reached. |
ps_user_edited_at | string (ISO date-time) | Timestamp of the last manual edit by a user. |
ps_completed_at | string (ISO date-time) | Timestamp when the contact completed all required steps. |
ps_email_verified | boolean | Whether the contact's email address has been verified. |
ps_email_verified_at | string (ISO date-time) | Timestamp of email verification. |
ps_phone_verified | boolean | Whether the contact's phone number has been verified. |
ps_phone_verified_at | string (ISO date-time) | Timestamp of phone verification. |
ps_embed | boolean | true if the contact converted via an embedded funnel. |
ps_cal_booked | boolean | Cal.com meeting booked. |
ps_ghl_booked | boolean | GoHighLevel meeting booked. |
ps_hubspot_booked | boolean | HubSpot meeting booked. |
ps_calendly_booked | boolean | Calendly meeting booked. |
utmParams fields
UTM tracking parameters from the contact's first attributed visit. All fields are optional. Read-only.
| Field | Type | Description |
|---|---|---|
utm_source | string | Marketing source (e.g. "google", "newsletter"). |
utm_medium | string | Marketing medium (e.g. "cpc", "email"). |
utm_campaign | string | Marketing campaign name. |
utm_term | string | Paid keyword search term. |
utm_content | string | Ad or content variant identifier. |
ContactMessage fields
Each item in the messages array has the following fields.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique message identifier. |
name | string | Yes | Internal name of the message. |
type | string | Yes | Message channel (e.g. "email", "sms"). |
content | string | Yes | Rendered message body. |
subject | string | No | Subject line. Present for email messages only. |
Pagination object
Returned in the meta field of the response envelope.
| Field | Type | Description |
|---|---|---|
page | integer | Current page number (0-based). |
limit | integer | Page size used for this query. |
total | integer | Total number of matching contacts across all pages. |
hasNext | boolean | true when another page of results is available. |
Example response
{
"data": [
{
"id": "cnt_abc123",
"status": "lead",
"email": "jane.doe@example.com",
"phone": "+49151234567890",
"firstName": "Jane",
"lastName": "Doe",
"name": "Jane Doe",
"website": "https://janedoe.com",
"birthday": "1990-06-15",
"address": {
"postalCode": "10115",
"city": "Berlin",
"state": "Berlin",
"country": "DE",
"street": "Unter den Linden",
"houseNumber": "1"
},
"meta": {
"ps_timezone": "Europe/Berlin",
"ps_converted_at": "2025-05-01T14:32:00.000Z",
"ps_first_seen_at": "2025-05-01T14:30:00.000Z",
"ps_last_seen_at": "2025-05-02T09:15:00.000Z",
"ps_source": "funnel",
"ps_start_slug": "start",
"ps_end_slug": "result",
"ps_email_verified": true,
"ps_embed": false
},
"utmParams": {
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "spring-launch"
},
"properties": {
"quiz_answer": "Option A",
"score": 42
},
"messages": [
{
"id": "msg_xyz789",
"name": "Welcome Email",
"type": "email",
"subject": "Welcome to our program!",
"content": "<p>Hi Jane, welcome aboard!</p>"
}
]
}
],
"meta": {
"page": 0,
"limit": 20,
"total": 143,
"hasNext": true
}
}
Errors
See Errors for the full list of status codes and handling guidance.
| Status | Meaning |
|---|---|
400 | Invalid query parameters (e.g. limit out of range, unknown sortField). |
401 | Missing or invalid API key. |
403 | Your API key does not have crm:read permission. |
500 | Internal server error. |