Outcome
Find active prospect contacts and continue a filtered search without guessing identifiers.
Before you start
- Verify the environment, credential, authorized organization and the published action permission workspace:read.
- Discover an accessible Engage workspace and bind resourceType=workspace and its resourceId separately from action input. Fixture identifiers grant no access.
Discover the target
- Select an authorized Engage workspace separately from action input. Start without cursor; select listId from lists.list when restricting membership.
- For CLI/MCP, pass filter as a structured object. Direct HTTP GET encodes that object as a JSON query value.
topolo whoami --json
topolo actions get app_topolo_engage.contacts.list --json
topolo workspaces list topolo-engage --json
Input fields
| Field | Required | Meaning and constraints |
|---|---|---|
listId | No | Optional discovered list identifier, trimmed non-empty string up to 128 characters; restricts contacts to its memberships. |
q | No | Optional trimmed search text up to 200 characters; matches email, first name, last name and company. |
cursor | No | Optional opaque continuation up to 512 characters, returned as nextCursor. Keep the original filters and workspace; omit for the first page. |
filter | No | Optional strict ICP object. Conditions are ANDed; at most 25 values per array field and 60 values overall. Supported fields include title/seniority, company/domain/country, address/verification/source, custom equality, date ranges, list membership, contactability and latest qualification fit/score. Use the current input schema for exact enums and nested bounds. |
Validate, plan and execute
Set ENGAGE_WORKSPACE_ID to the identifier of your selected workspace. Prepare payload.json from the current input schema and discovered identifiers. Use the same payload for validation, planning and execution.
: "${ENGAGE_WORKSPACE_ID:?Set ENGAGE_WORKSPACE_ID to your selected workspace identifier}"
topolo actions validate app_topolo_engage.contacts.list --data @payload.json --json
topolo actions plan app_topolo_engage.contacts.list --resource-type workspace --resource-id "$ENGAGE_WORKSPACE_ID" --data @payload.json --json
Execute the unchanged validated read in the selected workspace.
: "${ENGAGE_WORKSPACE_ID:?Set ENGAGE_WORKSPACE_ID to your selected workspace identifier}"
topolo actions call app_topolo_engage.contacts.list --resource-type workspace --resource-id "$ENGAGE_WORKSPACE_ID" --data @payload.json --json
For MCP, use topolo_get_action, topolo_validate_action, topolo_plan_action and topolo_call_action. Pass the selected resource context separately; confirm:true is required for mutations.
Worked requests and responses
Captured contacts.list · HTTP 200
Action input
{
"filter": {
"seniority": [
"c_level",
"vp"
]
}
}
Actual local response
{
"contacts": [
{
"id": "contact_3",
"email": "cy@globex.com",
"domain": "globex.com",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Globex",
"title": "VP Marketing",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "CTO",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: ICP filters over the published actions > contacts.list filter > filters by ICP fields over GET, backward compatible without one. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"filter": {
"contactability": "suppressed"
}
}
Actual local response
{
"contacts": [
{
"id": "contact_2",
"email": "bob@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "Sales Manager",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: ICP filters over the published actions > contacts.list filter > filters by contactability with the ledger rule. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"q": "analyt"
}
Actual local response
{
"contacts": [
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": "Ada",
"lastName": null,
"company": "Analytical",
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {
"tier": "a"
},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: audience routes > contacts > creates, reads, searches, edits and archives a manual contact. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{}
Actual local response
{
"contacts": [
{
"id": "contact_9",
"email": "p08@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_8",
"email": "p07@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_7",
"email": "p06@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_6",
"email": "p05@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_51",
"email": "p50@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_50",
"email": "p49@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_5",
"email": "p04@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_49",
"email": "p48@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_48",
"email": "p47@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_47",
"email": "p46@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_46",
"email": "p45@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_45",
"email": "p44@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_44",
"email": "p43@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_43",
"email": "p42@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_42",
"email": "p41@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_41",
"email": "p40@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_40",
"email": "p39@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_4",
"email": "p03@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_39",
"email": "p38@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_38",
"email": "p37@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_37",
"email": "p36@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_36",
"email": "p35@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_35",
"email": "p34@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_34",
"email": "p33@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_33",
"email": "p32@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_32",
"email": "p31@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_31",
"email": "p30@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_30",
"email": "p29@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_3",
"email": "p02@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_29",
"email": "p28@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_28",
"email": "p27@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_27",
"email": "p26@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_26",
"email": "p25@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_25",
"email": "p24@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_24",
"email": "p23@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_23",
"email": "p22@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_22",
"email": "p21@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_21",
"email": "p20@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_20",
"email": "p19@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_2",
"email": "p01@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_19",
"email": "p18@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_18",
"email": "p17@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_17",
"email": "p16@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_16",
"email": "p15@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_15",
"email": "p14@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_14",
"email": "p13@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_13",
"email": "p12@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_12",
"email": "p11@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_11",
"email": "p10@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_10",
"email": "p09@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": "WyIyMDI2LTEwLTAxVDAwOjAwOjAwLjAwMFoiLCJjb250YWN0XzEwIl0"
}
Source test: audience routes > contacts > pages newest first with an opaque cursor. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{}
Actual local response
{
"contacts": [],
"nextCursor": null
}
Source test: audience routes > permissions > allows reads with workspace:read and refuses writes without workspace:write. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"filter": {
"titleNone": [
"sales"
],
"companyIn": [
"acme"
]
}
}
Actual local response
{
"contacts": [
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "CTO",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: ICP filters over the published actions > contacts.list filter > filters by ICP fields over GET, backward compatible without one. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{}
Actual local response
{
"contacts": [
{
"id": "contact_3",
"email": "cy@globex.com",
"domain": "globex.com",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Globex",
"title": "VP Marketing",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_2",
"email": "bob@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "Sales Manager",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "CTO",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: ICP filters over the published actions > contacts.list filter > filters by ICP fields over GET, backward compatible without one. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"filter": {
"contactability": "eligible"
}
}
Actual local response
{
"contacts": [
{
"id": "contact_3",
"email": "cy@globex.com",
"domain": "globex.com",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Globex",
"title": "VP Marketing",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "CTO",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: ICP filters over the published actions > contacts.list filter > filters by contactability with the ledger rule. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"q": ""
}
Actual local response
{
"contacts": [
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": "Ada",
"lastName": null,
"company": "Analytical",
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {
"tier": "a"
},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: audience routes > contacts > creates, reads, searches, edits and archives a manual contact. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"q": "nobody"
}
Actual local response
{
"contacts": [],
"nextCursor": null
}
Source test: audience routes > contacts > creates, reads, searches, edits and archives a manual contact. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"cursor": "WyIyMDI2LTEwLTAxVDAwOjAwOjAwLjAwMFoiLCJjb250YWN0XzEwIl0"
}
Actual local response
{
"contacts": [
{
"id": "contact_1",
"email": "p00@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: audience routes > contacts > pages newest first with an opaque cursor. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"listId": "list_1"
}
Actual local response
{
"contacts": [
{
"id": "contact_2",
"email": "grace@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: audience routes > lists > adds and removes members, counting only active contacts. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"listId": "list_1"
}
Actual local response
{
"contacts": [],
"nextCursor": null
}
Source test: audience routes > lists > keeps lists and memberships inside their workspace. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"listId": "list_1"
}
Actual local response
{
"contacts": [
{
"id": "contact_3",
"email": "grace@navy.mil",
"domain": "navy.mil",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": "Grace",
"lastName": null,
"company": "Navy",
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "csv",
"custom": {
"industry": "Defense"
},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_2",
"email": "sales@acme.io",
"domain": "acme.io",
"addressType": "role",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Example Co",
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "csv",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": "Ada",
"lastName": null,
"company": "Analytical",
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "csv",
"custom": {
"industry": "Computing"
},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: audience routes > imports > applies idempotently: the same key returns the same result and writes nothing twice. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"q": "ada"
}
Actual local response
{
"contacts": [
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": null,
"title": null,
"website": null,
"country": null,
"timezone": null,
"source": "csv",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: audience routes > imports > applies without a list and honours an explicit mapping that ignores columns. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 200
Action input
{
"listId": "list_1"
}
Actual local response
{
"contacts": [
{
"id": "contact_2",
"email": "bob@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "Sales Manager",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
{
"id": "contact_1",
"email": "ada@acme.io",
"domain": "acme.io",
"addressType": "person",
"verificationStatus": "unknown",
"firstName": null,
"lastName": null,
"company": "Acme",
"title": "CTO",
"website": null,
"country": null,
"timezone": null,
"source": "manual",
"custom": {},
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
],
"nextCursor": null
}
Source test: ICP filters over the published actions > lists.members.add_from_filter > adds matching contacts idempotently, reports truncation and audits. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Effects and response meaning
- Reads at most 50 active contacts ordered by createdAt descending, then id descending. Archived contacts are omitted. Optional q searches email, first name, last name and company; the ICP filter combines all supplied conditions.
- Follow the exact nextCursor with the same q, listId and filter until it is null. Contactability filtering scans at most 1,000 candidate rows per request, so a short or empty page with nextCursor is not the end of the search. This is a live keyset search, not a snapshot export.
- The read does not enroll prospects, approve outreach or send messages. Returned classification and verification state do not authorize sending.
Verify the result
- Retain contacts[].id for contacts.get in the same workspace. Check nextCursor explicitly, rather than using page length as the completion test.
- Compare each returned contact against your intended search and inspect archivedAt and verificationStatus before a separately approved workflow.
Recover
Retry this read after a transient failure with the same verified workspace and validated input. Rediscover missing identifiers within that workspace; never create or modify a record to make a read succeed.
Refused responses
Captured contacts.list · HTTP 422
Refused input; do not execute
{
"filter": "{oops"
}
Actual local response
{
"error": {
"code": "invalid_action_input",
"message": "Input for contacts.list is invalid.",
"details": [
{
"expected": "object",
"code": "invalid_type",
"path": [
"filter"
],
"message": "Invalid input: expected object, received string"
}
]
}
}
Source test: ICP filters over the published actions > contacts.list filter > filters by ICP fields over GET, backward compatible without one. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 503
Action input
{}
Actual local response
{
"error": {
"code": "address_hashing_unconfigured",
"message": "Contact storage is unavailable: the address hashing secret is not configured."
}
}
Source test: audience routes > fails closed with 503 when the address hashing secret is missing. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 422
Action input
{
"filter": {
"titleAny": [
"v0",
"v1",
"v2",
"v3",
"v4",
"v5",
"v6",
"v7",
"v8",
"v9",
"v10",
"v11",
"v12",
"v13",
"v14",
"v15",
"v16",
"v17",
"v18",
"v19",
"v20",
"v21",
"v22",
"v23",
"v24"
],
"companyIn": [
"v0",
"v1",
"v2",
"v3",
"v4",
"v5",
"v6",
"v7",
"v8",
"v9",
"v10",
"v11",
"v12",
"v13",
"v14",
"v15",
"v16",
"v17",
"v18",
"v19",
"v20",
"v21",
"v22",
"v23",
"v24"
],
"countryIn": [
"v0",
"v1",
"v2",
"v3",
"v4",
"v5",
"v6",
"v7",
"v8",
"v9",
"v10",
"v11",
"v12",
"v13",
"v14",
"v15",
"v16",
"v17",
"v18",
"v19",
"v20",
"v21",
"v22",
"v23",
"v24"
]
}
}
Actual local response
{
"error": {
"code": "filter_too_complex",
"message": "A filter may bind at most 60 values; this one binds 75."
}
}
Source test: ICP filters over the published actions > contacts.list filter > refuses a filter that binds too many values. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 422
Action input
{
"cursor": "bogus"
}
Actual local response
{
"error": {
"code": "invalid_cursor",
"message": "The cursor is not one this API issued."
}
}
Source test: audience routes > contacts > returns 422 for an invalid email and for input the contract refuses. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 403
Action input
{}
Actual local response
{
"error": "workspace_forbidden"
}
Source test: audience routes > permissions > denies a workspace the caller holds no grant for. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured contacts.list · HTTP 422
Refused input; do not execute
{
"filter": {
"nope": 1
}
}
Actual local response
{
"error": {
"code": "invalid_action_input",
"message": "Input for contacts.list is invalid.",
"details": [
{
"code": "unrecognized_keys",
"keys": [
"nope"
],
"path": [
"filter"
],
"message": "Unrecognized key: \"nope\""
}
]
}
}
Source test: ICP filters over the published actions > contacts.list filter > filters by ICP fields over GET, backward compatible without one. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Error recovery
- HTTP 403
permission_denied: Stop and inspect the permission required by this action in the current credential. Do not broaden permissions or switch organization automatically. - HTTP 422
invalid_action_input: Correct the request against the current input schema and validate it again before planning. - HTTP 503
address_hashing_unconfigured: Stop and report the missing address-hashing configuration to the application operator. This read cannot provision it; do not switch context or modify credentials to bypass the refusal. - HTTP 422
filter_too_complex: Reduce filter values to the documented per-field and overall bounds, then validate the unchanged intended search again. - HTTP 422
invalid_cursor: Discard the malformed or stale cursor and restart the same search. Use only the exact nextCursor returned by that query; never construct or edit one. - HTTP 403
workspace_forbidden: Discover an accessible Engage workspace with the current credential and bind that resource explicitly; do not retry the denied fixture identifier.
Related actions
Evidence and limits
-
contracts/audience.mjs -
contracts/audience-filter.mjs -
apps/api/src/audience/audience-routes.ts -
apps/api/src/audience/contacts-handlers.ts -
apps/api/src/audience/contact-search.ts -
apps/api/src/audience/contacts-repository.ts -
apps/api/src/audience/lists-handlers.ts -
apps/api/src/audience/lists-repository.ts -
apps/api/src/audience/filter-source.ts -
apps/api/test/audience-routes.test.ts -
apps/api/test/segments-routes.test.ts -
Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
-
Widget/bootstrap use actual app handler but mocked worker runtime and app identity.
-
No gateway execution, live permissions, external delivery or deployed proof.
The examples are local route captures with synthetic context and simulated providers. They do not prove live authorization, CLI/MCP gateway execution, production state or delivery.