PlunkPlunk
Concepts

Contacts

Manage and organize your contacts effectively

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 — tracking an event for a contact that doesn't exist yet automatically creates it (subscribed by default).
  • Using POST /contacts — 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:

TypeDescription
StringAny text value that doesn't look like a date.
NumberNumeric values, including integers and floats.
Booleantrue or false.
DateA 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.

Default data type

If a value doesn't match number, boolean, or the ISO 8601 date format, it's typed as a string.

Non-persistent values

Send a value as { value, persistent: false } to use it once for template rendering without storing it on the contact:

{
  "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:

ValueBehaviorExample
Empty string ("")Ignored - field is not stored or updated{ name: "" } → Field is skipped
nullDelete - field is removed from contact data{ name: null } → Field is deleted
Other valuesStored/updated normally{ name: "John" } → Stored as "John"

Removing contact data

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.

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:

KeyDescription
emailThe contact's email address
createdAtTimestamp of when the contact was created
updatedAtTimestamp of the last update
subscribedBoolean 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).

Special keys

KeyDescription
localeThe 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 truecontact.subscribed event
  • subscribed flipped to falsecontact.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 typeSubscribedUnsubscribed
Transactional (via /v1/send)DeliveredDelivered
Campaigns (marketing)DeliveredNot delivered
Campaigns (headless)DeliveredNot delivered
Campaigns (transactional)DeliveredDelivered
Automations (marketing template)DeliveredNot delivered
Automations (headless template)DeliveredNot delivered
Automations (transactional template)DeliveredDelivered

Transactional emails and marketing templates

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.

What's next