{
  "openapi": "3.1.0",
  "info": {
    "title": "Infrastructure Messages API",
    "version": "1.0.0",
    "description": "Transactional email on top of Amazon SES: projects, sender-domain verification by DNS, sending with idempotency, a send log with a per-message event timeline, attachments, templates, per-project suppression list, reputation quota and signed delivery webhooks.\n\nEvery credential goes in `Authorization: Bearer <token>`: a project secret key (`msk_…`), a revocable project API key (`rm_…`, permission `read`/`send`/`full`), or an Infrastructure Auth JWT that identifies a person. Successful responses return the resource at the root; collections are `{items, page:{limit, offset, total, hasMore}}`; errors are `{error:{code, message, details?}}`.\n\nAn MCP server is also available at `/mcp/{projectId}` (not described here).",
    "contact": {
      "name": "Infrastructure",
      "url": "https://myinfrastructure.click/contact"
    },
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://messages.worker.myinfrastructure.click"
    }
  ],
  "externalDocs": {
    "description": "Agent guide (llms.txt)",
    "url": "https://myinfrastructure.click/products/messages/llms.txt"
  },
  "tags": [
    {
      "name": "Emails",
      "description": "Send and read the send log."
    },
    {
      "name": "Attachments",
      "description": "Upload files referenced by sends."
    },
    {
      "name": "Domains",
      "description": "Sender domains and DNS verification."
    },
    {
      "name": "Templates",
      "description": "Stored subjects/bodies with `{{variables}}`."
    },
    {
      "name": "Suppressions",
      "description": "Per-project blocked addresses."
    },
    {
      "name": "Reputation",
      "description": "Quota and sending health."
    },
    {
      "name": "Metrics",
      "description": "Aggregated send counts and rates."
    },
    {
      "name": "Projects",
      "description": "Projects and their credentials."
    },
    {
      "name": "API keys",
      "description": "Revocable project keys (`rm_…`)."
    },
    {
      "name": "Webhooks",
      "description": "Endpoints that receive signed delivery events."
    },
    {
      "name": "Account",
      "description": "Account-level views (person token only)."
    },
    {
      "name": "Status",
      "description": "Public service health."
    }
  ],
  "paths": {
    "/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Service health",
        "description": "Public health check of the service and its dependencies (database, Amazon SES, R2 attachments). Never exposes customer volume. Sent with `Cache-Control: no-store`.",
        "tags": [
          "Status"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Current health.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceStatus"
                }
              }
            }
          }
        }
      }
    },
    "/plan": {
      "get": {
        "operationId": "getPlan",
        "summary": "Account plan notice",
        "description": "The account's plan state as shown in the dashboard billing banner. Requires a PERSON token (project keys get 403). Never fails because of billing: when the Payments service does not answer, `state` is `unknown`.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Plan notice.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanNotice"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/projects": {
      "get": {
        "operationId": "listProjects",
        "summary": "List projects",
        "description": "Lists every project owned by the authenticated person, newest first. Requires a PERSON token (project keys get 403). Not paginated: `page.total` and `page.hasMore` are `null`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Projects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "All projects of the owner.",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "operationId": "createProject",
        "summary": "Create a project",
        "description": "Creates a project (and its SES configuration set — this can take several seconds; use a generous timeout). Requires a PERSON token. Idempotent by (owner, name): repeating with an existing name returns 409 with the existing project in `error.details` and WITHOUT the secret key. The `secretKey` (`msk_…`) is returned ONLY in this 201 response.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Project name (truncated to 120 characters)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Project created. Store `secretKey` now: it is never shown again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "A project with this name already exists for this owner (`code` CONFLICT, `details.reason` = `duplicate_name`, plus the public project fields).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{id}": {
      "get": {
        "operationId": "getProject",
        "summary": "Get a project",
        "description": "Returns one project. Project credentials can only read their own project; a project that does not exist or belongs to someone else is 404.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateProject",
        "summary": "Update a project",
        "description": "Renames the project and/or sets where reputation warnings are e-mailed. Only `name` and `notifyEmail` are editable. `notifyEmail: \"\"` clears it (warnings go back to the owner e-mail); omitting a field leaves it unchanged.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "minProperties": 1,
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "New name. Must not be blank."
                  },
                  "notifyEmail": {
                    "type": "string",
                    "description": "Address for reputation warnings, or an empty string to clear it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteProject",
        "summary": "Delete a project",
        "description": "Deletes the project and what it created outside the database: its SES domain identities and configuration set (first — if SES refuses, nothing is deleted and the call can be retried), then the row, then its R2 attachments in the background. Requires `full` permission (secret key, `full` API key or person token).",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Project deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Empty object: the status code already says it worked.",
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "description": "SES cleanup failed; the project was NOT deleted (`code` UPSTREAM_ERROR, `retriable: true`, `details.reason` = `ses_cleanup_failed`, `details.falhas` lists what failed). Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/me/deletion-preview": {
      "get": {
        "operationId": "getAccountDeletionPreview",
        "summary": "Preview account deletion",
        "description": "What this account still owns in Messages. Requires a PERSON token.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Inventory.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountInventory"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/me": {
      "delete": {
        "operationId": "deleteAccount",
        "summary": "Delete the account data in Messages",
        "description": "Confirms the account has nothing left in Messages. REFUSES with 409 while any project exists — delete each project with `DELETE /projects/{id}` first (that is what removes the SES identities). The account itself lives in Infrastructure Auth. Requires a PERSON token.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Nothing left in Messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "deleted"
                  ],
                  "properties": {
                    "deleted": {
                      "$ref": "#/components/schemas/AccountInventory"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Projects still exist (`code` CONFLICT, `details.reason` = `has_resources`, plus `projects`, `domains`, `verifiedDomains`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{id}/domains": {
      "get": {
        "operationId": "listDomains",
        "summary": "List domains",
        "description": "Lists the project's sender domains with their DNS records and verification status. Not paginated: `page.total` and `page.hasMore` are `null`.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Domains.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "All domains of the project.",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Domain"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "operationId": "createDomain",
        "summary": "Register a domain",
        "description": "Registers a sender domain (creates the identity in Amazon SES) and returns the DNS records to create: 3 DKIM CNAMEs, SPF TXT and MX on `send.<domain>` (never the apex), and an optional DMARC TXT. Counts against the plan domain limit of the whole account (402). Requires `full` permission. A scheme prefix or path in `domain` is stripped.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The domain, e.g. `example.com`.",
                    "examples": [
                      "example.com"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Domain registered; create the DNS records returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The domain is already registered (in any project).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{id}/domains/{domainId}": {
      "delete": {
        "operationId": "deleteDomain",
        "summary": "Remove a domain",
        "description": "Deletes the domain identity in SES and the domain from the project. Requires `full` permission.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "domainId",
            "in": "path",
            "required": true,
            "description": "Domain id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Domain removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Empty object: the status code already says it worked.",
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{id}/domains/{domainId}/verify": {
      "post": {
        "operationId": "verifyDomain",
        "summary": "Check domain verification",
        "description": "Asks SES for the current verification state, stores it and returns the domain. No request body. DNS propagation can take minutes to hours; sending from the domain only works once `status` is `verified`.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "domainId",
            "in": "path",
            "required": true,
            "description": "Domain id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current domain state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/emails": {
      "post": {
        "operationId": "sendEmail",
        "summary": "Send an email",
        "description": "Sends a transactional email through Amazon SES. Requires a PROJECT credential (`msk_` secret key or an `rm_` API key with `send` or `full`) — a person token gets 403. The sender domain must be registered in the project and verified. Checks run in this order: idempotency, project quota/pause, sender domain, recipients, plan allowance, suppression list, template, attachments, SES. Quota counts RECIPIENTS (to + cc + bcc). Send `Idempotency-Key` (header) or `idempotencyKey` (body) on every call: repeating a key that already succeeded returns 200 with the original send and `deduplicated: true`; a key whose attempt failed is reused.",
        "tags": [
          "Emails"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Idempotency key. Takes precedence over `idempotencyKey` in the body. Derive it from the fact (e.g. `welcome/user-123`), not from the clock.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmailRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deduplicated: a send with this idempotency key already exists; nothing new was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendEmailResult"
                }
              }
            }
          },
          "202": {
            "description": "Accepted by SES.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendEmailResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "description": "Not allowed: person token instead of a project credential, read-only API key, sender domain not registered or not verified, or sending paused for this project (`details.reason` = `sending_paused`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Template not found (`details.reason` = `template_not_found`, `details.available` lists existing names) or attachment key not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "One or more recipients are in the project suppression list (`details.reason` = `suppressed`, `details.addresses`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Project reputation quota exceeded (`details.reason` = `quota_exceeded`) or the plan 24h ceiling reached (`details.reason` = `daily_sends`, `details.resetsAt`). Waiting resolves it; do not upgrade for this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "SES refused the message (`code` UPSTREAM_ERROR, `retriable: true`, `details.emailId` is the logged failed send). Retrying with the same idempotency key is safe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listEmails",
        "summary": "List sends",
        "description": "Lists the project send log, newest first. NOTE: this route uses its own pagination defaults — `limit` defaults to 20 and is capped at 100.",
        "tags": [
          "Emails"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Which project to read when authenticating as a PERSON (Auth JWT / OAuth token). Required in that case (403 without it; 404 if the project is not yours). Ignored for project credentials (`msk_`/`rm_`), which always act on their own project.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, capped at 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of items to skip. Default 0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by send status.",
            "schema": {
              "$ref": "#/components/schemas/EmailStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sends.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "A page of sends.",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Email"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/emails/{id}": {
      "get": {
        "operationId": "getEmail",
        "summary": "Get a send with its timeline",
        "description": "Returns one send with its HTML/text body and the timeline of delivery events received from SES (oldest first).",
        "tags": [
          "Emails"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Email (send) id (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Which project to read when authenticating as a PERSON (Auth JWT / OAuth token). Required in that case (403 without it; 404 if the project is not yours). Ignored for project credentials (`msk_`/`rm_`), which always act on their own project.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/attachments": {
      "post": {
        "operationId": "uploadAttachment",
        "summary": "Upload an attachment",
        "description": "Uploads a file to R2 and returns its `key`, to be referenced later in `POST /emails` `attachments[].key`. The request body is the raw file bytes; `Content-Type` is stored as the file type. Max 6 MB per file (and 8 MB summed per message at send time). Requires a PROJECT credential with `send` or `full`.",
        "tags": [
          "Attachments"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "fileName",
            "in": "query",
            "required": false,
            "description": "File name. Default `anexo`.",
            "schema": {
              "type": "string",
              "default": "anexo"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "*/*": {
              "schema": {
                "type": "string",
                "contentMediaType": "application/octet-stream"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Attachment"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "description": "File larger than 6 MB (`code` PAYLOAD_TOO_LARGE). Send a link instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/reputation": {
      "get": {
        "operationId": "getReputation",
        "summary": "Project quota and sending health",
        "description": "The project's 24h recipient quota, usage, bounce/complaint rates and pause state — the same computation that blocks sending.",
        "tags": [
          "Reputation"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Which project to read when authenticating as a PERSON (Auth JWT / OAuth token). Required in that case (403 without it; 404 if the project is not yours). Ignored for project credentials (`msk_`/`rm_`), which always act on their own project.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reputation state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reputation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/reputation/unpause": {
      "post": {
        "operationId": "unpauseSending",
        "summary": "Resume a paused project",
        "description": "Resumes sending after an automatic pause. Requires a PERSON token and `?projectId=`. The owner can resume once; a second resume goes through support (403, `details.reason` = `unpause_limit`). The quota restarts at the initial step. If the project is not paused, returns 200 with the current reputation state.",
        "tags": [
          "Reputation"
        ],
        "security": [
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "description": "The project to resume.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sending resumed (empty object), or already active (current reputation state).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "description": "Empty object: the status code already says it worked.",
                      "additionalProperties": false
                    },
                    {
                      "$ref": "#/components/schemas/Reputation"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/suppressions": {
      "get": {
        "operationId": "listSuppressions",
        "summary": "List suppressed addresses",
        "description": "Addresses blocked in this project (permanent bounces and spam complaints are added automatically), newest first.",
        "tags": [
          "Suppressions"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Which project to read when authenticating as a PERSON (Auth JWT / OAuth token). Required in that case (403 without it; 404 if the project is not yours). Ignored for project credentials (`msk_`/`rm_`), which always act on their own project.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 50, capped at 200 (the applied value is reported in `page.limit`).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of items to skip. Default 0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suppressions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "A page of suppressions.",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Suppression"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/suppressions/{email}": {
      "delete": {
        "operationId": "deleteSuppression",
        "summary": "Release a suppressed address",
        "description": "Removes the address from the project suppression list so it can receive again. Requires `full` permission. Returns 200 even if the address was not suppressed.",
        "tags": [
          "Suppressions"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "description": "The suppressed address, URL-encoded. Compared in lowercase.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Which project to read when authenticating as a PERSON (Auth JWT / OAuth token). Required in that case (403 without it; 404 if the project is not yours). Ignored for project credentials (`msk_`/`rm_`), which always act on their own project.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Address released.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Empty object: the status code already says it worked.",
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{id}/api-keys": {
      "get": {
        "operationId": "listApiKeys",
        "summary": "List API keys",
        "description": "Lists the project API keys (`rm_…`), newest first. Never returns the key value or hash — only the prefix.",
        "tags": [
          "API keys"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "API keys.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "All API keys of the project.",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "operationId": "createApiKey",
        "summary": "Create an API key",
        "description": "Creates a revocable project API key (`rm_…`) with permission `read`, `send` (default) or `full`. The `key` value is returned ONLY in this response.",
        "tags": [
          "API keys"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Key label (truncated to 80 characters)."
                  },
                  "permission": {
                    "type": "string",
                    "enum": [
                      "read",
                      "send",
                      "full"
                    ],
                    "default": "send",
                    "description": "Any other value falls back to `send`."
                  },
                  "domainId": {
                    "type": "string",
                    "description": "Optional domain id stored with the key."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Key created. Store `key` now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{id}/api-keys/{keyId}": {
      "delete": {
        "operationId": "deleteApiKey",
        "summary": "Revoke an API key",
        "description": "Revokes the API key. Returns 200 even if no key with this id exists in the project.",
        "tags": [
          "API keys"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "keyId",
            "in": "path",
            "required": true,
            "description": "API key id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Key revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Empty object: the status code already says it worked.",
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{id}/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "List webhook endpoints",
        "description": "Lists the endpoints that receive delivery events, newest first. The signing secret is never returned here.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "All webhook endpoints of the project.",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Register a webhook endpoint",
        "description": "Registers a URL that receives signed delivery events (see the `emailEvent` webhook). The `secret` (`whsec_…`) is returned ONLY in this response.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/WebhookEventType"
                    },
                    "description": "Event types to receive. Empty or omitted = all events."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Endpoint registered. Store `secret` now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{id}/webhooks/{webhookId}": {
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Remove a webhook endpoint",
        "description": "Removes the endpoint. Returns 200 even if no endpoint with this id exists in the project.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "description": "Webhook endpoint id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Empty object: the status code already says it worked.",
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{id}/templates": {
      "get": {
        "operationId": "listTemplates",
        "summary": "List templates",
        "description": "Lists the project templates. Not paginated: `page.total` and `page.hasMore` are `null`.",
        "tags": [
          "Templates"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Templates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "All templates of the project.",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "operationId": "upsertTemplate",
        "summary": "Create or update a template",
        "description": "Creates a template, or updates subject/body of the template with the same name (upsert by name). Bodies may use `{{variable}}` placeholders, rendered at send time from `variables`.",
        "tags": [
          "Templates"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "subject"
                ],
                "description": "At least one of `html` or `text` is required.",
                "anyOf": [
                  {
                    "required": [
                      "html"
                    ]
                  },
                  {
                    "required": [
                      "text"
                    ]
                  }
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "subject": {
                    "type": "string"
                  },
                  "html": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing template updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateRef"
                }
              }
            }
          },
          "201": {
            "description": "Template created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateRef"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{id}/templates/{name}": {
      "delete": {
        "operationId": "deleteTemplate",
        "summary": "Delete a template",
        "description": "Deletes the template by name. Past sends are unaffected (they store the final body); future sends naming it get 404. Requires `full` permission.",
        "tags": [
          "Templates"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Template name, URL-encoded (it may contain spaces or accents).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "name"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/metrics": {
      "get": {
        "operationId": "getMetrics",
        "summary": "Send metrics",
        "description": "Send counts by status and delivery/bounce/complaint rates (percent) over the last `days` days.",
        "tags": [
          "Metrics"
        ],
        "security": [
          {
            "secretKey": []
          },
          {
            "apiKey": []
          },
          {
            "authJwt": []
          },
          {
            "oauth2": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Which project to read when authenticating as a PERSON (Auth JWT / OAuth token). Required in that case (403 without it; 404 if the project is not yours). Ignored for project credentials (`msk_`/`rm_`), which always act on their own project.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Window in days. Default 7, max 90.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 7
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Metrics.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metrics"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "webhooks": {
    "emailEvent": {
      "post": {
        "operationId": "receiveEmailEvent",
        "summary": "Delivery event sent to your endpoint",
        "description": "Posted to each active endpoint registered with `POST /projects/{id}/webhooks` whose `events` include this type (or are empty). Verify `x-riligar-signature` = `t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the endpoint secret>` before trusting the body. 5 s timeout, no retry.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "x-riligar-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<timestamp>,v1=<hex hmac>`"
          },
          {
            "name": "x-riligar-event",
            "in": "header",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "secretKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "msk_<64 hex>",
        "description": "Project secret key (`msk_…`), sent as `Authorization: Bearer msk_…`. Identifies ONE project with `full` permission. Returned once, at project creation."
      },
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "rm_<40 hex>",
        "description": "Revocable project API key (`rm_…`), sent as `Authorization: Bearer rm_…`. Identifies ONE project with permission `read`, `send` or `full`."
      },
      "authJwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Infrastructure Auth session JWT (RS256, issuer `riligar-auth`, verified against https://auth.worker.myinfrastructure.click/.well-known/jwks.json). Identifies a PERSON (`sub`), who owns projects; has `full` permission on them."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization code with PKCE (S256), issued by Infrastructure Auth. Use it to act on behalf of a person; scopes are listed in the authorization server metadata.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://auth.worker.myinfrastructure.click/oauth/authorize",
            "tokenUrl": "https://auth.worker.myinfrastructure.click/oauth/token",
            "scopes": {
              "messages:read": "Read projects, domains, sends, metrics.",
              "messages:write": "Send email and manage templates/suppressions.",
              "messages:admin": "Administrative operations (person tokens only)."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine code (the contract). `message` is for humans and may change.",
                "enum": [
                  "UNAUTHORIZED",
                  "FORBIDDEN",
                  "WRONG_AUDIENCE",
                  "INSUFFICIENT_SCOPE",
                  "NOT_FOUND",
                  "CONFLICT",
                  "NAME_TAKEN",
                  "EXPIRED",
                  "HAS_RESOURCES",
                  "VALIDATION_ERROR",
                  "UNPROCESSABLE",
                  "RATE_LIMITED",
                  "PAYLOAD_TOO_LARGE",
                  "PLAN_LIMIT",
                  "SUBSCRIPTION_REQUIRED",
                  "INTERNAL_ERROR",
                  "SERVICE_UNAVAILABLE",
                  "UPSTREAM_ERROR"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation (Portuguese)."
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Structured context the caller can act on (e.g. `reason`, `limit`, `resetsAt`)."
              },
              "retriable": {
                "type": "boolean",
                "description": "Present and `true` for RATE_LIMITED, SERVICE_UNAVAILABLE and UPSTREAM_ERROR."
              }
            }
          }
        }
      },
      "Page": {
        "type": "object",
        "required": [
          "limit",
          "offset",
          "total",
          "hasMore"
        ],
        "properties": {
          "limit": {
            "type": "integer",
            "description": "Applied page size."
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "`null` when the route does not count."
          },
          "hasMore": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`null` when `total` is unknown."
          }
        }
      },
      "ServiceStatus": {
        "type": "object",
        "required": [
          "service",
          "status",
          "checkedAt",
          "checks"
        ],
        "properties": {
          "service": {
            "type": "string",
            "const": "riligar-messages"
          },
          "status": {
            "type": "string",
            "enum": [
              "operational",
              "degraded",
              "down"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "description",
                "status",
                "ms"
              ],
              "additionalProperties": true,
              "properties": {
                "name": {
                  "type": "string",
                  "enum": [
                    "database",
                    "ses",
                    "storage"
                  ]
                },
                "description": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "operational",
                    "degraded",
                    "down"
                  ]
                },
                "ms": {
                  "type": "integer"
                },
                "error": {
                  "type": "string",
                  "const": "unavailable"
                },
                "sendingEnabled": {
                  "type": "boolean",
                  "description": "`ses` check only."
                },
                "enforcement": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "`ses` check only."
                },
                "dailyQuota": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "`ses` check only."
                },
                "maxSendRate": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "`ses` check only."
                },
                "deliveryFeedback": {
                  "type": "boolean",
                  "description": "`ses` check only: whether SNS delivery feedback is wired."
                }
              }
            }
          }
        }
      },
      "PlanNotice": {
        "type": "object",
        "required": [
          "state",
          "plan",
          "limit",
          "message"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "unknown",
              "active",
              "free",
              "none"
            ]
          },
          "plan": {
            "type": [
              "string",
              "null"
            ]
          },
          "limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Domain limit of the plan; `null` when unknown or unlimited."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Banner text, only when `state` is `none`."
          }
        }
      },
      "Project": {
        "type": "object",
        "required": [
          "id",
          "name",
          "publicKey",
          "notifyEmail",
          "notificationsTo",
          "sendingPaused",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "publicKey": {
            "type": "string",
            "description": "`pk_…`"
          },
          "notifyEmail": {
            "type": [
              "string",
              "null"
            ],
            "description": "Address chosen for reputation warnings."
          },
          "notificationsTo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where warnings actually go (`notifyEmail`, else the cached owner e-mail)."
          },
          "sendingPaused": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Project"
          },
          {
            "type": "object",
            "required": [
              "secretKey"
            ],
            "properties": {
              "secretKey": {
                "type": "string",
                "description": "`msk_…` — shown only once."
              }
            }
          }
        ]
      },
      "AccountInventory": {
        "type": "object",
        "required": [
          "projects",
          "domains",
          "verifiedDomains"
        ],
        "properties": {
          "projects": {
            "type": "integer"
          },
          "domains": {
            "type": "integer"
          },
          "verifiedDomains": {
            "type": "integer"
          }
        }
      },
      "DnsRecord": {
        "type": "object",
        "required": [
          "record",
          "type",
          "name",
          "value",
          "ttl"
        ],
        "properties": {
          "record": {
            "type": "string",
            "enum": [
              "DKIM",
              "SPF",
              "MX",
              "DMARC"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "CNAME",
              "TXT",
              "MX"
            ]
          },
          "name": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "priority": {
            "type": "integer",
            "description": "MX only."
          },
          "ttl": {
            "type": "string",
            "const": "Auto"
          },
          "optional": {
            "type": "boolean",
            "description": "DMARC only: recommended, not required."
          }
        }
      },
      "Domain": {
        "type": "object",
        "required": [
          "id",
          "domain",
          "region",
          "status",
          "canSend",
          "dnsRecords",
          "verifiedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "region": {
            "type": "string",
            "examples": [
              "us-east-1"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "verified",
              "failed",
              "temporary_failure"
            ]
          },
          "canSend": {
            "type": "boolean",
            "description": "`true` only when `status` is `verified`."
          },
          "dnsRecords": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DnsRecord"
            }
          },
          "verifiedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "EmailStatus": {
        "type": "string",
        "enum": [
          "queued",
          "sent",
          "delivered",
          "bounced",
          "complained",
          "failed",
          "rejected"
        ]
      },
      "SendEmailRequest": {
        "type": "object",
        "required": [
          "from"
        ],
        "description": "`to`, `cc` and `bcc` together need at least one valid address. `subject` and one of `html`/`text` are required unless provided by `templateName`.",
        "properties": {
          "from": {
            "type": "string",
            "description": "Sender, `addr@domain` or `Name <addr@domain>`. The domain must be verified in this project."
          },
          "to": {
            "description": "Recipient address or list.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "cc": {
            "description": "Cc address or list.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "bcc": {
            "description": "Bcc address or list.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "replyTo": {
            "description": "Reply-To address or list.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "subject": {
            "type": "string"
          },
          "html": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "templateName": {
            "type": "string",
            "description": "Name of a project template; its subject/body fill whatever is not given here."
          },
          "variables": {
            "type": "object",
            "additionalProperties": true,
            "description": "Values for `{{variable}}` placeholders (template sends only)."
          },
          "attachments": {
            "type": "array",
            "description": "Files previously uploaded with `POST /attachments`.",
            "items": {
              "type": "object",
              "required": [
                "key"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "description": "The key returned by `POST /attachments`."
                },
                "fileName": {
                  "type": "string"
                },
                "contentType": {
                  "type": "string"
                },
                "contentId": {
                  "type": "string",
                  "description": "Set to embed inline (`cid:`)."
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "value"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            },
            "description": "SES message tags; echoed in webhook events."
          },
          "headers": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "value"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            },
            "description": "Extra MIME headers."
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Used when the `Idempotency-Key` header is absent."
          }
        }
      },
      "SendEmailResult": {
        "type": "object",
        "required": [
          "id",
          "messageId",
          "status",
          "deduplicated"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "messageId": {
            "type": [
              "string",
              "null"
            ],
            "description": "SES message id."
          },
          "status": {
            "$ref": "#/components/schemas/EmailStatus"
          },
          "deduplicated": {
            "type": "boolean"
          }
        }
      },
      "Email": {
        "type": "object",
        "required": [
          "id",
          "messageId",
          "from",
          "to",
          "subject",
          "status",
          "error",
          "source",
          "createdAt",
          "sentAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "messageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/EmailStatus"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "mcp",
              "dashboard"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "sentAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "EmailDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Email"
          },
          {
            "type": "object",
            "required": [
              "html",
              "text",
              "events"
            ],
            "properties": {
              "html": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "text": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "events": {
                "type": "array",
                "description": "Delivery timeline, oldest first.",
                "items": {
                  "type": "object",
                  "required": [
                    "type",
                    "occurredAt"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "SES event, lowercased.",
                      "examples": [
                        "send",
                        "delivery",
                        "bounce",
                        "complaint",
                        "open",
                        "click",
                        "reject",
                        "delivery_delay",
                        "rendering_failure"
                      ]
                    },
                    "occurredAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "Attachment": {
        "type": "object",
        "required": [
          "key",
          "fileName",
          "contentType",
          "size"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "`<projectId>/<uuid>/<fileName>`"
          },
          "fileName": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "size": {
            "type": "integer",
            "description": "Bytes."
          }
        }
      },
      "Reputation": {
        "type": "object",
        "required": [
          "ageDays",
          "effectiveAgeDays",
          "quota",
          "baseQuota",
          "used",
          "remaining",
          "paused",
          "pausedReason",
          "sent",
          "bounces",
          "complaints",
          "level",
          "reason",
          "bounceRate",
          "complaintRate"
        ],
        "properties": {
          "ageDays": {
            "type": "integer"
          },
          "effectiveAgeDays": {
            "type": "integer",
            "description": "Age counted from the last resume, when there was one."
          },
          "quota": {
            "type": "integer",
            "description": "Recipients allowed in the 24h window after the health adjustment."
          },
          "baseQuota": {
            "type": "integer"
          },
          "used": {
            "type": "integer",
            "description": "Recipients sent in the window."
          },
          "remaining": {
            "type": "integer"
          },
          "paused": {
            "type": "boolean"
          },
          "pausedReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "sent": {
            "type": "integer"
          },
          "bounces": {
            "type": "integer"
          },
          "complaints": {
            "type": "integer"
          },
          "level": {
            "type": "string",
            "enum": [
              "ok",
              "warn",
              "pause"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "amostra_pequena",
              "bounce",
              "complaint"
            ]
          },
          "bounceRate": {
            "type": "number"
          },
          "complaintRate": {
            "type": "number"
          }
        }
      },
      "Suppression": {
        "type": "object",
        "required": [
          "id",
          "projectId",
          "email",
          "reason",
          "note",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "enum": [
              "bounce",
              "complaint",
              "manual"
            ]
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "SES diagnostic code, when present."
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix timestamp in seconds."
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "id",
          "name",
          "prefix",
          "permission",
          "lastUsedAt",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "First 11 characters of the key."
          },
          "permission": {
            "type": "string",
            "enum": [
              "read",
              "send",
              "full"
            ]
          },
          "lastUsedAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix timestamp in seconds."
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix timestamp in seconds."
          }
        }
      },
      "ApiKeyCreated": {
        "type": "object",
        "required": [
          "id",
          "name",
          "key",
          "permission"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "description": "`rm_…` — shown only once."
          },
          "permission": {
            "type": "string",
            "enum": [
              "read",
              "send",
              "full"
            ]
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "description": "`email.` + the lowercased SES event type. Not validated on input.",
        "examples": [
          "email.send",
          "email.delivery",
          "email.bounce",
          "email.complaint",
          "email.open",
          "email.click",
          "email.reject",
          "email.delivery_delay",
          "email.rendering_failure"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "url",
          "events",
          "isActive",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "isActive": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix timestamp in seconds."
          }
        }
      },
      "WebhookEndpointCreated": {
        "type": "object",
        "required": [
          "id",
          "url",
          "secret",
          "events"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "secret": {
            "type": "string",
            "description": "`whsec_…` — HMAC key for `x-riligar-signature`; shown only once."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "type": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "required": [
              "email_id",
              "message_id",
              "from",
              "to",
              "subject",
              "tags"
            ],
            "properties": {
              "email_id": {
                "type": "string"
              },
              "message_id": {
                "type": "string"
              },
              "from": {
                "type": "string"
              },
              "to": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "subject": {
                "type": "string"
              },
              "tags": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "name",
                    "value"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "value": {
                      "type": "string"
                    }
                  }
                }
              },
              "bounce": {
                "type": "object",
                "additionalProperties": true,
                "description": "`email.bounce` only: the SES bounce object."
              },
              "complaint": {
                "type": "object",
                "additionalProperties": true,
                "description": "`email.complaint` only: the SES complaint object."
              },
              "click": {
                "type": "object",
                "additionalProperties": true,
                "description": "`email.click` only: the SES click object."
              }
            }
          }
        }
      },
      "Template": {
        "type": "object",
        "required": [
          "id",
          "projectId",
          "name",
          "subject",
          "bodyHtml",
          "bodyText",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "bodyHtml": {
            "type": [
              "string",
              "null"
            ]
          },
          "bodyText": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix timestamp in seconds."
          },
          "updatedAt": {
            "type": "integer",
            "description": "Unix timestamp in seconds."
          }
        }
      },
      "TemplateRef": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Metrics": {
        "type": "object",
        "required": [
          "days",
          "total",
          "byStatus",
          "rates"
        ],
        "properties": {
          "days": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "byStatus": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Count per email status."
          },
          "rates": {
            "type": "object",
            "required": [
              "delivery",
              "bounce",
              "complaint"
            ],
            "properties": {
              "delivery": {
                "type": "number",
                "description": "Percent."
              },
              "bounce": {
                "type": "number",
                "description": "Percent."
              },
              "complaint": {
                "type": "number",
                "description": "Percent."
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid or missing input (`code` VALIDATION_ERROR).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid, revoked or expired credential (`code` UNAUTHORIZED). Send `Authorization: Bearer <token>`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Plan limit reached (`code` PLAN_LIMIT, or SUBSCRIPTION_REQUIRED when there is no subscription); `details` brings `resource`, `current`, `limit`, `plan`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but not allowed: wrong credential type for this route, insufficient API key permission, or `?projectId=` missing for a person token (`code` FORBIDDEN).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found, or not visible to this credential (`code` NOT_FOUND). Projects of other owners are indistinguishable from missing ones.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected failure (`code` INTERNAL_ERROR).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
