Email Trigger
Trigger a workflow when an email is sent to your workflow's dedicated address.
The Email trigger gives your workflow a dedicated email address. Whenever an email arrives at that address, the workflow fires with the parsed email contents (sender, subject, body, and any attachments) available in data.
The trigger address
Each email-trigger workflow is assigned a unique address in the format:
{shop-name}.{workflow-id}@jsworkflow.com
The address is shown on the workflow detail page under Trigger email address.
Caution: Treat the trigger address as a secret. Anyone who knows it can trigger your workflow by sending an email to it.
Sender whitelist
By default, the trigger accepts email from any sender. To restrict to specific senders, add a comma-separated list of allowed email addresses under Trigger email address on the workflow detail page.
orders@myapp.com, alerts@monitoring.io
Emails from addresses not on the list are silently rejected with a 403 and any uploaded attachments are cleaned up immediately. Leave the field empty to accept all senders.
The data object
data is the parsed email, passed directly to your start step:
{
from: "sender@example.com",
to: "mystore.abc123@jsworkflow.com",
subject: "New order confirmation",
text: "Plain-text body of the email",
html: "
HTML body of the email
",
attachments: [
{
filename: "invoice.pdf",
contentType: "application/pdf",
key: "mystore/abc123/email-/invoice.pdf"
}
]
}
| Field | Type | Description |
| from | string | Sender's email address |
| to | string | The trigger address the email was sent to |
| subject | string | Email subject line |
| text | string | Plain-text body (empty string if none) |
| html | string | HTML body (empty string if none) |
| attachments | array | List of attachment descriptors (see below) |
Attachment descriptor
Each item in data.attachments describes one attached file:
| Field | Type | Description |
| filename | string | Original filename of the attachment |
| contentType | string | MIME type, e.g. application/pdf, image/png |
| key | string | Storage key; pass to api.getAttachment() to retrieve the file |
Attachments are stored for 7 days from the time the email was received. After that, the key becomes invalid and api.getAttachment() returns null.
Accessing attachments
Use api.getAttachment(key) to retrieve the raw file content as an ArrayBuffer. This method is documented in full on the Attachments (/workflow-api/attachments/) page.
Put attachment work in a retryable step
The start step is not retryable. If your code throws after reading an attachment, start cannot be re-run. The safe pattern is to pass the key (not the file content) into a subsequent step, which IS retryable:
export class Workflow {
async start(data, headers, api) {
for (const att of data.attachments) {
await api.scheduleNextStep({
action: 'processAttachment',
payload: { key: att.key, filename: att.filename, contentType: att.contentType }
});
}
}
async processAttachment(data, headers, api) {
// This step is retryable, safe to fetch the file here
const buffer = await api.getAttachment(data.key);
if (!buffer) {
console.log('Attachment expired or not found:', data.key);
return;
}
// Convert to base64 for an API that expects it
const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
console.log(Processed ${data.filename} (${buffer.byteLength} bytes));
// Clean up once you're done
await api.deleteAttachment(data.key);
}
}
Example: process an emailed invoice
export class Workflow {
async start(data, headers, api) {
console.log('Email from:', data.from);
console.log('Subject:', data.subject);
console.log('Attachments:', data.attachments.length);
if (data.attachments.length === 0) {
console.log('No attachments, nothing to do.');
return;
}
// Schedule one step per attachment
for (const att of data.attachments) {
if (!att.contentType.startsWith('application/pdf')) continue;
await api.scheduleNextStep({
action: 'uploadInvoice',
payload: { key: att.key, filename: att.filename, sender: data.from }
});
}
}
async uploadInvoice(data, headers, api) {
const buffer = await api.getAttachment(data.key);
if (!buffer) {
console.log('Attachment no longer available:', data.key);
return;
}
// Upload the PDF to an external service
const formData = new FormData();
formData.append('file', new Blob([buffer], { type: 'application/pdf' }), data.filename);
formData.append('sender', data.sender);
const res = await fetch('https://your-api.example.com/invoices', {
method: 'POST',
body: formData,
});
console.log('Upload result:', res.status);
// Remove from storage now that we're done
await api.deleteAttachment(data.key);
}
}
Testing email-trigger workflows
After your workflow has been triggered at least once, open the workflow code editor and select More actions > Workflow runs. Click the Email link in the trigger column for any run to view the full payload in a modal. From there, click Use as test data to copy the payload and headers into the test input, useful for re-running with the same email content.
Note: Attachment files referenced by key values in a saved payload remain accessible via api.getAttachment() for up to 7 days from when the original email was received.