diff --git a/CLAUDE.md b/CLAUDE.md index ae6ed84..da7e5d2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,7 +40,6 @@ mailtrap-openapi/ | `contacts.openapi.yml` | Contact management API | `https://mailtrap.io` | | `sandbox.openapi.yml` | Email Sandbox/testing API | `https://mailtrap.io` | | `general.openapi.yml` | Account, users, permissions, billing | `https://mailtrap.io` | -| `webhooks.openapi.yml` | Webhook event handlers | Your endpoint (incoming) | | `email-api.openapi.yml` | Combined email API reference | Varies by operation | ### Base URL Rules @@ -86,7 +85,6 @@ A mapping file `helpscout_to_gitbook_redirects.csv` is available locally (not co | Sending Domain Setup | `https://docs.mailtrap.io/email-api-smtp/setup/sending-domain` | | Bulk Stream | `https://docs.mailtrap.io/email-api-smtp/setup/bulk-stream` | | Email Templates | `https://docs.mailtrap.io/email-marketing/campaigns/email-templates` | -| Webhooks | `https://docs.mailtrap.io/email-api-smtp/advanced/webhooks` | | Email Logs | `https://docs.mailtrap.io/email-api-smtp/analytics/logs` | | Email Sandbox | `https://docs.mailtrap.io/getting-started/email-sandbox` | | API Tokens | `https://docs.mailtrap.io/email-sandbox/setup/api-tokens` | diff --git a/README.md b/README.md index 3f46f61..ad6b841 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ The specifications are located in the `specs/` directory: | Contacts | `specs/contacts.openapi.yml` | `https://mailtrap.io` | | Sandbox | `specs/sandbox.openapi.yml` | `https://mailtrap.io` | | General | `specs/general.openapi.yml` | `https://mailtrap.io` | -| Webhooks | `specs/webhooks.openapi.yml` | Your endpoint | | Email API | `specs/email-api.openapi.yml` | Varies by operation | ## Usage diff --git a/SUMMARY.md b/SUMMARY.md index 05c30cf..5f125ab 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -106,17 +106,3 @@ kind: openapi spec: general ``` - -## Webhooks - -* ```yaml - props: - models: false - downloadLink: false - type: builtin:openapi - dependencies: - spec: - ref: - kind: openapi - spec: webhooks - ``` diff --git a/specs/webhooks.openapi.yml b/specs/webhooks.openapi.yml deleted file mode 100644 index f4cb5cc..0000000 --- a/specs/webhooks.openapi.yml +++ /dev/null @@ -1,725 +0,0 @@ -openapi: 3.1.0 -info: - title: Webhooks - version: 2.0.0 - description: | - Receive real-time notifications about email events and account activities. - - Webhooks allow you to receive all information about your deliverability and activities within your account almost in real-time. Instead of polling for updates, Mailtrap sends HTTP POST requests to your endpoint when events occur. - - {% hint style="info" %} - For setup instructions and configuration details, see the [Webhooks Guide](https://docs.mailtrap.io/email-api-smtp/advanced/webhooks). - {% endhint %} - - ## Event Types - - **Sending Events:** - - **delivery** - Email successfully delivered - - **bounce** - Permanent delivery failure - - **soft bounce** - Temporary failure (will retry) - - **spam** - Recipient reported spam - - **unsubscribe** - Recipient unsubscribed - - **open** - Email was opened - - **click** - Link was clicked - - **suspension** - Message suspended - - **reject** - Message rejected - - **Activity Log Events (Enterprise):** - - User login events - - Profile updates - - Permission changes - - And more - - ## Payload Formats - - Choose between two formats when configuring your webhook: - - {% tabs %} - {% tab title="JSON" %} - Events sent as a JSON object with an `events` array: - ```json - { - "events": [ - {"event": "delivery", "email": "user@example.com", ...} - ] - } - ``` - {% endtab %} - - {% tab title="JSON Lines" %} - Events sent as newline-delimited JSON: - ``` - {"event":"delivery","email":"user@example.com",...} - {"event":"open","email":"user@example.com",...} - ``` - {% endtab %} - {% endtabs %} - - ## Processing Webhooks - - - Respond with HTTP **200** to acknowledge receipt - - Process events asynchronously to avoid timeouts - - Implement idempotency using `event_id` - - Handle retries (40 retries every 5 minutes if non-200) - contact: - name: Mailtrap Support - url: 'https://docs.mailtrap.io' - email: support@mailtrap.io - license: - name: Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) - url: 'https://creativecommons.org/licenses/by-sa/4.0/' - x-logo: - url: 'https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-logo.svg' - -tags: - - name: json-handler - x-page-title: Receive events (JSON format) - x-page-icon: brackets-curly - x-page-description: Handle webhook events in JSON format - description: | - Receive webhook events as a JSON object containing an array of events. - - Set **Payload format: JSON** in your webhook settings. - - - name: jsonlines-handler - x-page-title: Receive events (JSON Lines format) - x-page-icon: list - x-page-description: Handle webhook events in JSON Lines format - description: | - Receive webhook events in [JSON Lines](https://jsonlines.org/) format - one JSON object per line. - - Set **Payload format: JSON Lines** in your webhook settings. - -webhooks: - receiveEventsJSON: - post: - summary: Receive events (JSON) - description: | - Mailtrap sends a `POST` request to your webhook URL with events as a JSON object. - - **Your endpoint should:** - - Accept `Content-Type: application/json` - - Return HTTP 200 to acknowledge receipt - - Process events asynchronously - - [Webhook setup guide](https://docs.mailtrap.io/email-api-smtp/advanced/webhooks) - operationId: receiveEventsJSON - tags: - - json-handler - x-codeSamples: - - lang: javascript - label: 'Node.js' - source: | - // Express.js webhook handler - app.post('/webhook', express.json(), (req, res) => { - const { events } = req.body; - - events.forEach(event => { - switch(event.event) { - case 'delivery': - console.log(`Delivered to ${event.email}`); - break; - case 'bounce': - console.log(`Bounced: ${event.response}`); - // Add to suppression list - break; - case 'open': - console.log(`Opened by ${event.email}`); - break; - case 'click': - console.log(`Clicked: ${event.url}`); - break; - } - }); - - res.status(200).send('OK'); - }); - - lang: python - label: 'Python' - source: | - from flask import Flask, request - - app = Flask(__name__) - - @app.route('/webhook', methods=['POST']) - def handle_webhook(): - data = request.get_json() - - for event in data.get('events', []): - event_type = event.get('event') - - if event_type == 'delivery': - print(f"Delivered to {event['email']}") - elif event_type == 'bounce': - print(f"Bounced: {event.get('response')}") - elif event_type == 'open': - print(f"Opened by {event['email']}") - elif event_type == 'click': - print(f"Clicked: {event.get('url')}") - - return 'OK', 200 - - lang: php - label: 'PHP' - source: | - Events - ); - [HttpPost] - public IActionResult Handle([FromBody] WebhookPayload payload) - { - foreach (var evt in payload.Events) - { - if (!evt.TryGetProperty("event", out var eventProp)) - continue; - - var type = eventProp.GetString(); - if (string.IsNullOrEmpty(type)) - continue; - - switch (type) - { - case "delivery": - if (evt.TryGetProperty("email", out var emailProp)) - Console.WriteLine($"Delivered to {emailProp.GetString()}"); - break; - case "bounce": - if (evt.TryGetProperty("response", out var respProp)) - Console.WriteLine($"Bounced: {respProp.GetString()}"); - break; - case "open": - if (evt.TryGetProperty("email", out var emailOpenProp)) - Console.WriteLine($"Opened: {emailOpenProp.GetString()}"); - break; - case "click": - if (evt.TryGetProperty("url", out var urlProp)) - Console.WriteLine($"Clicked: {urlProp.GetString()}"); - break; - } - } - return Ok("OK"); - } - } - - lang: java - label: 'Java' - source: | - // Spring Boot controller - @RestController - public class WebhookController { - @PostMapping("/webhook") - public String handle(@RequestBody Map payload) { - List> events = - (List>) payload.get("events"); - - for (Map event : events) { - String type = (String) event.get("event"); - switch (type) { - case "delivery": - System.out.println("Delivered to " + event.get("email")); - break; - case "bounce": - System.out.println("Bounced: " + event.get("response")); - break; - } - } - return "OK"; - } - } - requestBody: - description: Batch of events in JSON format - content: - application/json: - schema: - type: object - properties: - events: - type: array - items: - oneOf: - - $ref: '#/components/schemas/SendingWebhookEvent' - - $ref: '#/components/schemas/ActivityLogWebhookEvent' - examples: - 'Delivery': - description: Email delivered successfully - value: - events: - - event: delivery - timestamp: 1728669700 - sending_stream: transactional - category: Password reset - custom_variables: - user_id: "123" - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - sending_domain_name: examplesender.com - 'Bounce': - description: Permanent delivery failure - value: - events: - - event: bounce - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - response: '[CS01] Message rejected due to local policy' - response_code: 555 - bounce_category: spam - sending_domain_name: examplesender.com - 'Soft Bounce': - description: Temporary failure (will retry) - value: - events: - - event: soft bounce - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - response: '4.7.1 Temporary error, please retry' - response_code: 451 - bounce_category: greylisting - sending_domain_name: examplesender.com - 'Spam': - description: User reported as spam - value: - events: - - event: spam - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - sending_domain_name: examplesender.com - 'Suspension': - description: Message suspended (rate limit, verification) - value: - events: - - event: suspension - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - reason: Your account has reached its daily sending limit. - sending_domain_name: examplesender.com - 'Reject': - description: Message rejected (suppressed, billing) - value: - events: - - event: reject - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - reason: 'Recipient in suppression list. Reason: unsubscription' - sending_domain_name: examplesender.com - 'Open': - description: Email was opened - value: - events: - - event: open - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - ip: 127.138.158.185 - user_agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)' - sending_domain_name: examplesender.com - 'Click': - description: Link was clicked - value: - events: - - event: click - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - ip: 142.86.27.2 - user_agent: 'Mozilla/5.0 (Windows NT x.y; Win64; x64)' - url: 'https://mailtrap.io/email-api' - sending_domain_name: examplesender.com - 'Unsubscribe': - description: Recipient unsubscribed - value: - events: - - event: unsubscribe - timestamp: 1728669700 - sending_stream: transactional - message_id: 1df37d17-0286-4d8b-8edf-bc4ec5be86e6 - email: receiver@example.com - event_id: bede7236-2284-43d6-a953-1fdcafd0fdbc - sending_domain_name: examplesender.com - 'Activity Log: User Login': - value: - events: - - event: activity_log.user.login - description: logged in with SSO - actor: - id: 1 - type: user - name: John Doe - timestamp: 1735830138 - 'Activity Log: Profile Update': - value: - events: - - event: activity_log.user.updated - description: updated the user profile - actor: - id: 1 - type: user - name: John Doe - resource: - id: '1' - type: user - name: John Doe - changes: - name: - from: John - to: John Doe - timestamp: 1735830138 - responses: - '200': - description: Return 200 to acknowledge successful receipt - '500': - description: Any other status triggers retry (40 retries every 5 min) - - receiveEventsJSONLines: - post: - summary: Receive events (JSON Lines) - description: | - Mailtrap sends a `POST` request to your webhook URL with events in [JSON Lines](https://jsonlines.org/) format. - - Each line is a separate JSON object. Parse line by line. - - **Your endpoint should:** - - Accept `Content-Type: application/jsonl` - - Return HTTP 200 to acknowledge receipt - - Process events asynchronously - - [Webhook setup guide](https://docs.mailtrap.io/email-api-smtp/advanced/webhooks) - operationId: receiveEventsJSONLines - tags: - - jsonlines-handler - x-codeSamples: - - lang: javascript - label: 'Node.js' - source: | - // Express.js handler for JSON Lines - app.post('/webhook', express.text({ type: '*/*' }), (req, res) => { - const lines = req.body.split('\n').filter(line => line.trim()); - - lines.forEach(line => { - const event = JSON.parse(line); - - switch(event.event) { - case 'delivery': - console.log(`Delivered to ${event.email}`); - break; - case 'bounce': - console.log(`Bounced: ${event.response}`); - break; - } - }); - - res.status(200).send('OK'); - }); - - lang: python - label: 'Python' - source: | - from flask import Flask, request - import json - - app = Flask(__name__) - - @app.route('/webhook', methods=['POST']) - def handle_webhook(): - lines = request.data.decode('utf-8').strip().split('\n') - - for line in lines: - event = json.loads(line) - event_type = event.get('event') - - if event_type == 'delivery': - print(f"Delivered to {event['email']}") - elif event_type == 'bounce': - print(f"Bounced: {event.get('response')}") - - return 'OK', 200 - - lang: php - label: 'PHP' - source: | -