PushMail.dev

Contacts

Contacts are email addresses associated with a site. Each contact can have tags, lists, custom fields, and a subscription status.

The contact object

{
  "id": 42,
  "siteId": 1,
  "email": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "status": "subscribed",
  "leadScore": 45,
  "customFields": { "company": "Acme Inc" },
  "source": "api",
  "ipAddress": null,
  "subscribedAt": "2025-01-15T10:00:00.000Z",
  "createdAt": "2025-01-15T10:00:00.000Z",
  "updatedAt": "2025-01-15T10:00:00.000Z"
}

Status values

StatusDescription
subscribedActive, will receive emails
unsubscribedOpted out via unsubscribe link
bouncedHard bounce detected — suppressed automatically
complainedSpam report received — suppressed automatically

Create a contact

POST /v1/contacts

ParameterTypeDescription
siteIdrequiredintegerThe site this contact belongs to
emailrequiredstringEmail address (automatically lowercased)
firstNamestringFirst name
lastNamestringLast name
customFieldsobjectKey-value pairs, e.g. {"company": "Acme"}
sourcestringHow they signed up. Defaults to "api"
tagsstring[]Tag names to apply. Tags are auto-created if new.
listsinteger[]List IDs to add the contact to
Request
curl -X POST https://pushmail.dev/api/v1/contacts \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": 1,
    "email": "jane@example.com",
    "firstName": "Jane",
    "tags": ["newsletter", "paid-user"],
    "customFields": { "plan": "pro" }
  }'

Returns 201 on success, 409 if the email already exists for this site.

List contacts

GET /v1/contacts

ParameterTypeDescription
siteIdrequiredintegerFilter by site (query param)
qstringSearch by email (partial match)
statusstringFilter by status: subscribed, unsubscribed, bounced, complained
pageintegerPage number (default: 1)
limitintegerResults per page, 1-100 (default: 50)
Request
curl "https://pushmail.dev/api/v1/contacts?siteId=1&q=jane&status=subscribed&page=1&limit=20" \
  -H "Authorization: Bearer pm_live_YOUR_KEY"
Response
{
  "data": {
    "contacts": [
      {
        "id": 42,
        "email": "jane@example.com",
        "firstName": "Jane",
        "status": "subscribed",
        "tags": ["signup", "trial"],
        ...
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 1,
      "totalPages": 1
    }
  }
}

Get a contact

GET /v1/contacts/:id

Returns the contact along with their tags, lists, and recent sends.

curl https://pushmail.dev/api/v1/contacts/42 \
  -H "Authorization: Bearer pm_live_YOUR_KEY"
Response
{
  "data": {
    "contact": {
      "id": 42,
      "email": "jane@example.com",
      "firstName": "Jane",
      "status": "subscribed",
      ...
    },
    "tags": [{ "id": 1, "name": "newsletter" }],
    "lists": [{ "id": 3, "name": "Weekly Newsletter" }],
    "sends": []
  }
}

Update a contact

PUT /v1/contacts/:id

curl -X PUT https://pushmail.dev/api/v1/contacts/42 \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Janet",
    "customFields": { "plan": "enterprise" }
  }'

Delete a contact

DELETE /v1/contacts/:id

curl -X DELETE https://pushmail.dev/api/v1/contacts/42 \
  -H "Authorization: Bearer pm_live_YOUR_KEY"

Bulk create or update contacts

POST /v1/contacts/bulk

Create or update up to 100 contacts in a single request. Existing contacts (matched by email within the site) are updated; new emails are created. Tags are auto-created if they don't exist, and list memberships are added automatically.

ParameterTypeDescription
siteIdrequiredintegerThe site these contacts belong to
contactsrequiredarrayArray of contact objects (1-100 items)
contacts[].emailrequiredstringEmail address
contacts[].firstNamestringFirst name
contacts[].lastNamestringLast name
contacts[].phonestringPhone number (max 50 chars)
contacts[].companystringCompany name (max 200 chars)
contacts[].customFieldsobjectKey-value pairs of custom data
contacts[].tagsstring[]Tag names to apply (auto-created if new)
contacts[].listsinteger[]List IDs to add the contact to
Request
curl -X POST https://pushmail.dev/api/v1/contacts/bulk \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": 1,
    "contacts": [
      {
        "email": "alice@example.com",
        "firstName": "Alice",
        "tags": ["newsletter"],
        "lists": [3]
      },
      {
        "email": "bob@example.com",
        "firstName": "Bob",
        "customFields": { "plan": "pro" }
      }
    ]
  }'
