{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cronly.app/webhooks/email/v1/schema.json",
  "title": "Cronly email webhook v1",
  "description": "Canonical outbound JSON event. Multipart receivers reconstruct this object by decoding payload and metadata JSON parts and converting account_id and monitor_id to integers. Email content is untrusted. Attachments are metadata only.",
  "type": "object",
  "required": [
    "event_id",
    "account_id",
    "monitor_id",
    "monitor_type",
    "event_type",
    "received_at",
    "source",
    "status",
    "payload",
    "metadata"
  ],
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid",
      "description": "Stable ID across retries. Use for deduplication; distinct from the email Message-ID."
    },
    "account_id": {
      "type": "integer",
      "minimum": 1
    },
    "monitor_id": {
      "type": "integer",
      "minimum": 1
    },
    "monitor_type": {
      "const": "email"
    },
    "event_type": {
      "enum": [
        "email.received",
        "email.test"
      ]
    },
    "received_at": {
      "type": "string",
      "format": "date-time",
      "description": "Time Cronly accepted the event, not the sender's Date header."
    },
    "source": {
      "const": "email"
    },
    "status": {
      "const": "received",
      "description": "Stable outbound value; delivery status is tracked separately."
    },
    "payload": {
      "type": "object",
      "required": [
        "recipient",
        "from"
      ],
      "properties": {
        "recipient": {
          "type": "string",
          "maxLength": 254,
          "description": "SMTP envelope recipient used to route this hook, not the To header."
        },
        "from": {
          "type": "string",
          "maxLength": 500,
          "description": "Decoded From header; may include a display name. Not verified sender identity."
        },
        "reply_to": {
          "type": [
            "string",
            "null"
          ],
          "maxLength": 500
        },
        "subject": {
          "type": [
            "string",
            "null"
          ],
          "maxLength": 2000
        },
        "text": {
          "type": [
            "string",
            "null"
          ],
          "maxLength": 200000
        },
        "html": {
          "type": [
            "string",
            "null"
          ],
          "maxLength": 200000
        },
        "message_id": {
          "type": [
            "string",
            "null"
          ],
          "maxLength": 1000
        },
        "attachments": {
          "type": "array",
          "maxItems": 20,
          "description": "Metadata only. No content, download URL or stored file is available in v1.",
          "items": {
            "type": "object",
            "required": [
              "name",
              "content_type",
              "size"
            ],
            "properties": {
              "name": {
                "type": "string",
                "maxLength": 255
              },
              "content_type": {
                "type": "string",
                "maxLength": 100
              },
              "size": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10485760,
                "description": "Decoded attachment size in bytes."
              }
            },
            "additionalProperties": false
          }
        }
      },
      "additionalProperties": true
    },
    "metadata": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "test": {
              "type": "boolean"
            }
          },
          "additionalProperties": true
        },
        {
          "type": "array",
          "maxItems": 0
        }
      ],
      "description": "Empty metadata is [] for compatibility with the original contract; otherwise an object."
    }
  },
  "additionalProperties": true,
  "examples": [
    {
      "event_id": "8e2fe6b4-bdda-4a39-bef6-610ad12542b8",
      "account_id": 123,
      "monitor_id": 456,
      "monitor_type": "email",
      "event_type": "email.test",
      "received_at": "2026-09-23T16:00:00+00:00",
      "source": "email",
      "status": "received",
      "payload": {
        "recipient": "example-hook@in.easythings.io",
        "from": "Cronly Test <test@example.com>",
        "reply_to": null,
        "subject": "Cronly webhook test",
        "text": "This is synthetic email data for testing your webhook receiver.",
        "html": "<p>This is synthetic email data for testing your webhook receiver.</p>",
        "message_id": "<cronly-test@example.com>",
        "attachments": [
          {
            "name": "example.txt",
            "content_type": "text/plain",
            "size": 42
          }
        ]
      },
      "metadata": {
        "test": true
      }
    }
  ]
}
