Skip to content

Automate with n8n or Make

Verify the contacts another tool stores, on a schedule: send them as one batch with their IDs, wait for it, then write each recommendation back.

A workflow in n8n or Make can verify the contacts a tool keeps (a CRM, a newsletter platform, a spreadsheet, a database) with no file to export by hand. It reads the contacts, sends their addresses to PricklyMails as one batch, each with its contact's ID, then writes each recommendation back to that contact. On the PricklyMails side it takes three requests, sent from the platform's HTTP node; the nodes that read and update the contacts are the tool's own, in the platform's docs.

Before you start

Create an API key in the dashboard with the email:verify:bulk and results:read scopes (see API keys). In n8n, save it as a Bearer Auth credential, the key alone since n8n adds Bearer itself, and pick it in each HTTP Request node. In Make, set each Make a request module of the HTTP app to the API key authentication type, with a key whose Key is Bearer pm_live_your_key, placed In the header under the name Authorization. Leave the key out of Headers: Make's HTTP app asks for credentials in their own field, and a value typed in Headers travels with the scenario's blueprint.

Send the contacts as one batch

Gather the contacts the tool returned into one request: emails holds their addresses and custom_ids, in the same order, each contact's ID in the tool. n8n's Aggregate node, or Make's Array aggregator, turns the contacts into these two lists. Here contacts.json holds { "name": "Notion contacts", "emails": [...], "custom_ids": [...] }, 20 contacts:

Request
curl -X POST https://api.pricklymails.com/api/batches \
  -H "Authorization: Bearer pm_live_your_key" \
  -H "Content-Type: application/json" \
  -d @contacts.json
Response
{
    "batch_uuid": "f6ae54c6-0fa4-474c-ab5b-f53808bb7917",
    "status": "processing",
    "total_jobs": 20,
    "syntax_errors": 0,
    "duplicates": 0,
    "estimated_cost": 200
}

A custom_id is the contact's ID in the tool, never its address: one that holds an @ is refused, and it is at most 256 characters long. A malformed address, or a second copy of one, never becomes a job, so its contact gets no row in the results; the batch counts it under syntax_errors or duplicates. A batch holds at least 20 addresses and at most 4,096 per request: stream a longer list in chunks, and verify fewer than 20 one at a time (see "One new contact at a time" below).

Wait for the batch

A batch sends no webhook, so the workflow reads GET /api/batches/{uuid} until status is no longer processing: it ends completed, or partially_completed when some jobs failed.

Request
curl https://api.pricklymails.com/api/batches/f6ae54c6-0fa4-474c-ab5b-f53808bb7917 \
  -H "Authorization: Bearer pm_live_your_key"

In n8n, a Wait node of a minute, then an If node on status whose false branch goes back to the Wait node, makes the loop. A Make scenario runs from its first module to its last, without going back: let the scenario that submits the batch keep its batch_uuid in a Data store, and a second scenario, scheduled later, read the batch from there.

Write each recommendation back

GET /api/batches/{uuid}/results returns 96 rows at most per request (limit=96): add offset until a page comes back with fewer rows than the limit. n8n's HTTP Request node does it with its Pagination option (Update a Parameter in Each Request), and Make's Make a request with its offset-based pagination.

Request
curl "https://api.pricklymails.com/api/batches/f6ae54c6-0fa4-474c-ab5b-f53808bb7917/results?limit=96&offset=0" \
  -H "Authorization: Bearer pm_live_your_key"
Response
{
    "results": [
        {
            "uuid": "cc23c478-cf28-4908-acf5-cdcc58c80935",
            "custom_id": "page-00",
            "status": "completed",
            "score": 0,
            "verdict": "invalid",
            "reason": "null_mx",
            "recommendation": "remove"
        }
    ],
    "limit": 96,
    "offset": 0
}

n8n's Split Out node, or Make's Iterator, turns the rows into one item each. Then the tool's own node updates the contact whose ID is the row's custom_id: remove takes it out of your sends (an unsubscribe, a tag, a removal from the list), review marks it for a look before the next send, and send changes nothing. What each recommendation means is on Batches; where to write it in each tool is on its tool page.

One new contact at a time

For a contact that arrives on its own (a form, a signup), the workflow skips the batch: it sends the address to POST /api/verify with Prefer: wait=12, and gets the result in the same response when the check ends within those 12 seconds (see Verify an email). Give the HTTP node more time than that: set n8n's Timeout option, in milliseconds, to 16,000 or more, and Make's, in seconds, to 16 or more. Without the result in time, the workflow reads it later with GET /api/verify/{uuid} or receives it on a webhook. The key then needs the email:verify:single scope as well.