Response
{
  "data": {
    "created": 1,
    "updated": 1,
    "errors": []
  }
}

If some contacts fail (e.g. invalid data), they appear in the errors array with the email and error message. Successfully processed contacts are not affected by individual failures.

Partial failure
{
  "data": {
    "created": 1,
    "updated": 0,
    "errors": [
      { "email": "bad-data@example.com", "error": "UNIQUE constraint failed" }
    ]
  }
}

Tags

Tags are lightweight labels for grouping contacts. They're scoped per site and auto-created when you include them in a contact creation request.

Add tags when creating a contact
{
  "siteId": 1,
  "email": "user@example.com",
  "tags": ["trial", "webinar-attendee"]
}
List tags for a site
curl "https://pushmail.dev/api/v1/tags?siteId=1" \
  -H "Authorization: Bearer pm_live_YOUR_KEY"

Tags are assigned when creating a contact via POST /v1/contacts with the tags field, or by adding the contact to a tag-triggered sequence. To manage tags on existing contacts, use the dashboard.

Lists

Lists are named groups for targeting campaigns and sequences. Unlike tags, lists must be created before you can add contacts to them.

Create a list
curl -X POST https://pushmail.dev/api/v1/lists \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": 1,
    "name": "Weekly Newsletter",
    "description": "Subscribers who opted in to weekly updates"
  }'
Add contacts to a list
curl -X POST https://pushmail.dev/api/v1/lists/3/contacts \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": [42] }'

Bulk import

Import contacts from a CSV file. The import runs asynchronously — you get a job ID back and can poll for progress.

Upload CSV for import
curl -X POST https://pushmail.dev/api/v1/imports/upload \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -F "file=@contacts.csv" \
  -F "siteId=1" \
  -F "listId=3" \
  -F 'columnMapping={"email":"Email Address","firstName":"First Name"}'
Check import progress
curl https://pushmail.dev/api/v1/imports/7 \
  -H "Authorization: Bearer pm_live_YOUR_KEY"

# Response:
{
  "data": {
    "import": {
      "id": 7,
      "status": "processing",
      "totalRows": 5000,
      "processedRows": 2100,
      "importedRows": 2050,
      "skippedRows": 50,
      "errorRows": 0
    }
  }
}

CSV format: Include a header row. At minimum, an email column is required. Duplicates are skipped automatically.

Export contacts

GET /v1/contacts/export

Download contacts as CSV or JSON. Supports filtering by list, tag, status, or segment. Limited to 10,000 contacts per export.

ParameterTypeDescription
siteIdrequiredintegerThe site to export from (query param)
formatstring"csv" or "json" (default: "csv")
listIdintegerFilter by list membership
tagIdintegerFilter by tag
statusstringFilter by status: subscribed, unsubscribed, bounced, complained
segmentIdintegerFilter by dynamic segment
Export as CSV
curl "https://pushmail.dev/api/v1/contacts/export?siteId=1&format=csv&status=subscribed" \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -o contacts.csv
Export as JSON
curl "https://pushmail.dev/api/v1/contacts/export?siteId=1&format=json&listId=3" \
  -H "Authorization: Bearer pm_live_YOUR_KEY"
JSON response
{
  "data": {
    "contacts": [
      {
        "email": "jane@example.com",
        "firstName": "Jane",
        "lastName": "Doe",
        "status": "subscribed",
        "phone": null,
        "company": "Acme Inc",
        "jobTitle": null,
        "city": "San Francisco",
        "state": "CA",
        "country": "US",
        "createdAt": "2025-01-15T10:00:00.000Z",
        "customFields": { "plan": "pro" }
      }
    ],
    "total": 1
  }
}

CSV columns: email, firstName, lastName, status, phone, company, jobTitle, city, state, country, createdAt, customFields (as JSON string).

Next steps

  • Lead Scoring -- Automatically score contacts based on engagement and custom events
  • Segments -- Create dynamic audiences based on contact attributes, tags, engagement, and more
  • Sequences -- Enroll contacts into automated drip campaigns
  • Campaigns -- Send one-off emails to a list, tag, or segment

On this page