API reference

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_name or timezone is set to null.
  • An omitted custom_fields becomes {}.
  • An omitted company_name leaves the company as it is. Send "company_name": null to 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.