{
  "openapi": "3.1.0",
  "info": {
    "title": "Cronly outbound email webhooks",
    "version": "1.0.0",
    "description": "Receiver contract, not Cronly's privileged inbound SMTP adapter API. Configure your HTTPS receiver URL on a Cronly email hook. JSON is the default; multipart is selectable per hook. Every event has a stable ID and pinned version. Both formats forward attachment metadata only. Verify signatures before parsing. Return 2xx only after durable acceptance. Ordinary failures require manual retry (maximum five total attempts); interrupted workers recover automatically. Delivery is at least once and unordered. Test events use email.test, metadata.test=true and count against the event allowance. Existing JSON body structure is retained; this is not CloudEvents."
  },
  "externalDocs": {
    "url": "https://docs.cronly.app/service-guides/email-hooks"
  },
  "webhooks": {
    "emailEvent": {
      "post": {
        "operationId": "receiveCronlyEmail",
        "summary": "Receive an email.received or email.test event",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stable event UUID; equals body event_id."
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds for this attempt; verify within 300 seconds."
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Standard Webhooks HMAC-SHA256 signature: v1,<base64 signature>. Sign webhook-id + '.' + webhook-timestamp + '.' + exact raw body, using the base64-decoded whsec_ key."
          },
          {
            "name": "X-Cronly-Schema-Version",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Pinned contract version: 1."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Same event UUID."
          },
          {
            "name": "X-Cronly-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Same event UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "https://cronly.app/webhooks/email/v1/schema.json"
              },
              "example": {
                "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
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "https://cronly.app/webhooks/email/v1/schema.json"
              },
              "encoding": {
                "payload": {
                  "contentType": "application/json"
                },
                "metadata": {
                  "contentType": "application/json"
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Event durably accepted, or previously accepted duplicate."
          },
          "400": {
            "description": "Invalid payload; recorded as failed, no automatic retry."
          },
          "401": {
            "description": "Signature verification failed."
          },
          "429": {
            "description": "Receiver temporarily busy; recorded as failed; manual retry currently required."
          },
          "500": {
            "description": "Receiver failure; recorded as failed; manual retry currently required."
          }
        },
        "x-cronly-schema-version": "1"
      }
    }
  }
}
