{
  "openapi": "3.1.0",
  "info": {
    "title": "Mailfornet API",
    "version": "1.0.0",
    "summary": "Disposable inboxes for automated testing",
    "description": "Create a disposable inbox from your code and read the mail that arrives in it. Built for the test that signs up, waits for a verification code and types it back in.",
    "termsOfService": "https://mailfornet.com/terms",
    "contact": { "name": "Mailfornet support", "url": "https://mailfornet.com/contact" }
  },
  "servers": [{ "url": "https://api.mailfornet.com/v1" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Inboxes" },
    { "name": "Messages" },
    { "name": "Webhooks" },
    { "name": "Account" }
  ],
  "paths": {
    "/inboxes": {
      "post": {
        "tags": ["Inboxes"],
        "summary": "Create an inbox",
        "operationId": "createInbox",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": { "type": "string", "maxLength": 100 },
            "description": "Repeat a request safely: the same key returns the original response."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "username": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9._-]{1,29}$",
                    "description": "Leave out for a random one."
                  },
                  "domain": { "type": "string", "description": "One of the domains from /domains." },
                  "ttlMinutes": { "type": "integer", "minimum": 1, "maximum": 1440, "default": 60 }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Inbox" } } }
          },
          "409": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/inboxes/{address}": {
      "delete": {
        "tags": ["Inboxes"],
        "summary": "Delete an inbox and its mail",
        "operationId": "deleteInbox",
        "parameters": [{ "$ref": "#/components/parameters/Address" }],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "deleted": { "type": "boolean" }, "address": { "type": "string" } }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/inboxes/{address}/messages": {
      "get": {
        "tags": ["Messages"],
        "summary": "List messages",
        "description": "With `wait`, the request holds open until a message arrives, so no polling loop is needed.",
        "operationId": "listMessages",
        "parameters": [
          { "$ref": "#/components/parameters/Address" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } },
          {
            "name": "wait",
            "in": "query",
            "schema": { "type": "integer", "minimum": 0, "maximum": 60, "default": 0 },
            "description": "Seconds to hold the connection open waiting for mail."
          },
          {
            "name": "since",
            "in": "query",
            "schema": { "type": "integer" },
            "description": "Only mail received after this unix timestamp in milliseconds."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "address": { "type": "string" },
                    "messages": { "type": "array", "items": { "$ref": "#/components/schemas/MessageSummary" } },
                    "waited": { "type": "boolean" }
                  }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/Error" },
          "410": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/messages/{id}": {
      "get": {
        "tags": ["Messages"],
        "summary": "Read one message",
        "operationId": "getMessage",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          {
            "name": "address",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "format": "email" },
            "description": "The inbox the message belongs to."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Message" } } }
          },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List webhooks",
        "operationId": "listWebhooks",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": { "type": "array", "items": { "$ref": "#/components/schemas/Webhook" } }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Register a webhook",
        "description": "The signing secret is returned once, in this response, and cannot be read again.",
        "operationId": "createWebhook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": { "url": { "type": "string", "format": "uri", "description": "Must be https." } }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Webhook" },
                    { "type": "object", "properties": { "secret": { "type": "string" } } }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "tags": ["Webhooks"],
        "summary": "Remove a webhook",
        "operationId": "deleteWebhook",
        "parameters": [{ "$ref": "#/components/parameters/WebhookId" }],
        "responses": {
          "200": { "description": "Deleted" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "Recent delivery attempts",
        "operationId": "webhookDeliveries",
        "parameters": [{ "$ref": "#/components/parameters/WebhookId" }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "event": { "type": "string" },
                          "status": { "type": "integer", "description": "0 when the request never completed." },
                          "error": { "type": "string" },
                          "at": { "type": "string", "format": "date-time" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "tags": ["Account"],
        "summary": "This month's usage",
        "operationId": "getUsage",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "month": { "type": "string", "examples": ["2026-09"] },
                    "used": { "type": "integer" },
                    "quota": { "type": "integer" },
                    "remaining": { "type": "integer" },
                    "resetsAt": { "type": "integer", "description": "Unix seconds." }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/domains": {
      "get": {
        "tags": ["Account"],
        "summary": "Domains you can use",
        "operationId": "listDomains",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "domains": { "type": "array", "items": { "type": "string" } } }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key, created at https://mailfornet.com/account"
      }
    },
    "parameters": {
      "Address": {
        "name": "address",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "email" }
      },
      "WebhookId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "integer" }
      }
    },
    "responses": {
      "Error": {
        "description": "Something was wrong with the request",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Inbox": {
        "type": "object",
        "properties": {
          "address": { "type": "string", "format": "email" },
          "createdAt": { "type": "string", "format": "date-time" },
          "expiresAt": { "type": "string", "format": "date-time" }
        }
      },
      "MessageSummary": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "from": { "type": "string" },
          "subject": { "type": "string" },
          "intro": { "type": "string", "description": "The first line, for matching without a second call." },
          "receivedAt": { "type": "string", "format": "date-time" }
        }
      },
      "Message": {
        "allOf": [
          { "$ref": "#/components/schemas/MessageSummary" },
          {
            "type": "object",
            "properties": {
              "text": { "type": "string" },
              "html": { "type": "string" },
              "attachments": { "type": "array", "items": { "type": "object" } }
            }
          }
        ]
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "url": { "type": "string", "format": "uri" },
          "createdAt": { "type": "string", "format": "date-time" },
          "disabled": { "type": "boolean", "description": "Switched off after 20 consecutive failures." },
          "failCount": { "type": "integer" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": ["invalid_request_error", "authentication_error", "rate_limit_error", "api_error"]
              },
              "code": {
                "type": "string",
                "description": "Stable machine-readable code. Branch on this, not on the message.",
                "examples": ["address_taken", "quota_exceeded", "invalid_api_key"]
              },
              "message": { "type": "string" },
              "docUrl": { "type": "string", "format": "uri" }
            }
          },
          "requestId": { "type": "string", "description": "Also sent as the Mailfornet-Request-Id header." }
        }
      }
    }
  }
}
