Skip to content

Protokol Mail

Protokol Mail is a built-in integration that lets you send transactional emails from your Protokol project. Emails are queued and delivered asynchronously with automatic retry and spam reporting.


Overview

Key characteristics:

  • Async delivery — emails are queued and sent in the background with automatic retries (up to 3 attempts)
  • HTML email support — send rich HTML emails with automatic content sanitization
  • Attachments — supports file attachments via base64-encoded content or multipart form upload (max 5 attachments, 5MB each)
  • Spam reporting — built-in spam report form for recipients to flag unwanted emails
  • Rate limiting — configurable per-project rate limits (default: 100/hour, 1000/day)
  • Pay-as-you-go — optional overage billing when rate limits are exceeded
  • Workflow integration — use the send-email workflow node or listen to email lifecycle events
  • Environment-aware — separate dev and live environments for safe testing

Sending an Email

import { Mail } from "@ptkl/sdk/beta"

const mail = new Mail()
const result = await mail.send({
  to: ["recipient@example.com"],
  subject: "Welcome to our platform",
  body: "<h1>Welcome!</h1><p>Thank you for signing up.</p>",
})

console.log(result.message_id) // UUID of the queued email
curl -X POST https://lemon.protokol.io/luma/integrations/v1/protokol-mail/emails \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "to": ["recipient@example.com"],
    "subject": "Welcome to our platform",
    "body": "<h1>Welcome!</h1><p>Thank you for signing up.</p>"
  }'

Request Body

Field Type Required Description
to string[] Yes Recipient email addresses
cc string[] No CC email addresses
bcc string[] No BCC email addresses
subject string Yes Email subject line
body string Yes Email body (HTML)
reply_to string No Reply-to email address
sender_name string No Custom sender display name
attachments AttachmentInput[] No File attachments (see below)

Attachment Input

Field Type Description
file_id string Optional DMS file ID reference
file_name string File name
mime_type string MIME type (e.g. application/pdf)
content string Base64-encoded file content
size number File size in bytes

Response

{
  "status": true,
  "message_id": "550e8400-e29b-41d4-a716-446655440000",
  "message": "Email queued for sending"
}

Sending with Attachments

import { Mail } from "@ptkl/sdk/beta"
import fs from "fs"

const mail = new Mail()
const pdfContent = fs.readFileSync("invoice.pdf")

const result = await mail.send({
  to: ["billing@example.com"],
  subject: "Invoice #123",
  body: "<p>Please find your invoice attached.</p>",
  reply_to: "billing@company.com",
  sender_name: "Billing Department",
  attachments: [{
    file_name: "invoice.pdf",
    mime_type: "application/pdf",
    content: pdfContent.toString("base64"),
    size: pdfContent.length,
  }],
})
curl -X POST https://lemon.protokol.io/luma/integrations/v1/protokol-mail/emails \
  -H "Authorization: Bearer $TOKEN" \
  -F "to=billing@example.com" \
  -F "subject=Invoice #123" \
  -F "body=<p>Please find your invoice attached.</p>" \
  -F "reply_to=billing@company.com" \
  -F "sender_name=Billing Department" \
  -F "file=@invoice.pdf"

Note

The multipart form endpoint accepts a maximum total payload of 25MB. Individual attachments are limited to 5MB each, with a maximum of 5 attachments per email.


Listing Emails

Retrieve a paginated list of email logs for your project, with optional status filtering.

Query Parameters

Parameter Type Default Description
status string Filter by status: pending, sent, or failed
page number 1 Page number (1-indexed)
limit number 20 Items per page (max 100)
import { Mail } from "@ptkl/sdk/beta"

const mail = new Mail()

// List all emails
const emails = await mail.list()

// List failed emails with pagination
const failed = await mail.list({
  status: "failed",
  page: 2,
  limit: 10,
})
curl -X GET "https://lemon.protokol.io/luma/integrations/v1/protokol-mail/emails?status=failed&page=2&limit=10" \
  -H "Authorization: Bearer $TOKEN"

Getting a Single Email

import { Mail } from "@ptkl/sdk/beta"

const mail = new Mail()
const email = await mail.get("550e8400-e29b-41d4-a716-446655440000")

console.log(email.status)     // "sent"
console.log(email.subject)    // "Welcome to our platform"
curl -X GET https://lemon.protokol.io/luma/integrations/v1/protokol-mail/emails/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer $TOKEN"

Email Log Fields

