Files
plunk/apps/wiki/content/docs/concepts/contacts.mdx
T

152 lines
9.4 KiB
Plaintext

---
title: Contacts
description: Manage and organize your contacts effectively
icon: Users
---
Contacts in Plunk represent an individual email recipient. Each contact has an identifier and is linked to an email address.
## Adding contacts
Contacts can be added to your Plunk project in several ways:
- Using [`/v1/track`](/api-reference/public-api/trackEvent) — tracking an event for a contact that doesn't exist yet automatically creates it (subscribed by default).
- Using [`POST /contacts`](/api-reference/contacts/createContact) — create or upsert a single contact. Existing emails are **updated**, not rejected; the response includes a `_meta: { isNew, isUpdate }` block and uses status `201` for new contacts and `200` for updates.
- Bulk **CSV import** via `POST /contacts/import` — upload a CSV (≤ 5 MB) and poll `GET /contacts/import/:jobId` for status. The first column must be `email`; any other columns map to keys under `data.*`.
- Bulk **subscribe / unsubscribe / delete** via `POST /contacts/bulk-subscribe`, `bulk-unsubscribe`, and `bulk-delete` — each accepts up to 1,000 contact IDs and returns a job ID; poll `GET /contacts/bulk/:jobId` for progress.
- Adding emails to a **static segment** with `POST /segments/:id/members` and `createMissing: true` will create any contacts that don't already exist.
- Manually through the dashboard.
## Contact Data
You can associate custom data with each contact using key-value pairs. This data can be used for segmentation and personalization.
### Data types
Contact data types are inferred from the **first non-null value** Plunk sees for a given key in your project:
| Type | Description |
| ------- | ------------------------------------------------------------------------------------------------------------ |
| String | Any text value that doesn't look like a date. |
| Number | Numeric values, including integers and floats. |
| Boolean | `true` or `false`. |
| Date | A string matching `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS[.sss]Z`. The full ISO 8601 form is required for date-aware operators in segments. |
The type is sampled per-project on first write — once a key is typed as `number`, subsequent string values for that key are still stored, but the **inferred type** stays `number` for the segment filter UI. If you accidentally mix types, the safest reset is to delete the field via `DELETE /contacts/fields/:field` and re-import with the type you want.
<Callout title="Default data type" type="info">
If a value doesn't match number, boolean, or the ISO 8601 date format, it's typed as a string.
</Callout>
### Non-persistent values
Send a value as `{ value, persistent: false }` to use it once for template rendering without storing it on the contact:
```json
{
"email": "user@example.com",
"data": {
"firstName": "Ada",
"resetCode": { "value": "ABC123", "persistent": false }
}
}
```
`firstName` is saved on the contact; `resetCode` is available to the template for this send only and is then discarded. Use this for one-shot values like password reset codes, magic links, and one-time tokens.
### Special value handling
When creating or updating contacts, certain values are handled specially:
| Value | Behavior | Example |
| ------------------- | ------------------------------------------- | ------------------------------------- |
| Empty string (`""`) | Ignored - field is not stored or updated | `{ name: "" }` → Field is skipped |
| `null` | Delete - field is removed from contact data | `{ name: null }` → Field is deleted |
| Other values | Stored/updated normally | `{ name: "John" }` → Stored as "John" |
<Callout title="Removing contact data" type="info">
To remove a field from a contact's data, set it to `null` when creating or updating the contact. Empty strings are
automatically filtered out and won't overwrite existing data.
</Callout>
### Reserved keys
Some keys are reserved by Plunk and managed at the contact level (not inside `data`). You can read them but you can't set them through `data`:
| Key | Description |
| ------------ | ------------------------------------------------------------------------ |
| `email` | The contact's email address |
| `createdAt` | Timestamp of when the contact was created |
| `updatedAt` | Timestamp of the last update |
| `subscribed` | Boolean indicating whether the contact is subscribed to marketing emails |
The following keys are also **silently filtered out** of any `data` payload — sending them through `/v1/track`, `POST /contacts`, or `PATCH /contacts/:id` will not store them and will not return an error:
`id`, `plunk_id`, `plunk_email`, `unsubscribeUrl`, `subscribeUrl`, `manageUrl`
The last three are auto-generated per-recipient at send time and exposed as template variables (see [Templates](/concepts/templates)).
### Special keys
| Key | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| locale | The contact's preferred locale in ISO 639 (e.g. 'en', 'fr', 'es'). Specifying the locale field on a contact will override the project-wide locale for contact-facing pages and email footers |
## Subscription State
Every contact has a `subscribed` field that determines which types of emails they will receive. A newly created contact is subscribed by default when created via `/v1/track`; contacts created via `POST /contacts` use whatever value you pass (default: `false`).
When you update a contact, **omitting `subscribed` keeps the current state** — it is not the same as passing `false`. To change the state, pass an explicit `true` or `false`.
Every flip of `subscribed` automatically tracks an event on the contact:
- `subscribed` flipped to `true` → `contact.subscribed` event
- `subscribed` flipped to `false` → `contact.unsubscribed` event
These events fire on every path that changes subscription state — manual edits, the public unsubscribe page, bulk operations, automatic unsubscribes from bounces / complaints. You can branch on them in workflows.
### How contacts become unsubscribed
A contact can become unsubscribed in several ways:
- **Manually** through the dashboard or via the API.
- **Self-service** by clicking the unsubscribe link in an email (powered by the public unsubscribe pages).
- **Automatically** on a permanent (hard) email bounce. Soft bounces don't change subscription state.
- **Automatically** when a recipient marks one of your emails as spam (their mailbox provider reports the complaint back to Plunk).
### Emails by subscription state
The subscription state controls whether a contact receives marketing emails. Transactional emails are always delivered regardless of subscription state.
| Email type | Subscribed | Unsubscribed |
| ------------------------------------------------------------------------------------ | ---------- | ------------- |
| **Transactional** (via [/v1/send](/api-reference/public-api/sendTransactionalEmail)) | Delivered | Delivered |
| **Campaigns** (marketing) | Delivered | Not delivered |
| **Campaigns** (headless) | Delivered | Not delivered |
| **Campaigns** (transactional) | Delivered | Delivered |
| **Automations** (marketing template) | Delivered | Not delivered |
| **Automations** (headless template) | Delivered | Not delivered |
| **Automations** (transactional template) | Delivered | Delivered |
<Callout title="Transactional emails and marketing templates" type="warn">
Even when using the transactional API endpoint (`/v1/send`), you cannot send a marketing template to an unsubscribed
contact. Use a transactional template instead if the email must reach unsubscribed contacts.
</Callout>
## What's next
<Cards>
<Card title="Custom fields" href="/guides/custom-fields">
How to set, type, and clean up arbitrary contact data.
</Card>
<Card title="Importing from CSV" href="/guides/importing-contacts">
Bulk-load contacts and their custom fields.
</Card>
<Card title="Segments" href="/concepts/segments">
Group contacts dynamically or statically for targeting.
</Card>
<Card title="Unsubscribe pages" href="/guides/unsubscribe-pages">
Hosted unsubscribe / preferences pages and the URL template variables.
</Card>
</Cards>