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
| Status | Description |
|---|---|
subscribed | Active, will receive emails |
unsubscribed | Opted out via unsubscribe link |
bounced | Hard bounce detected — suppressed automatically |
complained | Spam report received — suppressed automatically |
Create a contact
POST /v1/contacts
| Parameter | Type | Description |
|---|---|---|
siteIdrequired | integer | The site this contact belongs to |
emailrequired | string | Email address (automatically lowercased) |
firstName | string | First name |
lastName | string | Last name |
customFields | object | Key-value pairs, e.g. {"company": "Acme"} |
source | string | How they signed up. Defaults to "api" |
tags | string[] | Tag names to apply. Tags are auto-created if new. |
lists | integer[] | List IDs to add the contact to |
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
| Parameter | Type | Description |
|---|---|---|
siteIdrequired | integer | Filter by site (query param) |
q | string | Search by email (partial match) |
status | string | Filter by status: subscribed, unsubscribed, bounced, complained |
page | integer | Page number (default: 1) |
limit | integer | Results per page, 1-100 (default: 50) |
curl "https://pushmail.dev/api/v1/contacts?siteId=1&q=jane&status=subscribed&page=1&limit=20" \
-H "Authorization: Bearer pm_live_YOUR_KEY"{
"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"{
"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.
| Parameter | Type | Description |
|---|---|---|
siteIdrequired | integer | The site these contacts belong to |
contactsrequired | array | Array of contact objects (1-100 items) |
contacts[].emailrequired | string | Email address |
contacts[].firstName | string | First name |
contacts[].lastName | string | Last name |
contacts[].phone | string | Phone number (max 50 chars) |
contacts[].company | string | Company name (max 200 chars) |
contacts[].customFields | object | Key-value pairs of custom data |
contacts[].tags | string[] | Tag names to apply (auto-created if new) |
contacts[].lists | integer[] | List IDs to add the contact to |
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" }
}
]
}'{
"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.
{
"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.
{
"siteId": 1,
"email": "user@example.com",
"tags": ["trial", "webinar-attendee"]
}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.
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"
}'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.
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"}'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.
| Parameter | Type | Description |
|---|---|---|
siteIdrequired | integer | The site to export from (query param) |
format | string | "csv" or "json" (default: "csv") |
listId | integer | Filter by list membership |
tagId | integer | Filter by tag |
status | string | Filter by status: subscribed, unsubscribed, bounced, complained |
segmentId | integer | Filter by dynamic segment |
curl "https://pushmail.dev/api/v1/contacts/export?siteId=1&format=csv&status=subscribed" \
-H "Authorization: Bearer pm_live_YOUR_KEY" \
-o contacts.csvcurl "https://pushmail.dev/api/v1/contacts/export?siteId=1&format=json&listId=3" \
-H "Authorization: Bearer pm_live_YOUR_KEY"{
"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
Authentication
PushMail uses API keys for programmatic access and session cookies for the dashboard. All API requests must be authenticated.
Sequences
Sequences are automated multi-step email flows with branching conditions. Define emails, delays, and condition branches, then enroll contacts to start the drip.