Field Type Description
message_id string Unique message identifier (UUID)
project_uuid string Project scope
tenant_uuid string Tenant scope
component_uuid string Workflow component reference (if sent from workflow)
env string Environment: dev or live
from string Sender address (auto-generated)
to string[] Recipients
cc string[] CC recipients
bcc string[] BCC recipients
reply_to string Reply-to address
subject string Subject line
body string HTML body
attachments string Attachment metadata (JSON)
status string pending, sent, or failed
retry_count number Number of send attempts
max_retries number Maximum allowed retries (default: 3)
error_message string Last error message (if failed)
sent_at string Timestamp when successfully sent
spam_reported boolean Whether reported as spam
spam_reported_at string When spam was reported

Resending a Failed Email

Re-queue a failed email for another delivery attempt.

import { Mail } from "@ptkl/sdk/beta"

const mail = new Mail()
await mail.resend("550e8400-e29b-41d4-a716-446655440000")
curl -X POST https://lemon.protokol.io/luma/integrations/v1/protokol-mail/emails/550e8400-e29b-41d4-a716-446655440000/resend \
  -H "Authorization: Bearer $TOKEN"

Attachments

Listing Attachments

import { Mail } from "@ptkl/sdk/beta"

const mail = new Mail()
const attachments = await mail.listAttachments("550e8400-e29b-41d4-a716-446655440000")

attachments.forEach(att => {
  console.log(att.file_name, att.mime_type, att.file_size)
})
curl -X GET https://lemon.protokol.io/luma/integrations/v1/protokol-mail/emails/550e8400-e29b-41d4-a716-446655440000/attachments \
  -H "Authorization: Bearer $TOKEN"

Downloading an Attachment

Returns the raw binary content with appropriate Content-Type and Content-Disposition headers.

import { Mail } from "@ptkl/sdk/beta"

const mail = new Mail()
const content = await mail.downloadAttachment(
  "550e8400-e29b-41d4-a716-446655440000",
  "660e8400-e29b-41d4-a716-446655440000"
)
curl -X GET https://lemon.protokol.io/luma/integrations/v1/protokol-mail/emails/550e8400-e29b-41d4-a716-446655440000/attachments/660e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer $TOKEN" \
  --output attachment.pdf

Warning

Attachment binary content is automatically cleaned up after 7 days. Download attachments promptly if you need to retain them.


Permissions

All authenticated endpoints require RBAC permissions:

Permission Endpoints
read protokol-mail List emails, get email, list attachments, download attachment
send protokol-mail Send email, resend email

Rate Limits

Each project has configurable rate limits based on the subscription tier:

Setting Default Description
Emails per hour 100 Maximum emails sent per hour
Emails per day 1000 Maximum emails sent per day
Max body size 500 KB Maximum HTML body size
Max attachments 5 Maximum number of attachments per email
Max attachment size 5 MB Maximum size per individual attachment

Note

If pay-as-you-go is enabled on your subscription, you can exceed rate limits — overage usage is billed separately on an hourly basis.


Workflow Integration

Send Email Node

Use the integration.protokol-mail.send-email workflow action node to send emails as part of a workflow. The node accepts the same fields as the API and queues the email for async delivery.

Email Events

The mail system publishes lifecycle events that can trigger workflows:

Event When
protokol-mail.email.sent Email successfully delivered
protokol-mail.email.failed Email permanently failed (max retries exceeded)
protokol-mail.email.spam_reported Recipient submitted a spam report

Spam Reporting

Each email footer includes a link to a spam report form. Recipients can report unwanted emails, which:

  1. Marks the email as spam-reported in the system
  2. Publishes a protokol-mail.email.spam_reported workflow event
  3. Displays a confirmation page to the reporter

Using in Platform Functions

You can send emails directly from platform functions using the SDK:

const { Mail } = $sdk.version('0.10')
const mail = new Mail()

const result = await mail.send({
  to: [$input.body.email],
  subject: "Your order has been confirmed",
  body: `
    <h1>Order Confirmed</h1>
    <p>Hi ${$input.body.name},</p>
    <p>Your order #${$input.body.order_id} has been confirmed.</p>
  `,
})

response.json({
  success: true,
  message_id: result.message_id,
})

SDK Reference

The Mail class is available in both SDK versions:

// v0.10 (beta)
import { Mail } from "@ptkl/sdk/beta"

// v0.9 (stable)
import { Mail } from "@ptkl/sdk"

// Or via the Integrations facade
import { Integrations } from "@ptkl/sdk/beta"
const integrations = new Integrations()
const mail = integrations.getMail()
Method Description
mail.send(input) Queue an email for sending
mail.list(params?) List emails with optional filtering
mail.get(messageId) Get a single email by ID
mail.resend(messageId) Resend a failed email
mail.listAttachments(messageId) List attachment metadata
mail.downloadAttachment(messageId, attachmentId) Download attachment content

For full type definitions, see the SDK TypeDoc reference.