Prospects
A prospect is a person you email. A prospect belongs to a company, which anymail derives from the email domain or from company_name.
| Method | Path | Action |
|---|---|---|
POST |
/api/v1/prospects |
Create a prospect, optionally enrol it in a campaign |
GET |
/api/v1/prospects |
List prospects |
GET |
/api/v1/prospects/{id} |
Get one prospect |
PATCH |
/api/v1/prospects/{id} |
Update a prospect |
Create a prospect#
POST /api/v1/prospects runs the same function as the form in the app: the same address validation, the same grouping into a company and the same duplicate handling. The endpoint honours an Idempotency-Key.
Query
| Parameter | Type | Notes |
|---|---|---|
campaign |
UUID | Optional. After the prospect is created, anymail enrols it in this campaign with the rules of Add prospects in the app. The campaign must be draft or active. An unknown or unusable campaign returns 400 with Unknown or unusable campaign. and no prospect is created. |
The response does not say whether the enrolment succeeded. Check the run history in Settings, Integrations, History. To enrol existing prospects with a result for each person, use POST /campaigns/{id}/prospects.
Body (JSON, up to 64 KiB)
| Field | Type | Required | Notes |
|---|---|---|---|
email |
string | Yes | Trimmed and lower-cased. The last segment of the domain must start with a letter. |
first_name |
string or null | No | |
last_name |
string or null | No | |
timezone |
string or null | No | Not checked against a time zone database. |
company_name |
string or null | No | The company name you give. It takes precedence over the email domain when anymail groups prospects into companies. |
custom_fields |
object | No | Keys map to a string, number or boolean. Numbers and booleans are stored as strings. An object, an array or null as a value returns 400. |
curl -X POST "https://app.anymail.watch/api/v1/prospects" \
-H "Authorization: Bearer $ANYMAIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"ada@example.com","first_name":"Ada","company_name":"Example Ltd","custom_fields":{"role":"CTO"}}'
Response 201
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"email": "ada@example.com",
"company_id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427"
}
company_id is null for an address on a public mail provider, such as gmail.com, when you give no company_name. That response is a valid 201.
Errors
| Status | Code | Message |
|---|---|---|
400 |
Request body too large., Expected a JSON body., Body is not valid JSON., Body must be a JSON object. |
|
400 |
`email` must be a string., `<field>` must be a string or null., `custom_fields` must be an object of string values., `custom_fields.<key>` must be a string, number or boolean. |
|
400 |
invalid-email |
Enter a valid email address. |
409 |
duplicate |
<email> is already in your workspace. |
500 |
db-error |
A database failure. |
502 |
company-failed |
Grouping into a company failed. |
A repeated call without an idempotency key returns 409, and nothing is updated. Use PATCH to change an existing prospect.
List prospects#
GET /api/v1/prospects returns prospects with the activity columns of the Prospects screen.
| Parameter | Type | Notes |
|---|---|---|
email |
string | Exact address, case-insensitive. |
company_id |
UUID | Prospects of one company. |
status |
string | One of blocked, bounced, replied, active, contacted, not_contacted. The definition and the precedence match the Status column in the app: suppressed, then bounced, then replied, then active in a campaign, then contacted, then not contacted. |
created_since |
ISO 8601 | Prospects created at or after this time. |
limit |
integer | 1 to 100. Default 50. |
cursor |
string | See Pagination. |
updated_since does not exist, because prospects carry no modification timestamp. The parameter returns 400 so that a script does not rely on a filter that is not there.
curl "https://app.anymail.watch/api/v1/prospects?status=replied&limit=2" \
-H "Authorization: Bearer $ANYMAIL_TOKEN"
Response 200
{
"data": [
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"email": "ada@example.com",
"first_name": "Ada",
"last_name": null,
"company": { "id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427", "name": "Example Ltd", "domain": "example.com" },
"timezone": null,
"custom_fields": { "role": "CTO" },
"tags": ["q3"],
"source": "API",
"verification_status": null,
"verification_checked_at": null,
"status": "replied",
"campaign_count": 1,
"emails_sent": 3,
"date_contacted": "2026-08-20T08:02:11.512+00:00",
"date_responded": "2026-08-25T16:19:38.123+00:00",
"created_at": "2026-08-19T12:00:00.000+00:00"
}
],
"next_cursor": null
}
Get a prospect#
GET /api/v1/prospects/{id} returns one prospect in the shape above, without the data wrapper. An unknown id, an id from another workspace and an id that is not a UUID all return 404 with {"error":"Prospect not found."}.
curl "https://app.anymail.watch/api/v1/prospects/7c9e6679-7425-40de-944b-e07fc1f90ae7" \
-H "Authorization: Bearer $ANYMAIL_TOKEN"
Update a prospect#
PATCH /api/v1/prospects/{id} takes the same fields and the same validation as the create call. email is required, because it is the natural key.
Send the full state you want:
- An omitted
first_name,last_nameortimezoneis set tonull. - An omitted
custom_fieldsbecomes{}. - An omitted
company_nameleaves the company as it is. Send"company_name": nullto clear it.
A change of email domain or of company_name moves the prospect to another company, which anymail creates when needed. source does not change.
curl -X PATCH "https://app.anymail.watch/api/v1/prospects/7c9e6679-7425-40de-944b-e07fc1f90ae7" \
-H "Authorization: Bearer $ANYMAIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"ada@example.com","first_name":"Ada","last_name":"Lovelace","company_name":"Example Ltd"}'
Response 200
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"email": "ada@example.com",
"company_id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427"
}
Errors
| Status | Code | Message |
|---|---|---|
400 |
Body errors as for create. | |
400 |
invalid-email |
Enter a valid email address. |
404 |
not-found |
Prospect not found. This includes an id from another workspace and an id that is not a UUID. |
409 |
duplicate |
<email> is already used by another prospect in your workspace. |
500 |
db-error |
A database failure. |
502 |
company-failed |
Grouping into a company failed. |