{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://multimailer.local/schema/campaign.schema.json", "title": "MultiMailer Campaign", "type": "object", "required": [ "version", "campaign", "template", "entries" ], "properties": { "version": { "type": "string", "const": "1.0" }, "campaign": { "type": "object", "required": [ "id", "name" ], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "description": { "type": "string" }, "mode": { "type": "string", "enum": [ "draft", "test", "send" ], "default": "draft" } }, "additionalProperties": false }, "fields": { "type": "array", "items": { "type": "object", "required": [ "name" ], "properties": { "name": { "type": "string" }, "type": { "type": "string", "enum": [ "string", "integer", "double", "date", "password" ], "default": "string" }, "label": { "type": "string" }, "required": { "type": "boolean", "default": false }, "can_override": { "type": "boolean", "default": true, "description": "Whether recipient/entry field values may override the global value for this field." } }, "additionalProperties": false }, "default": [] }, "global_values": { "type": "object", "additionalProperties": true, "default": {} }, "server": { "type": "object", "properties": { "smtp": { "type": "object", "properties": { "host": { "type": "string" }, "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, "username": { "type": "string" }, "password": { "type": "string" }, "security": { "type": "string", "enum": [ "plain", "tls", "starttls" ], "default": "starttls" }, "timeout_seconds": { "type": "integer", "minimum": 1, "default": 30 } }, "additionalProperties": false }, "imap": { "type": "object", "properties": { "enabled": { "type": "boolean", "default": false }, "host": { "type": "string" }, "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, "username": { "type": "string" }, "password": { "type": "string" }, "security": { "type": "string", "enum": [ "plain", "tls", "starttls" ], "default": "tls" }, "sent_folder": { "type": "string", "default": "auto" }, "timeout_seconds": { "type": "integer", "minimum": 1, "default": 30 } }, "additionalProperties": false } }, "additionalProperties": false }, "recipients": { "type": "object", "properties": { "from": { "$ref": "#/$defs/recipient" }, "allow_individual_from": { "type": "boolean", "default": false }, "to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "allow_individual_to": { "type": "boolean", "default": false }, "cc": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "allow_individual_cc": { "type": "boolean", "default": false }, "bcc": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "allow_individual_bcc": { "type": "boolean", "default": false }, "reply_to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "allow_individual_reply_to": { "type": "boolean", "default": false }, "bounce_to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "allow_individual_bounce_to": { "type": "boolean", "default": false }, "disposition_notification_to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "allow_individual_disposition_notification_to": { "type": "boolean", "default": false } }, "additionalProperties": false, "default": {} }, "template": { "oneOf": [ { "type": "object", "required": [ "subject" ], "properties": { "subject": { "type": "string" }, "text": { "type": "string" }, "html": { "type": "string" } }, "additionalProperties": false }, { "type": "object", "required": [ "source" ], "properties": { "source": { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "const": "files" }, "subject_path": { "type": "string" }, "text_path": { "type": "string" }, "html_path": { "type": "string" }, "encoding": { "type": "string", "default": "utf-8" } }, "additionalProperties": false } }, "additionalProperties": false } ] }, "attachments": { "type": "object", "properties": { "base_path": { "type": "string", "default": ".", "description": "Campaign-level base path. Global and entry attachment base_dir values are resolved relative to this path unless absolute." }, "allow_individual": { "type": "boolean", "default": false }, "send_without_attachments": { "type": "boolean", "default": true, "description": "Legacy compatibility flag. Prefer validation_policy and per-config missing_behavior for new campaigns." }, "global": { "type": "array", "items": { "$ref": "#/$defs/attachment_config" }, "default": [] }, "missing_behavior": { "type": "string", "enum": [ "block", "ask", "drop", "continue", "warn" ], "default": "ask" }, "ambiguous_behavior": { "type": "string", "enum": [ "block", "ask", "drop", "continue", "warn" ], "default": "ask" } }, "additionalProperties": false, "default": { "base_path": ".", "global": [] } }, "entries": { "oneOf": [ { "type": "object", "required": [ "inline" ], "properties": { "inline": { "type": "array", "items": { "$ref": "#/$defs/entry" } } }, "additionalProperties": false }, { "type": "object", "required": [ "source", "mapping" ], "properties": { "source": { "$ref": "#/$defs/source" }, "mapping": { "type": "object", "description": "Internal campaign path -> source column/key. Examples: to.0.email, fields.number, attachments.0.file_filter.", "additionalProperties": { "type": "string" } }, "defaults": { "$ref": "#/$defs/entry" } }, "additionalProperties": false } ] }, "validation_policy": { "type": "object", "properties": { "missing_required_attachment": { "type": "string", "enum": [ "block", "ask", "drop", "continue", "warn" ], "default": "ask" }, "missing_optional_attachment": { "type": "string", "enum": [ "block", "ask", "drop", "continue", "warn" ], "default": "warn" }, "ambiguous_attachment_match": { "type": "string", "enum": [ "block", "ask", "drop", "continue", "warn" ], "default": "ask" }, "missing_email": { "type": "string", "enum": [ "block", "drop" ], "default": "block" }, "template_error": { "type": "string", "enum": [ "block", "drop" ], "default": "block" }, "inactive_entry": { "type": "string", "enum": [ "drop", "block", "warn" ], "default": "drop" } }, "additionalProperties": false, "default": {} }, "delivery": { "type": "object", "properties": { "rate_limit": { "type": "object", "properties": { "messages_per_minute": { "type": "integer", "minimum": 1, "default": 5 }, "concurrency": { "type": "integer", "minimum": 1, "default": 1 } }, "additionalProperties": false }, "imap_append_sent": { "type": "object", "properties": { "enabled": { "type": "boolean", "default": false }, "folder": { "type": "string", "default": "auto" } }, "additionalProperties": false }, "retry": { "type": "object", "properties": { "max_attempts": { "type": "integer", "minimum": 1, "default": 3 }, "backoff_seconds": { "type": "array", "items": { "type": "integer", "minimum": 1 }, "default": [ 60, 300, 900 ] } }, "additionalProperties": false } }, "additionalProperties": false, "default": {} }, "status_tracking": { "type": "object", "properties": { "enabled": { "type": "boolean", "default": true }, "initial_build_status": { "type": "string", "enum": [ "built", "build_failed" ], "default": "built" }, "initial_send_status": { "type": "string", "enum": [ "draft", "queued" ], "default": "draft" } }, "additionalProperties": false, "default": { "enabled": true } } }, "additionalProperties": false, "$defs": { "recipient": { "type": "object", "required": [ "email" ], "properties": { "email": { "type": "string", "format": "email" }, "name": { "type": "string" }, "type": { "type": "string", "enum": [ "to", "cc", "bcc", "reply_to", "bounce_to", "disposition_notification_to" ], "default": "to" } }, "additionalProperties": false }, "attachment_config": { "type": "object", "required": [ "base_dir", "file_filter" ], "properties": { "id": { "type": "string", "description": "Optional stable ID for UI/status references." }, "label": { "type": "string" }, "base_dir": { "type": "string", "description": "Directory relative to attachments.base_path unless absolute." }, "file_filter": { "type": "string", "description": "Glob/filter expression, rendered with global/local fields before matching." }, "include_subdirs": { "type": "boolean", "default": false }, "required": { "type": "boolean", "default": true }, "allow_multiple": { "type": "boolean", "default": false }, "missing_behavior": { "type": "string", "enum": [ "block", "ask", "drop", "continue", "warn" ], "default": "ask" }, "ambiguous_behavior": { "type": "string", "enum": [ "block", "ask", "drop", "continue", "warn" ], "default": "ask" }, "zip": { "type": "object", "properties": { "enabled": { "type": "boolean", "default": false }, "filename_template": { "type": "string" }, "password_template": { "type": "string" }, "method": { "type": "string", "enum": [ "zip_standard", "aes" ], "default": "aes" } }, "additionalProperties": false, "default": { "enabled": false } } }, "additionalProperties": false }, "entry": { "type": "object", "properties": { "id": { "type": "string" }, "active": { "type": "boolean", "default": true }, "from": { "$ref": "#/$defs/recipient" }, "to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "combine_to": { "type": "boolean", "default": true }, "cc": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "combine_cc": { "type": "boolean", "default": true }, "bcc": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "combine_bcc": { "type": "boolean", "default": true }, "reply_to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "combine_reply_to": { "type": "boolean", "default": true }, "bounce_to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "combine_bounce_to": { "type": "boolean", "default": true }, "disposition_notification_to": { "type": "array", "items": { "$ref": "#/$defs/recipient" }, "default": [] }, "combine_disposition_notification_to": { "type": "boolean", "default": true }, "attachments": { "type": "array", "items": { "$ref": "#/$defs/attachment_config" }, "default": [] }, "combine_attachments": { "type": "boolean", "default": true }, "fields": { "type": "object", "additionalProperties": true, "default": {} }, "last_sent": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "source": { "type": "object", "required": [ "type", "path" ], "properties": { "type": { "type": "string", "enum": [ "csv", "json" ] }, "path": { "type": "string" }, "delimiter": { "type": "string", "default": ";" }, "encoding": { "type": "string", "default": "utf-8" }, "has_header": { "type": "boolean", "default": true } }, "additionalProperties": false } } }