{
  "openapi": "3.0.1",
  "info": {
    "title": "mailcatchr API",
    "description": "The mailcatchr public API is what test suites and CI pipelines call. It lists the mailboxes an API key can see, reads the messages they received, waits for a message to arrive, sends outbound mail, fetches TOTP codes for two-factor logins, and manages webhook interceptors.\n\n## Authentication\n\nEvery request carries an API key: `Authorization: Bearer mc_live_...`. Keys are issued per workspace from the dashboard and shown once at creation. A key holds two scopes fixed at issue time: the mailboxes it may read (all, or an explicit set) and the TOTP authenticators it may read.\n\nA resource outside the key's scope answers **404**, never 403, so a key cannot learn that a sibling resource exists.\n\n## Conventions\n\n* Base URL: `https://api.mailcatchr.com`. Paths are versioned under `/v1/`; v1 only ever changes additively.\n* IDs are ULIDs. Timestamps are ISO 8601 with offset.\n* Errors return JSON `{ \"error\": \"<code>\" }` plus any fields named in the endpoint description.\n* Rate limit: 100 requests per minute per key and 1,000 per minute per workspace. Over the limit answers 429 with `Retry-After`.\n* Idempotency: POST endpoints accept an `Idempotency-Key` header. A replay within 24 hours returns the original response without running again.",
    "contact": {
      "name": "mailcatchr support",
      "url": "https://mailcatchr.com/",
      "email": "support@mailcatchr.com"
    },
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.mailcatchr.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/v1/mailboxes": {
      "get": {
        "tags": [
          "Mailboxes"
        ],
        "summary": "List mailboxes",
        "description": "Returns every mailbox the key can read, across the workspace. A key scoped to an explicit set of mailboxes sees only those; a key scoped to all mailboxes also sees ones created after it was issued.\n\n`dailyInboundUsed` is today's count against the mailbox's inbound cap and resets at midnight UTC.",
        "operationId": "listMailboxes",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MailboxResponse"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/workspaces/{workspaceId}/mailboxes": {
      "post": {
        "tags": [
          "Mailboxes"
        ],
        "summary": "Create a mailbox",
        "description": "Creates a catchall mailbox. Its subdomain is live as soon as the call returns: anything sent to `*@<subdomain>.mailcatchr.com` is delivered to it.\n\n`subdomain` is optional; when omitted one is derived from `name`. `retentionDays` defaults to the workspace's plan default and may not exceed the plan maximum.\n\nErrors:\n* `400 invalid_name`, `invalid_subdomain`, `invalid_group_id`, `group_not_found`\n* `400 invalid_retention` with `max`, the plan's ceiling in days\n* `409 subdomain_taken`, `subdomain_reserved`\n* `409 plan_max_mailboxes_reached` with `limit`\n* `404` when the workspace is not the key's workspace",
        "operationId": "createMailbox",
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "description": "Workspace the mailbox belongs to. Must match the key's workspace.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMailboxRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailboxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not Found"
          },
          "409": {
            "description": "Conflict"
          }
        }
      }
    },
    "/v1/mailboxes/{mailboxId}/messages": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "List messages",
        "description": "Returns the newest messages in the mailbox first, as summaries without bodies. Page backwards by passing the last message's `id` as `before`.\n\nAnswers `404` when the mailbox does not exist or is outside the key's scope.",
        "operationId": "listMessages",
        "parameters": [
          {
            "name": "mailboxId",
            "in": "path",
            "description": "Mailbox to read.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 200. Default 50.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "before",
            "in": "query",
            "description": "Cursor: the `id` of the last message on the previous page. Returns messages older than it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MessageSummaryResponse"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/mailboxes/{mailboxId}/messages/await": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Wait for a message",
        "description": "Long-polls until a message matching every supplied filter arrives, then returns its summary. This is the call a test makes right after triggering the email it wants to assert on.\n\nThe connection stays open for up to `timeout` seconds. If nothing matches in time the response is `204` with no body, so a client can tell \"not yet\" (`204`) from \"no such mailbox\" (`404`). Fetch the body with *Get a message* once you have the id.\n\nFilters are optional, case-insensitive substring matches, and combine with AND.",
        "operationId": "awaitMessage",
        "parameters": [
          {
            "name": "mailboxId",
            "in": "path",
            "description": "Mailbox to watch.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "subject",
            "in": "query",
            "description": "Case-insensitive substring the subject must contain.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Case-insensitive substring the sender address must contain.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Case-insensitive substring the recipient address must contain.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "Only messages received at or after this time are eligible. Defaults to five seconds before the request, so a message that arrived just before the call still matches.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "timeout",
            "in": "query",
            "description": "Seconds to wait, 1 to 60. Default 30.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageSummaryResponse"
                }
              }
            }
          },
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/mailboxes/{mailboxId}/messages/{id}": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Get a message",
        "description": "Returns the full message: text and HTML bodies, the parsed headers as JSON, envelope details, the SPF result, and attachment metadata.\n\nAnswers `404` when the message or mailbox does not exist or is outside the key's scope.",
        "operationId": "getMessage",
        "parameters": [
          {
            "name": "mailboxId",
            "in": "path",
            "description": "Mailbox the message belongs to.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Message id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageDetailResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/mailboxes/{mailboxId}/send": {
      "post": {
        "tags": [
          "Outbound"
        ],
        "summary": "Send a message",
        "description": "Queues an outbound email from the mailbox and answers `202` with the send id and the id the message will have in the mailbox. Delivery happens asynchronously.\n\n`from` must use a sending identity verified in the workspace. Recipients on the workspace suppression list are refused. Each send is billed from the workspace wallet at $0.50 per 1,000 emails.\n\nErrors:\n* `400 invalid_body` when neither `textBody` nor `htmlBody` is present or an address is malformed\n* `403 outbound_disabled` when outbound is switched off for the mailbox or the plan\n* `422 invalid_from_domain` with `domain`\n* `422 suppressed_recipients` with `recipients`\n* `429 daily_outbound_limit_reached` with `count` and `limit`\n* `402 insufficient_funds` with `balance_cents` and `required_cents`\n* `402 storage_limit_reached` with `limit_bytes`\n* `404` when the mailbox does not exist or is outside the key's scope",
        "operationId": "sendMessage",
        "parameters": [
          {
            "name": "mailboxId",
            "in": "path",
            "description": "Mailbox to send from. Outbound must be enabled on it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "402": {
            "description": "Payment Required"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "422": {
            "description": "Unprocessable Content"
          },
          "429": {
            "description": "Too Many Requests"
          }
        }
      }
    },
    "/v1/workspaces/{workspaceId}/suppression": {
      "get": {
        "tags": [
          "Outbound"
        ],
        "summary": "List suppressed recipients",
        "description": "Returns the workspace suppression list: addresses that bounced or complained, which outbound sends will refuse. `reason` is `bounce` or `complaint`.",
        "operationId": "listSuppression",
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "description": "Workspace whose suppression list to read.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SuppressionEntryResponse"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/workspaces/{workspaceId}/suppression/{id}": {
      "delete": {
        "tags": [
          "Outbound"
        ],
        "summary": "Remove a suppressed recipient",
        "description": "Deletes one entry from the suppression list so the address can receive outbound mail again. Answers `204` on success.",
        "operationId": "removeSuppression",
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "description": "Workspace the entry belongs to.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Suppression entry id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/totp/{totpId}/code": {
      "get": {
        "tags": [
          "TOTP"
        ],
        "summary": "Get the current TOTP code",
        "description": "Returns the code an authenticator app would show right now for a stored authenticator, with when it expires. Use it to complete a two-factor login in an automated test.\n\nThe key must include the authenticator in its TOTP scope. Authenticators are registered in the dashboard; this API only reads codes.\n\nAnswers `404` when the authenticator does not exist or is outside the key's scope.",
        "operationId": "getTotpCode",
        "parameters": [
          {
            "name": "totpId",
            "in": "path",
            "description": "Authenticator id, as shown in the dashboard under Tools.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpCodeResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "Returns every webhook interceptor in the key's workspace. Each one has a capture URL of the form `https://webhooks.mailcatchr.com/<key>`.",
        "operationId": "listWebhooks",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookView"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook",
        "description": "Creates an interceptor with a fresh capture key. Every field is optional: the response defaults to `200` with an empty body, forwarding is off, and retention is the plan default.\n\nSet `forwardMode` and `forwardUrl` to relay each captured request to your own system.\n\nErrors:\n* `400 invalid_<field>` naming the field that failed validation\n* `409 hook_limit_reached` with `limit`",
        "operationId": "createWebhook",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertWebhookDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookView"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "409": {
            "description": "Conflict"
          }
        }
      }
    },
    "/v1/webhooks/{webhookId}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get a webhook",
        "description": "Returns one interceptor including its capture key, configured response, forwarding settings and request count.",
        "operationId": "getWebhook",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "Webhook id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookView"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a webhook",
        "description": "Changes any of the interceptor's settings. Omitted fields are left as they are. Errors as for *Create a webhook*.",
        "operationId": "updateWebhook",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "Webhook id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertWebhookDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookView"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not Found"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook",
        "description": "Deletes the interceptor and every request it captured. Its capture URL stops answering immediately.",
        "operationId": "deleteWebhook",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "Webhook id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/webhooks/{webhookId}/requests": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List captured requests",
        "description": "Returns a page of captured requests, newest first, as summaries without headers or body. `total` is the count across all pages.",
        "operationId": "listWebhookRequests",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "Webhook whose captured requests to list.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number. Default 1.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Requests per page. Default 50.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookRequestPage"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Clear captured requests",
        "description": "Deletes every request captured by the interceptor. The interceptor itself is kept.",
        "operationId": "clearWebhookRequests",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "Webhook whose captured requests to delete.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/webhooks/requests/{requestId}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get a captured request",
        "description": "Returns the full captured request: method, path, query, headers, body (as text when it is text), source address, the response that was returned, and the forwarding outcome if forwarding is on.",
        "operationId": "getWebhookRequest",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "description": "Captured request id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookRequestView"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/webhooks/requests/{requestId}/replay": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Replay a captured request",
        "description": "Forwards the captured request to the interceptor's forward URL again. Requires forwarding to be configured; answers `400 invalid_forward` otherwise.",
        "operationId": "replayWebhookRequest",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "description": "Captured request id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AttachmentResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "filename": {
            "type": "string",
            "nullable": true
          },
          "contentType": {
            "type": "string",
            "nullable": true
          },
          "contentId": {
            "type": "string",
            "nullable": true
          },
          "isInline": {
            "type": "boolean"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          },
          "virusScanStatus": {
            "$ref": "#/components/schemas/VirusScanStatus"
          }
        },
        "additionalProperties": false
      },
      "CreateMailboxRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "subdomain": {
            "type": "string",
            "nullable": true
          },
          "retentionDays": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "groupId": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "MailboxResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "workspaceId": {
            "type": "string",
            "nullable": true
          },
          "groupId": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "subdomain": {
            "type": "string",
            "nullable": true
          },
          "retentionDays": {
            "type": "integer",
            "format": "int32"
          },
          "outboundEnabled": {
            "type": "boolean"
          },
          "dailyInboundUsed": {
            "type": "integer",
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "MessageDetailResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "from": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "string",
            "nullable": true
          },
          "cc": {
            "type": "string",
            "nullable": true
          },
          "bcc": {
            "type": "string",
            "nullable": true
          },
          "replyTo": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "textBody": {
            "type": "string",
            "nullable": true
          },
          "htmlBody": {
            "type": "string",
            "nullable": true
          },
          "headersJson": {
            "type": "string",
            "nullable": true
          },
          "spfResult": {
            "type": "string",
            "nullable": true
          },
          "envelopeFrom": {
            "type": "string",
            "nullable": true
          },
          "remoteIp": {
            "type": "string",
            "nullable": true
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttachmentResponse"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "MessageSummaryResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "from": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          },
          "attachmentCount": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "SendRequest": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "string",
            "nullable": true
          },
          "cc": {
            "type": "string",
            "nullable": true
          },
          "bcc": {
            "type": "string",
            "nullable": true
          },
          "replyTo": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "textBody": {
            "type": "string",
            "nullable": true
          },
          "htmlBody": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "SendResponse": {
        "type": "object",
        "properties": {
          "outboundSendId": {
            "type": "string",
            "nullable": true
          },
          "messageId": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "SuppressionEntryResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "recipient": {
            "type": "string",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "addedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "TotpCodeResponse": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresInSeconds": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "Ulid": {
        "type": "object",
        "properties": {
          "random": {
            "type": "string",
            "format": "byte",
            "nullable": true,
            "readOnly": true
          },
          "time": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "additionalProperties": false
      },
      "UpsertWebhookDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "responseStatus": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "responseContentType": {
            "type": "string",
            "nullable": true
          },
          "responseBody": {
            "type": "string",
            "nullable": true
          },
          "forwardMode": {
            "type": "string",
            "nullable": true
          },
          "forwardUrl": {
            "type": "string",
            "nullable": true
          },
          "retentionDays": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "VirusScanStatus": {
        "enum": [
          0,
          1,
          2,
          3,
          4
        ],
        "type": "integer",
        "format": "int32"
      },
      "WebhookForwardMode": {
        "enum": [
          0,
          1,
          2
        ],
        "type": "integer",
        "format": "int32"
      },
      "WebhookForwardStatus": {
        "enum": [
          0,
          1,
          2,
          3
        ],
        "type": "integer",
        "format": "int32"
      },
      "WebhookRequestPage": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookRequestSummary"
            },
            "nullable": true
          },
          "total": {
            "type": "integer",
            "format": "int64"
          },
          "page": {
            "type": "integer",
            "format": "int32"
          },
          "pageSize": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "WebhookRequestSummary": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Ulid"
          },
          "method": {
            "type": "string",
            "nullable": true
          },
          "path": {
            "type": "string",
            "nullable": true
          },
          "contentType": {
            "type": "string",
            "nullable": true
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int32"
          },
          "sourceIp": {
            "type": "string",
            "nullable": true
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "responseStatus": {
            "type": "integer",
            "format": "int32"
          },
          "forwardStatus": {
            "$ref": "#/components/schemas/WebhookForwardStatus"
          },
          "forwardHttpStatus": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "WebhookRequestView": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Ulid"
          },
          "endpointId": {
            "$ref": "#/components/schemas/Ulid"
          },
          "method": {
            "type": "string",
            "nullable": true
          },
          "path": {
            "type": "string",
            "nullable": true
          },
          "query": {
            "type": "string",
            "nullable": true
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "nullable": true
          },
          "contentType": {
            "type": "string",
            "nullable": true
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int32"
          },
          "sourceIp": {
            "type": "string",
            "nullable": true
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "responseStatus": {
            "type": "integer",
            "format": "int32"
          },
          "forwardStatus": {
            "$ref": "#/components/schemas/WebhookForwardStatus"
          },
          "forwardAttempts": {
            "type": "integer",
            "format": "int32"
          },
          "forwardHttpStatus": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "forwardedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "forwardError": {
            "type": "string",
            "nullable": true
          },
          "forwardResponse": {
            "type": "string",
            "nullable": true
          },
          "bodyIsText": {
            "type": "boolean"
          },
          "bodyText": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "WebhookView": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Ulid"
          },
          "workspaceId": {
            "$ref": "#/components/schemas/Ulid"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "key": {
            "type": "string",
            "nullable": true
          },
          "responseStatus": {
            "type": "integer",
            "format": "int32"
          },
          "responseContentType": {
            "type": "string",
            "nullable": true
          },
          "responseBody": {
            "type": "string",
            "nullable": true
          },
          "forwardMode": {
            "$ref": "#/components/schemas/WebhookForwardMode"
          },
          "forwardUrl": {
            "type": "string",
            "nullable": true
          },
          "retentionDays": {
            "type": "integer",
            "format": "int32"
          },
          "maxRetentionDays": {
            "type": "integer",
            "format": "int32"
          },
          "requestCount": {
            "type": "integer",
            "format": "int64"
          },
          "lastRequestAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "description": "API key as `Authorization: Bearer mc_live_...`",
        "scheme": "bearer",
        "bearerFormat": "mc_live_<token>"
      }
    }
  },
  "security": [
    {
      "bearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Mailboxes",
      "description": "Catchall inboxes. Each mailbox owns a subdomain; any address on it is delivered."
    },
    {
      "name": "Messages",
      "description": "Mail received by a mailbox. `await` is the method most tests use: it blocks until a matching message lands."
    },
    {
      "name": "Outbound",
      "description": "Send mail from a mailbox and manage the workspace suppression list. Outbound is pay-as-you-go from the workspace wallet."
    },
    {
      "name": "TOTP",
      "description": "Current codes from authenticators stored in the workspace, for automating two-factor logins."
    },
    {
      "name": "Webhooks",
      "description": "Webhook interceptors: URLs that record every request they receive and optionally forward it on."
    }
  ]
}