From 172f24a7b3ca135ed011669b2fd41ed9cc0c5495 Mon Sep 17 00:00:00 2001 From: Ivan Yurchanka Date: Fri, 10 Apr 2026 12:49:21 +0200 Subject: [PATCH 1/5] Add webhooks API endpoints to email sending spec Adds full CRUD documentation for the webhooks API based on the public API changes in railsware/falcon#9954. Includes list, get, create, update, and delete endpoints with schemas, examples, and cURL samples. Co-Authored-By: Claude Opus 4.6 (1M context) --- specs/email-sending.openapi.yml | 492 ++++++++++++++++++++++++++++++++ 1 file changed, 492 insertions(+) diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index a9d7c7d..9f2394c 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -45,6 +45,14 @@ tags: x-page-description: Email logs description: List and retrieve email sending logs for the account. + - name: webhooks + x-page-title: Webhooks + x-page-description: Manage webhook endpoints + description: | + Configure webhooks to receive real-time notifications about email events such as deliveries, bounces, opens, clicks, and more. + + Webhooks can be scoped to a specific sending domain or apply to all domains in the account. Each webhook has a signing secret (returned only on creation) that you can use to verify the authenticity of incoming webhook payloads. + paths: /api/accounts/{account_id}/sending_domains: post: @@ -1358,6 +1366,399 @@ paths: '429': $ref: '#/components/responses/LIMIT_EXCEEDED' + /api/accounts/{account_id}/webhooks: + get: + operationId: listWebhooks + summary: List webhooks + description: Returns all webhooks for the account. Results are ordered by ID descending (newest first). + tags: + - webhooks + x-codeSamples: + - lang: shell + label: 'cURL' + source: | + curl -X GET https://mailtrap.io/api/accounts/{account_id}/webhooks \ + -H 'Authorization: Bearer YOUR_API_KEY' + parameters: + - $ref: '#/components/parameters/account_id' + responses: + '200': + description: List of webhooks + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Webhook' + example: + data: + - id: 1 + url: 'https://example.com/webhooks/transactional' + active: true + webhook_type: sending_transactional + payload_format: json + sending_mode: transactional + mailsend_domain_id: 435 + event_types: + - delivery + - bounce + domain_name: mailtrap.io + - id: 2 + url: 'https://example.com/webhooks/bulk' + active: true + webhook_type: sending_bulk + payload_format: jsonlines + sending_mode: campaigns + mailsend_domain_id: 436 + event_types: + - delivery + - bounce + - open + - click + domain_name: notifications.mailtrap.io + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + + post: + operationId: createWebhook + summary: Create a webhook + description: | + Create a new webhook for the account. The response includes a `signing_secret` that can be used to verify webhook payloads. The signing secret is only returned in the create response and cannot be retrieved later. + + {% hint style="warning" %} + Make sure to store the `signing_secret` from the response. It is only returned once upon creation. + {% endhint %} + tags: + - webhooks + x-codeSamples: + - lang: shell + label: 'cURL' + source: | + curl -X POST https://mailtrap.io/api/accounts/{account_id}/webhooks \ + -H 'Authorization: Bearer YOUR_API_KEY' \ + -H 'Content-Type: application/json' \ + -d '{ + "webhook": { + "url": "https://example.com/webhooks", + "webhook_type": "sending_transactional", + "payload_format": "json", + "sending_mode": "transactional", + "event_types": ["delivery", "bounce"], + "mailsend_domain_id": 435 + } + }' + parameters: + - $ref: '#/components/parameters/account_id' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - webhook + properties: + webhook: + type: object + required: + - url + - webhook_type + properties: + url: + type: string + format: uri + description: The URL that will receive webhook payloads. Must be a valid HTTP(S) URL. + example: 'https://example.com/webhooks' + webhook_type: + type: string + description: | + The type of webhook. Determines which events the webhook can subscribe to. + + - `sending_transactional` - Events for transactional email sending. Requires `sending_mode` set to `transactional` and `event_types`. + - `sending_bulk` - Events for bulk/campaign email sending. Requires `sending_mode` set to `campaigns` and `event_types`. + - `activity_log` - Account activity log events. Does not accept `sending_mode`, `mailsend_domain_id`, or `event_types`. + enum: + - sending_transactional + - sending_bulk + - activity_log + example: sending_transactional + active: + type: boolean + description: Whether the webhook is active. Defaults to `true`. + default: true + example: true + payload_format: + type: string + description: Format of the webhook payload. + enum: + - json + - jsonlines + default: json + example: json + sending_mode: + type: string + description: | + Required for `sending_transactional` and `sending_bulk` webhook types. Must match the webhook type: `transactional` for `sending_transactional`, `campaigns` for `sending_bulk`. + enum: + - transactional + - campaigns + example: transactional + event_types: + type: array + description: | + List of event types to subscribe to. Required for `sending_transactional` and `sending_bulk` webhook types. Must not be set for `activity_log` type. + items: + type: string + enum: + - delivery + - soft_bounce + - bounce + - suspension + - unsubscribe + - open + - spam_complaint + - click + - reject + example: + - delivery + - bounce + mailsend_domain_id: + type: integer + description: | + ID of the sending domain to scope this webhook to. If omitted, the webhook applies to all domains. Must not be set for `activity_log` type. + example: 435 + responses: + '200': + description: Webhook created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookCreateResponse' + example: + data: + id: 1 + url: 'https://example.com/webhooks' + active: true + webhook_type: sending_transactional + payload_format: json + sending_mode: transactional + mailsend_domain_id: 435 + event_types: + - delivery + - bounce + domain_name: mailtrap.io + signing_secret: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/UnprocessableEntity' + + /api/accounts/{account_id}/webhooks/{webhook_id}: + get: + operationId: getWebhook + summary: Get a webhook + description: Returns a single webhook by ID. + tags: + - webhooks + x-codeSamples: + - lang: shell + label: 'cURL' + source: | + curl -X GET https://mailtrap.io/api/accounts/{account_id}/webhooks/{webhook_id} \ + -H 'Authorization: Bearer YOUR_API_KEY' + parameters: + - $ref: '#/components/parameters/account_id' + - $ref: '#/components/parameters/webhook_id' + responses: + '200': + description: Webhook details + content: + application/json: + schema: + type: object + properties: + data: + $ref: '#/components/schemas/Webhook' + example: + data: + id: 1 + url: 'https://example.com/webhooks' + active: true + webhook_type: sending_transactional + payload_format: json + sending_mode: transactional + mailsend_domain_id: 435 + event_types: + - delivery + - bounce + domain_name: mailtrap.io + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + + patch: + operationId: updateWebhook + summary: Update a webhook + description: Update an existing webhook. Only the fields provided in the request body will be updated. + tags: + - webhooks + x-codeSamples: + - lang: shell + label: 'cURL' + source: | + curl -X PATCH https://mailtrap.io/api/accounts/{account_id}/webhooks/{webhook_id} \ + -H 'Authorization: Bearer YOUR_API_KEY' \ + -H 'Content-Type: application/json' \ + -d '{ + "webhook": { + "url": "https://example.com/webhooks/updated", + "active": false + } + }' + parameters: + - $ref: '#/components/parameters/account_id' + - $ref: '#/components/parameters/webhook_id' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - webhook + properties: + webhook: + type: object + properties: + url: + type: string + format: uri + description: The URL that will receive webhook payloads. Must be a valid HTTP(S) URL. + example: 'https://example.com/webhooks/updated' + active: + type: boolean + description: Whether the webhook is active. + example: false + payload_format: + type: string + description: Format of the webhook payload. + enum: + - json + - jsonlines + example: json + sending_mode: + type: string + description: | + Must match the webhook type: `transactional` for `sending_transactional`, `campaigns` for `sending_bulk`. + enum: + - transactional + - campaigns + example: transactional + event_types: + type: array + description: List of event types to subscribe to. + items: + type: string + enum: + - delivery + - soft_bounce + - bounce + - suspension + - unsubscribe + - open + - spam_complaint + - click + - reject + example: + - delivery + - bounce + - unsubscribe + responses: + '200': + description: Webhook updated successfully + content: + application/json: + schema: + type: object + properties: + data: + $ref: '#/components/schemas/Webhook' + example: + data: + id: 1 + url: 'https://example.com/webhooks/updated' + active: false + webhook_type: sending_transactional + payload_format: json + sending_mode: transactional + mailsend_domain_id: 435 + event_types: + - delivery + - bounce + - unsubscribe + domain_name: mailtrap.io + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/UnprocessableEntity' + + delete: + operationId: deleteWebhook + summary: Delete a webhook + description: Permanently delete a webhook. Returns the deleted webhook data. + tags: + - webhooks + x-codeSamples: + - lang: shell + label: 'cURL' + source: | + curl -X DELETE https://mailtrap.io/api/accounts/{account_id}/webhooks/{webhook_id} \ + -H 'Authorization: Bearer YOUR_API_KEY' + parameters: + - $ref: '#/components/parameters/account_id' + - $ref: '#/components/parameters/webhook_id' + responses: + '200': + description: Webhook deleted successfully + content: + application/json: + schema: + type: object + properties: + data: + $ref: '#/components/schemas/Webhook' + example: + data: + id: 1 + url: 'https://example.com/webhooks' + active: true + webhook_type: activity_log + payload_format: json + sending_mode: null + mailsend_domain_id: null + event_types: [] + domain_name: null + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + components: securitySchemes: HeaderAuth: @@ -1408,6 +1809,15 @@ components: type: string format: uuid + webhook_id: + name: webhook_id + in: path + required: true + description: Webhook ID + schema: + type: integer + example: 1 + StartDateQueryFilter: name: start_date description: Start date for which to include the results. @@ -1672,6 +2082,88 @@ components: type: string format: date-time + Webhook: + type: object + properties: + id: + type: integer + example: 1 + url: + type: string + format: uri + example: 'https://example.com/webhooks' + active: + type: boolean + example: true + webhook_type: + type: string + enum: + - sending_transactional + - sending_bulk + - activity_log + example: sending_transactional + payload_format: + type: string + enum: + - json + - jsonlines + example: json + sending_mode: + type: + - string + - 'null' + enum: + - transactional + - campaigns + - null + description: | + The sending mode. Set to `transactional` for `sending_transactional` webhooks, `campaigns` for `sending_bulk` webhooks. Null for `activity_log` webhooks. + example: transactional + mailsend_domain_id: + type: + - integer + - 'null' + description: ID of the sending domain this webhook is scoped to. Null if the webhook applies to all domains. + example: 435 + event_types: + type: array + description: List of event types the webhook is subscribed to. Empty for `activity_log` webhooks. + items: + type: string + enum: + - delivery + - soft_bounce + - bounce + - suspension + - unsubscribe + - open + - spam_complaint + - click + - reject + example: + - delivery + - bounce + domain_name: + type: + - string + - 'null' + description: Domain name of the associated sending domain. Null if not scoped to a domain. + example: mailtrap.io + + WebhookCreateResponse: + type: object + properties: + data: + allOf: + - $ref: '#/components/schemas/Webhook' + - type: object + properties: + signing_secret: + type: string + description: | + Secret key for verifying webhook payload signatures using HMAC SHA-256. Only returned on creation. + example: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 + UnprocessableEntity: type: object properties: From d7f0b56c643ec90d744086543ab17d0fcda4cdbe Mon Sep 17 00:00:00 2001 From: Ivan Yurchanka Date: Fri, 10 Apr 2026 12:58:03 +0200 Subject: [PATCH 2/5] Update webhooks description to cover all webhook types Include activity log and mention all supported webhook types instead of focusing only on email sending events. Co-Authored-By: Claude Opus 4.6 (1M context) --- specs/email-sending.openapi.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index 9f2394c..14b4471 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -49,9 +49,12 @@ tags: x-page-title: Webhooks x-page-description: Manage webhook endpoints description: | - Configure webhooks to receive real-time notifications about email events such as deliveries, bounces, opens, clicks, and more. + Configure webhooks to receive real-time notifications about events in your Mailtrap account. Supported webhook types: - Webhooks can be scoped to a specific sending domain or apply to all domains in the account. Each webhook has a signing secret (returned only on creation) that you can use to verify the authenticity of incoming webhook payloads. + - **Sending (Transactional & Bulk)** - Email sending events such as deliveries, bounces, opens, clicks, unsubscribes, and more. Can be scoped to a specific sending domain or apply to all domains. + - **Activity Log** - [User audit log](https://docs.mailtrap.io/account-and-organization/privacy-and-security/activity-log#using-user-audit-log-with-mailtrap-webhooks) events for monitoring account activities live, such as user profile changes, domain additions, and other administrative actions. + + Each webhook has a signing secret (returned only on creation) that you can use to verify the authenticity of incoming webhook payloads using HMAC SHA-256. paths: /api/accounts/{account_id}/sending_domains: From 85e1469a20bb546bbd0239fc614f0821e60893d1 Mon Sep 17 00:00:00 2001 From: Ivan Yurchanka Date: Fri, 10 Apr 2026 12:59:22 +0200 Subject: [PATCH 3/5] Enrich webhooks description with payload, signature, and delivery details Add information from the webhooks docs page: payload batching and formats, signature verification via Mailtrap-Signature header, delivery requirements, and retry behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- specs/email-sending.openapi.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index 14b4471..e3a55db 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -49,12 +49,27 @@ tags: x-page-title: Webhooks x-page-description: Manage webhook endpoints description: | - Configure webhooks to receive real-time notifications about events in your Mailtrap account. Supported webhook types: + Configure webhooks to receive real-time notifications about events in your Mailtrap account. For a complete guide, see [Webhooks](https://docs.mailtrap.io/email-api-smtp/advanced/webhooks). + + **Supported webhook types:** - **Sending (Transactional & Bulk)** - Email sending events such as deliveries, bounces, opens, clicks, unsubscribes, and more. Can be scoped to a specific sending domain or apply to all domains. - **Activity Log** - [User audit log](https://docs.mailtrap.io/account-and-organization/privacy-and-security/activity-log#using-user-audit-log-with-mailtrap-webhooks) events for monitoring account activities live, such as user profile changes, domain additions, and other administrative actions. - Each webhook has a signing secret (returned only on creation) that you can use to verify the authenticity of incoming webhook payloads using HMAC SHA-256. + **Payload delivery:** + + - Events are batched and delivered every 30 seconds, up to 500 events per request. + - **JSON** format (`application/json`) wraps events in `{"events": [...]}`. + - **JSON Lines** format (`application/jsonl`) sends one event per line. + + **Signature verification:** + + Each webhook has a signing secret (returned only on creation) used to sign payloads with HMAC SHA-256. The signature is sent in the `Mailtrap-Signature` header. See [Verify webhook signature](https://docs.mailtrap.io/email-api-smtp/advanced/webhooks#verify-webhook-signature) for details. + + **Delivery requirements:** + + - Your endpoint must respond with HTTP `200` within 30 seconds. + - Failed deliveries are retried up to 40 times every 5 minutes. After all retries are exhausted, the webhook is automatically paused. paths: /api/accounts/{account_id}/sending_domains: @@ -1431,7 +1446,7 @@ paths: operationId: createWebhook summary: Create a webhook description: | - Create a new webhook for the account. The response includes a `signing_secret` that can be used to verify webhook payloads. The signing secret is only returned in the create response and cannot be retrieved later. + Create a new webhook for the account. The response includes a `signing_secret` that is used to [verify webhook signatures](https://docs.mailtrap.io/email-api-smtp/advanced/webhooks#verify-webhook-signature) via HMAC SHA-256 (sent in the `Mailtrap-Signature` header). The signing secret is only returned in the create response and cannot be retrieved later. {% hint style="warning" %} Make sure to store the `signing_secret` from the response. It is only returned once upon creation. From 65399aea6ff3a618571e81847a53f1d959a811fc Mon Sep 17 00:00:00 2001 From: Ivan Yurchanka Date: Fri, 10 Apr 2026 15:13:14 +0200 Subject: [PATCH 4/5] Rename mailsend_domain_id to domain_id in webhooks API Co-Authored-By: Claude Opus 4.6 (1M context) --- specs/email-sending.openapi.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index e3a55db..b966655 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -1388,7 +1388,7 @@ paths: get: operationId: listWebhooks summary: List webhooks - description: Returns all webhooks for the account. Results are ordered by ID descending (newest first). + description: Returns all webhooks for the account. tags: - webhooks x-codeSamples: @@ -1419,7 +1419,7 @@ paths: webhook_type: sending_transactional payload_format: json sending_mode: transactional - mailsend_domain_id: 435 + domain_id: 435 event_types: - delivery - bounce @@ -1430,7 +1430,7 @@ paths: webhook_type: sending_bulk payload_format: jsonlines sending_mode: campaigns - mailsend_domain_id: 436 + domain_id: 436 event_types: - delivery - bounce @@ -1467,7 +1467,7 @@ paths: "payload_format": "json", "sending_mode": "transactional", "event_types": ["delivery", "bounce"], - "mailsend_domain_id": 435 + "domain_id": 435 } }' parameters: @@ -1499,7 +1499,7 @@ paths: - `sending_transactional` - Events for transactional email sending. Requires `sending_mode` set to `transactional` and `event_types`. - `sending_bulk` - Events for bulk/campaign email sending. Requires `sending_mode` set to `campaigns` and `event_types`. - - `activity_log` - Account activity log events. Does not accept `sending_mode`, `mailsend_domain_id`, or `event_types`. + - `activity_log` - Account activity log events. Does not accept `sending_mode`, `domain_id`, or `event_types`. enum: - sending_transactional - sending_bulk @@ -1545,7 +1545,7 @@ paths: example: - delivery - bounce - mailsend_domain_id: + domain_id: type: integer description: | ID of the sending domain to scope this webhook to. If omitted, the webhook applies to all domains. Must not be set for `activity_log` type. @@ -1565,7 +1565,7 @@ paths: webhook_type: sending_transactional payload_format: json sending_mode: transactional - mailsend_domain_id: 435 + domain_id: 435 event_types: - delivery - bounce @@ -1612,7 +1612,7 @@ paths: webhook_type: sending_transactional payload_format: json sending_mode: transactional - mailsend_domain_id: 435 + domain_id: 435 event_types: - delivery - bounce @@ -1719,7 +1719,7 @@ paths: webhook_type: sending_transactional payload_format: json sending_mode: transactional - mailsend_domain_id: 435 + domain_id: 435 event_types: - delivery - bounce @@ -1767,7 +1767,7 @@ paths: webhook_type: activity_log payload_format: json sending_mode: null - mailsend_domain_id: null + domain_id: null event_types: [] domain_name: null '401': @@ -2137,7 +2137,7 @@ components: description: | The sending mode. Set to `transactional` for `sending_transactional` webhooks, `campaigns` for `sending_bulk` webhooks. Null for `activity_log` webhooks. example: transactional - mailsend_domain_id: + domain_id: type: - integer - 'null' From 7206979b57586e4dd2f567636417ec1b0c7f9da5 Mon Sep 17 00:00:00 2001 From: Ivan Yurchanka Date: Fri, 10 Apr 2026 15:15:04 +0200 Subject: [PATCH 5/5] Update list webhooks example to show transactional and activity log types Co-Authored-By: Claude Opus 4.6 (1M context) --- specs/email-sending.openapi.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index b966655..897ea90 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -1425,18 +1425,14 @@ paths: - bounce domain_name: mailtrap.io - id: 2 - url: 'https://example.com/webhooks/bulk' + url: 'https://example.com/webhooks/audit' active: true - webhook_type: sending_bulk - payload_format: jsonlines - sending_mode: campaigns - domain_id: 436 - event_types: - - delivery - - bounce - - open - - click - domain_name: notifications.mailtrap.io + webhook_type: activity_log + payload_format: json + sending_mode: null + domain_id: null + event_types: [] + domain_name: null '401': $ref: '#/components/responses/Unauthorized' '403':