mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
fix: route email trigger path through standard info channel (#8996)
* docs(skill): document email triggers and S3 attachments
Add an "Email triggers" section to the triggers skill covering the
local-part config, the parsed_email/raw_email/email_extra_args payload,
the URL-style extras convention, where to find trigger_path (only with
a preprocessor, at event.trigger_path), and — most importantly — that
binary attachments are uploaded to the workspace S3 bucket and surface
as `{ s3: "windmill_emails/<job_id>/attachments/<filename>" }`. Scripts
must use wmill.loadS3File / wmill.load_s3_file to read them.
Also pulls EmailTrigger into the schema mappings so a real
`email_trigger.schema.yaml` is generated, and adds Email/Azure to the
trigger kinds list in the CLI agent guidance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref for email trigger path fix
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 26184ab7a4aadfc529dcedf038aa08d36c7ad381
This commit updates the EE repository reference after PR #553 was merged in windmill-ee-private.
Previous ee-repo-ref: 318a46897a605dc9be3817901f35ba5a99a0a525
New ee-repo-ref: 26184ab7a4aadfc529dcedf038aa08d36c7ad381
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
@@ -37,7 +37,7 @@ When a new app needs to be created, YOU run \`wmill app new\` yourself with \`--
|
||||
|
||||
## Triggers
|
||||
|
||||
You MUST use the \`triggers\` skill to configure HTTP routes, WebSocket, Kafka, NATS, SQS, MQTT, GCP, or Postgres CDC triggers.
|
||||
You MUST use the \`triggers\` skill to configure HTTP routes, WebSocket, Kafka, NATS, SQS, MQTT, GCP, Azure, Email, or Postgres CDC triggers.
|
||||
|
||||
## Schedules
|
||||
|
||||
|
||||
@@ -5563,6 +5563,49 @@ Examples:
|
||||
- \`u/user/webhook.http_trigger.yaml\`
|
||||
- \`f/data/kafka_consumer.kafka_trigger.yaml\`
|
||||
- \`f/sync/postgres_cdc.postgres_trigger.yaml\`
|
||||
- \`f/inbound/orders.email_trigger.yaml\`
|
||||
|
||||
## Email Triggers
|
||||
|
||||
An email trigger routes incoming emails to a script or flow. Each trigger reserves a local-part: emails sent to \`<local_part>@<windmill_email_domain>\` are delivered to the configured runnable. Set \`workspaced_local_part: true\` to namespace it per workspace (the actual recipient becomes \`<workspace_id>-<local_part>@…\`); on Windmill Cloud this is required.
|
||||
|
||||
Senders may append URL-style extras to the local-part with \`+\`: \`mytrigger+foo=bar+baz=qux@…\`. They flow through to the script as \`email_extra_args\` (see below).
|
||||
|
||||
### Payload
|
||||
|
||||
The runnable receives:
|
||||
|
||||
- \`parsed_email\` — \`{ headers, text_body, html_body, attachments[] }\`. Each \`attachment\` has \`{ headers, body }\`.
|
||||
- \`raw_email\` — the raw RFC 822 message as a string, **or** an S3 object (\`{ s3: "windmill_emails/<job_id>/raw.eml" }\`) if the message exceeds 1 MiB.
|
||||
- \`email_extra_args\` (optional, only when sender appended \`+key=value\` extras) — a flat object of the parsed extras.
|
||||
|
||||
With a preprocessor, all of the above are nested under \`event\` along with \`event.kind = "email"\` and \`event.trigger_path\` (the trigger's path). Without a preprocessor, \`trigger_path\` is **not** exposed — add a preprocessor if you need it.
|
||||
|
||||
### Attachments are S3 objects
|
||||
|
||||
Binary attachments are uploaded to the workspace S3 bucket and surface in \`parsed_email.attachments[i].body\` as:
|
||||
|
||||
\`\`\`json
|
||||
{ "s3": "windmill_emails/<job_id>/attachments/<filename>" }
|
||||
\`\`\`
|
||||
|
||||
To read the bytes inside a script, use the wmill SDK:
|
||||
|
||||
\`\`\`ts
|
||||
// TypeScript
|
||||
import * as wmill from "windmill-client"
|
||||
const file = await wmill.loadS3File(parsed_email.attachments[0].body)
|
||||
\`\`\`
|
||||
|
||||
\`\`\`python
|
||||
# Python
|
||||
import wmill
|
||||
data = wmill.load_s3_file(parsed_email["attachments"][0]["body"])
|
||||
\`\`\`
|
||||
|
||||
If the workspace has no S3 resource configured (Workspace Settings → Object storage), \`body\` falls back to the string \`"configure s3 in the workspace settings to handle attachments"\`. The same applies to large \`raw_email\` bodies. Email attachment storage requires the server to be built with the \`parquet\` feature.
|
||||
|
||||
Text/HTML/inline parts are placed inline in \`body\` as strings.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
@@ -7180,6 +7223,71 @@ required:
|
||||
- azure_mode
|
||||
- scope_resource_id
|
||||
- subscription_name
|
||||
`,
|
||||
"email_trigger": `type: object
|
||||
properties:
|
||||
script_path:
|
||||
type: string
|
||||
description: Path to the script or flow to execute when triggered
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: The user or group this trigger runs as (permissioned_as)
|
||||
is_flow:
|
||||
type: boolean
|
||||
description: True if script_path points to a flow, false if it points to a script
|
||||
labels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
local_part:
|
||||
type: string
|
||||
workspaced_local_part:
|
||||
type: boolean
|
||||
error_handler_path:
|
||||
type: string
|
||||
error_handler_args:
|
||||
type: object
|
||||
description: The arguments to pass to the script or flow
|
||||
retry:
|
||||
type: object
|
||||
properties:
|
||||
constant:
|
||||
type: object
|
||||
description: Retry with constant delay between attempts
|
||||
properties:
|
||||
attempts:
|
||||
type: integer
|
||||
description: Number of retry attempts
|
||||
seconds:
|
||||
type: integer
|
||||
description: Seconds to wait between retries
|
||||
exponential:
|
||||
type: object
|
||||
description: Retry with exponential backoff (delay doubles each time)
|
||||
properties:
|
||||
attempts:
|
||||
type: integer
|
||||
description: Number of retry attempts
|
||||
multiplier:
|
||||
type: integer
|
||||
description: Multiplier for exponential backoff
|
||||
seconds:
|
||||
type: integer
|
||||
minimum: 1
|
||||
description: Initial delay in seconds
|
||||
random_factor:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: Random jitter percentage (0-100) to avoid thundering herd
|
||||
retry_if:
|
||||
$ref: '#/components/schemas/RetryIf'
|
||||
description: Retry configuration for failed module executions
|
||||
required:
|
||||
- script_path
|
||||
- permissioned_as
|
||||
- is_flow
|
||||
- local_part
|
||||
`,
|
||||
"gcp_trigger": `type: object
|
||||
properties:
|
||||
@@ -8116,6 +8224,7 @@ export const SCHEMA_MAPPINGS: Record<string, SchemaMapping[]> = {
|
||||
{ name: "SqsTrigger", schemaKey: "sqs_trigger", filePattern: "*.sqs_trigger.yaml" },
|
||||
{ name: "GcpTrigger", schemaKey: "gcp_trigger", filePattern: "*.gcp_trigger.yaml" },
|
||||
{ name: "AzureTrigger", schemaKey: "azure_trigger", filePattern: "*.azure_trigger.yaml" },
|
||||
{ name: "EmailTrigger", schemaKey: "email_trigger", filePattern: "*.email_trigger.yaml" },
|
||||
],
|
||||
"schedules": [
|
||||
{ name: "Schedule", schemaKey: "schedule", filePattern: "*.schedule.yaml" },
|
||||
|
||||
Reference in New Issue
Block a user