Send transactional email
Send a transactional email via the public API. Automatically creates or updates the recipient contact.
Required content: either a template ID, or both subject and body. Template fields can be overridden by explicit request fields.
Sender: from is required unless using a template that already has a from configured. The sender's domain must be verified.
Multiple recipients: when to is an array, each recipient is processed sequentially with its own contact upsert and rendered email — there is no batch-send semantics. Sending is always immediate; for scheduled sends, use a Campaign.
Attachments: up to 10 attachments per email and 10 MB total by default. The total message size cannot exceed 40 MB.
Authorization
ApiKeyAuth API Key authentication. Secret keys (sk_*) are required for all endpoints except /v1/track. Public keys (pk_*) only work with the /v1/track endpoint for client-side event tracking. The project is automatically derived from the key.
In: header
Request Body
application/json
Recipient email(s). Can be a string, an object with {name, email}, or an array of either.
Email subject. Required if no template is provided. Cannot contain newline characters.
1 <= length <= 998Email body (HTML). Required if no template is provided.
1 <= lengthTemplate ID to use for this email. When provided, uses the template's subject, body, from, and reply-to settings. You can override these by explicitly providing subject, body, from, or reply fields in the request. Template variables are populated from the data field.
Sender email address (requires verified domain). Required unless using a template that has a 'from' address configured. Can be a string (e.g., 'hello@example.com') or an object with {name, email} (e.g., {name: 'My App', email: 'hello@example.com'}).
Deprecated. Sender display name. Prefer from: { name, email }. Used only as a fallback when from is a string and no name is set there.
Subscription state to apply to the recipient. For new contacts, defaults to false on /v1/send. For existing contacts, omitting this preserves their current state — pass true or false to explicitly change it. A change emits contact.subscribed or contact.unsubscribed.
Variables for template rendering and contact data updates. Each value can be:
- A primitive (string, number, boolean) — saved on the contact and available as a template variable.
null— deletes the field from the contact.- An empty string — skipped (does not overwrite existing data).
- An object
{ value, persistent: false }— used for this send only, not stored on the contact (good for one-shot password reset codes, magic links).
Reserved keys (id, plunk_id, plunk_email, email, unsubscribeUrl, subscribeUrl, manageUrl) are silently filtered out.
Custom email headers. Header names cannot contain \r\n. Header values are limited to 998 characters and cannot contain \r\n (header injection is rejected).
Reply-to address.
emailEmail attachments. Default cap: 10 attachments and 10 MB total. The full message size cannot exceed 40 MB.
items <= 10Response Body
application/json
application/json
application/json
curl -X POST "https://next-api.useplunk.com/v1/send" \ -H "Content-Type: application/json" \ -d '{ "to": "user@example.com", "subject": "Password Reset Request", "body": "<h1>Reset Your Password</h1><p>Click the link to reset: {{resetLink}}</p>", "data": { "resetLink": "https://example.com/reset/abc123" } }'{
"success": true,
"data": {
"emails": [
{
"contact": {
"id": "cnt_abc123",
"email": "user@example.com"
},
"email": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf"
}
],
"timestamp": "2025-01-15T10:30:00.000Z"
}
}{
"code": 0,
"error": "string",
"message": "string",
"time": 0
}{
"code": 0,
"error": "string",
"message": "string",
"time": 0
}