{
  "openapi": "3.1.0",
  "info": {
    "title": "Email aliases: inbox API for AI agents",
    "version": "1.0.0",
    "summary": "Make email inboxes, wait for mail and read one-time codes and links.",
    "description": "Receive-only email inboxes for agents. An inbox is an address at alias.utilarium.app. Mail to it is kept for 7 days, and its one-time codes and links are extracted. Make a token on https://alias.utilarium.app/ (API tokens) and send it as Authorization: Bearer <token>. Guide: https://alias.utilarium.app/agents/"
  },
  "servers": [{ "url": "https://alias.utilarium.app" }],
  "x-limits": {
    "tokens_per_account": 3,
    "inboxes_per_token": 10,
    "mail_per_day_per_token": 100,
    "keep_days": 7,
    "requests_per_minute_per_token": 120,
    "max_mail_bytes": 5242880,
    "rules_per_inbox": 5,
    "max_page": 50,
    "default_page": 20
  },
  "security": [{ "bearer": [] }],
  "paths": {
    "/v1/inboxes": {
      "post": {
        "operationId": "createInbox",
        "summary": "Make an inbox",
        "description": "Makes an inbox with a random address. At most 10 inboxes for each token.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/InboxInput" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new inbox.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Inbox" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      },
      "get": {
        "operationId": "listInboxes",
        "summary": "List the inboxes of the token",
        "responses": {
          "200": {
            "description": "The inboxes, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["inboxes", "max"],
                  "properties": {
                    "inboxes": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Inbox" }
                    },
                    "max": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/inboxes/{id}": {
      "parameters": [{ "$ref": "#/components/parameters/Id" }],
      "get": {
        "operationId": "getInbox",
        "summary": "Read one inbox",
        "responses": {
          "200": {
            "description": "The inbox.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Inbox" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      },
      "patch": {
        "operationId": "updateInbox",
        "summary": "Change the label, the webhook or the rules of an inbox",
        "description": "Send only the fields to change. A null webhook_url removes the webhook. A list of rules replaces all the rules.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/InboxInput" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The changed inbox.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Inbox" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      },
      "delete": {
        "operationId": "deleteInbox",
        "summary": "Delete an inbox and its mail",
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/inboxes/{id}/messages": {
      "parameters": [{ "$ref": "#/components/parameters/Id" }],
      "get": {
        "operationId": "listMessages",
        "summary": "List the mail of an inbox, newest first, without bodies",
        "description": "Poll with since to wait for new mail: send the received_at of the newest mail you have.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many messages to return, 1 to 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "before",
            "in": "query",
            "description": "The next value of an earlier answer: the page after it.",
            "schema": { "type": "string" }
          },
          {
            "name": "since",
            "in": "query",
            "description": "Only mail received after this time.",
            "schema": { "type": "string", "format": "date-time" }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["messages", "next"],
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MessageSummary"
                      }
                    },
                    "next": {
                      "type": ["string", "null"],
                      "description": "Pass it as before to get the next page. Null at the end."
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/messages/{id}": {
      "parameters": [{ "$ref": "#/components/parameters/Id" }],
      "get": {
        "operationId": "getMessage",
        "summary": "Read one mail with its text and HTML",
        "responses": {
          "200": {
            "description": "The mail.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Message" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      },
      "delete": {
        "operationId": "deleteMessage",
        "summary": "Delete one mail",
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "A personal API token: alias_ and 40 letters or digits. Make it on https://alias.utilarium.app/ when you are signed in."
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      }
    },
    "responses": {
      "Ok": {
        "description": "Done.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["ok"],
              "properties": { "ok": { "const": true } }
            }
          }
        }
      },
      "Error": {
        "description": "An error. The code is stable; the message is for people.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "examples": ["not_found"] },
              "message": { "type": "string" }
            }
          }
        }
      },
      "Rule": {
        "type": "object",
        "required": ["name", "pattern"],
        "properties": {
          "name": { "type": "string", "minLength": 1, "maxLength": 40 },
          "pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The source of a JavaScript regular expression. The first capture group, or else the whole match, is the value."
          },
          "target": {
            "type": "string",
            "enum": ["subject", "text", "html"],
            "default": "text"
          }
        }
      },
      "InboxInput": {
        "type": "object",
        "properties": {
          "label": { "type": "string", "maxLength": 60 },
          "webhook_url": {
            "type": ["string", "null"],
            "description": "A Hooks address: https://hooks.utilarium.app/in/ and 16 letters or digits, optionally followed by a path."
          },
          "rules": {
            "type": "array",
            "maxItems": 5,
            "items": { "$ref": "#/components/schemas/Rule" }
          }
        }
      },
      "Inbox": {
        "type": "object",
        "required": [
          "id",
          "address",
          "label",
          "webhook_url",
          "rules",
          "created_at"
        ],
        "properties": {
          "id": { "type": "string" },
          "address": { "type": "string", "format": "email" },
          "label": { "type": "string" },
          "webhook_url": { "type": ["string", "null"] },
          "rules": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Rule" }
          },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Code": {
        "type": "object",
        "required": ["value", "rule"],
        "properties": {
          "value": { "type": "string" },
          "rule": {
            "type": "string",
            "description": "default for the built-in rules, else the name of your rule."
          }
        }
      },
      "Link": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string" },
          "kind": {
            "type": "string",
            "enum": [
              "unsubscribe",
              "reset",
              "magic",
              "verify",
              "confirm",
              "activate",
              "login"
            ]
          }
        }
      },
      "Attachment": {
        "type": "object",
        "required": ["filename", "content_type", "size"],
        "properties": {
          "filename": { "type": "string" },
          "content_type": { "type": "string" },
          "size": { "type": "integer" }
        }
      },
      "MessageSummary": {
        "type": "object",
        "required": [
          "id",
          "from",
          "from_name",
          "to",
          "subject",
          "received_at",
          "codes",
          "links",
          "matches",
          "size",
          "has_html",
          "attachments"
        ],
        "properties": {
          "id": { "type": "string" },
          "from": { "type": "string" },
          "from_name": { "type": "string" },
          "to": { "type": "string" },
          "subject": { "type": "string" },
          "received_at": { "type": "string", "format": "date-time" },
          "codes": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Code" }
          },
          "links": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Link" }
          },
          "matches": {
            "type": "array",
            "description": "One value for each own rule that matched.",
            "items": { "$ref": "#/components/schemas/Code" }
          },
          "size": {
            "type": "integer",
            "description": "Bytes of the whole mail."
          },
          "has_html": { "type": "boolean" },
          "attachments": {
            "type": "array",
            "description": "Names and sizes only. The files are not kept.",
            "items": { "$ref": "#/components/schemas/Attachment" }
          }
        }
      },
      "Message": {
        "allOf": [
          { "$ref": "#/components/schemas/MessageSummary" },
          {
            "type": "object",
            "required": ["inbox_id", "text", "html", "truncated"],
            "properties": {
              "inbox_id": { "type": "string" },
              "text": { "type": "string" },
              "html": { "type": ["string", "null"] },
              "truncated": {
                "type": "boolean",
                "description": "True when the text or the HTML was cut to fit."
              }
            }
          }
        ]
      }
    }
  }
}
