openapi: 3.2.0
info:
title: Plunk Public API
description: Open-source email platform API for transactional emails, campaigns, and marketing automation
version: 1.0.0
contact:
name: Plunk Support
url: https://www.useplunk.com
servers:
- url: https://next-api.useplunk.com
description: Production server
security:
- ApiKeyAuth: []
tags:
- name: Public API
description: Public API endpoints for sending emails and tracking events
paths:
/v1/send:
post:
tags:
- Public API
summary: Send transactional email
description: '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.'
operationId: sendEmail
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- to
properties:
to:
oneOf:
- type: string
format: email
description: Simple email address
- type: object
required:
- email
properties:
name:
type: string
description: Recipient display name
email:
type: string
format: email
description: Recipient email address
description: Recipient with name and email
- type: array
items:
oneOf:
- type: string
format: email
- type: object
required:
- email
properties:
name:
type: string
description: Recipient display name
email:
type: string
format: email
description: Recipient email address
description: Array of recipients (strings or objects)
description: Recipient email(s). Can be a string, an object with {name, email}, or an array of either.
subject:
type: string
minLength: 1
maxLength: 998
description: Email subject. Required if no `template` is provided. Cannot contain newline characters.
body:
type: string
minLength: 1
description: Email body (HTML). Required if no `template` is provided.
template:
type: string
format: uuid
description: Template 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.
from:
oneOf:
- type: string
format: email
description: Simple email address
- type: object
required:
- email
properties:
name:
type: string
description: Sender display name
email:
type: string
format: email
description: Sender email address
description: Sender with name and email
description: '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''}).'
name:
type: string
description: '**Deprecated.** Sender display name. Prefer `from: { name, email }`. Used only as a fallback when `from` is a string and no name is set there.'
subscribed:
type: boolean
description: 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`.
data:
type: object
additionalProperties: true
description: '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.'
headers:
type: object
additionalProperties:
type: string
description: 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:
type: string
format: email
description: Reply-to address.
attachments:
type: array
description: 'Email attachments. Default cap: 10 attachments and 10 MB total. The full message size cannot exceed 40 MB.'
maxItems: 10
items:
type: object
required:
- filename
- content
- contentType
properties:
filename:
type: string
maxLength: 255
description: Attachment filename. Cannot contain newline or quote characters.
content:
type: string
description: Base64-encoded file content.
contentType:
type: string
maxLength: 255
description: MIME type (e.g., `application/pdf`, `image/png`).
contentId:
type: string
description: Content-ID for inline images. Required when `disposition` is `inline`. Reference the image in the email body via ``.
disposition:
type: string
enum:
- attachment
- inline
default: attachment
description: Use `inline` together with `contentId` to embed images in the body. Use `attachment` (the default) for downloadable files.
examples:
simple:
summary: Simple transactional email
value:
to: user@example.com
subject: Password Reset Request
body: '
Click the link to reset: {{resetLink}}
' data: resetLink: https://example.com/reset/abc123 withNames: summary: Email with recipient and sender names value: to: name: Jane Doe email: jane@example.com from: name: My Company email: hello@mycompany.com subject: Welcome to Our Service body:We're glad to have you.
data: name: Jane multipleRecipients: summary: Multiple recipients with names value: to: - name: Jane Doe email: jane@example.com - name: John Smith email: john@example.com from: name: Newsletter email: news@mycompany.com subject: Monthly Update body:Please find your invoice attached.
attachments: - filename: invoice.pdf content: JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL... contentType: application/pdf responses: '200': description: Email queued successfully content: application/json: schema: type: object properties: success: type: boolean data: type: object properties: emails: type: array items: type: object properties: contact: type: object properties: id: type: string email: type: string email: type: string description: Plunk email record ID. Use this to correlate webhook events (which include this ID as 'emailId' in the event data) with your send requests. timestamp: type: string format: date-time example: 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' '400': description: Malformed JSON body, or an invalid `Idempotency-Key` header. content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': description: The project is disabled and cannot send. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The `template` ID does not exist in this project. content: application/json: schema: $ref: '#/components/schemas/Error' '409': $ref: '#/components/responses/IdempotencyConflict' '422': $ref: '#/components/responses/ValidationError' parameters: - $ref: '#/components/parameters/IdempotencyKey' /v1/track: post: tags: - Public API summary: Track event description: 'Track an event for a contact. Automatically creates or upserts the contact, then records the event. Tracked events can be used as workflow triggers, segment filters, and audience filters. **Reserved event names** (rejected with `VALIDATION_ERROR` and code `reserved_event`): anything matching `email.*`, `contact.subscribed`, `contact.unsubscribed`, `segment.