{
  "openapi": "3.1.0",
  "info": {
    "title": "Postwind API",
    "description": "HTTP API of Postwind, a self-hosted email campaign service. Authenticate with `Authorization: Bearer <key>`; errors are problem+json (RFC 9457) with a stable `code`.",
    "version": "0.1.0"
  },
  "paths": {
    "/api/v1/admin/keys": {
      "get": {
        "tags": [
          "keys"
        ],
        "summary": "List admin keys",
        "description": "The admin keys in id order, a page at a time, the bootstrap key and\nrevoked and expired ones included, without secrets. Admin keys only.",
        "operationId": "list_admin_keys",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin key (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "keys"
        ],
        "summary": "Create an admin key",
        "description": "Creates an admin key, which may do everything, and records\n`key.created`. The answer carries the key string, once. Admin keys\nonly.",
        "operationId": "create_admin_key",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAdminKeyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The key and its key string, shown this once",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "`no-store`"
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The key's URL (`DELETE` revokes it)"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKey"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin key (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/admin/keys/{key_id}": {
      "delete": {
        "tags": [
          "keys"
        ],
        "summary": "Revoke an admin key",
        "description": "Revokes the admin key for good, the caller's own included. Records\n`key.revoked`. Revoking a revoked key changes nothing. The last usable\nadmin key is not revoked: create another first. Admin keys only.",
        "operationId": "revoke_admin_key",
        "parameters": [
          {
            "name": "key_id",
            "in": "path",
            "description": "The key's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The key is revoked"
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin key (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such admin key (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The last usable admin key (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects": {
      "get": {
        "tags": [
          "projects"
        ],
        "summary": "List projects",
        "description": "Projects in creation order, a page at a time. Admin keys only.",
        "operationId": "list_projects",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_deleted",
            "in": "query",
            "description": "Deleted projects too; default `false`",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin key (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "projects"
        ],
        "summary": "Create a project",
        "description": "Creates a project with a slug unique among live projects and the given\nsettings (the rest default), and records `project.created`. Admin keys\nonly.",
        "operationId": "create_project",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProjectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The project",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The project's URL"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin key (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The slug is taken (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): `unknown_field`, `required`, `invalid_type`, `invalid`, `out_of_range`, `unknown_relay`; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}": {
      "get": {
        "tags": [
          "projects"
        ],
        "summary": "Read a project",
        "description": "One project, deleted or not (a deleted one has `deleted_at`). An admin\nkey reads any project; a project key with scope `read` reads its own.",
        "operationId": "get_project",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "projects"
        ],
        "summary": "Delete a project",
        "description": "Deletes the project softly: it refuses every change from now on, an\nadmin key can still read it, its slug is free. Records\n`project.deleted`. Deleting a deleted project changes nothing. Admin\nkeys only.",
        "operationId": "delete_project",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The project is deleted"
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin key (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "projects"
        ],
        "summary": "Update a project",
        "description": "Renames the project and changes the settings the body names; the rest\nkeep their values. The slug never changes. Admin keys only.",
        "operationId": "update_project_settings",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProjectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The project after the update",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin key (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns": {
      "get": {
        "tags": [
          "campaigns"
        ],
        "summary": "List a project's campaigns",
        "description": "The project's campaigns in creation order, a page at a time,\noptionally of one status. Needs scope `read`; an admin key may list any\nproject's.",
        "operationId": "list_campaigns",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only campaigns in this status: `draft`, `scheduled`, `launching`, `sending`, `paused`, `completed`, `cancelled`, `failed`",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "campaigns"
        ],
        "summary": "Create a campaign",
        "description": "Creates a campaign in the project, a `draft`: nothing is sent until it\nis launched. Needs scope `write`; an admin key may create one in any\nlive project. Writes `campaign.created`.",
        "operationId": "create_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCampaignRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The campaign, a draft",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The campaign's URL"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): `unknown_field`, `required`, `invalid_type`, `invalid` (also a list or template that is not the project's, a deleted list, a partial), `out_of_range`, `unknown_relay`; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns/{campaign_id}": {
      "get": {
        "tags": [
          "campaigns"
        ],
        "summary": "Read a campaign",
        "description": "One campaign of the project, in whatever status. Needs scope `read`.",
        "operationId": "get_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "campaigns"
        ],
        "summary": "Update a campaign",
        "description": "Changes the fields the body names; the rest keep their values. Only a\n`draft` or `scheduled` campaign takes a change. Needs scope `write`.",
        "operationId": "update_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCampaignRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The campaign after the update",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The campaign is past `scheduled`, or its project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns/{campaign_id}/cancel": {
      "post": {
        "tags": [
          "campaigns"
        ],
        "summary": "Cancel a campaign",
        "description": "Cancels a scheduled, sending or paused campaign for good\n(`campaign.cancelled`): every delivery still `queued` or `deferred` is\n`cancelled` in one write; a letter already handed to a transport keeps\nits outcome. A cancelled campaign answers as it is (and its open\ndeliveries are cancelled again). Needs scope `send`.",
        "operationId": "cancel_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign, `cancelled`",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `send` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The campaign is a `draft`, `launching`, `completed` or `failed`, or its project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns/{campaign_id}/launch": {
      "post": {
        "tags": [
          "campaigns"
        ],
        "summary": "Launch a campaign",
        "description": "Launches the draft now: checks every precondition (a template that\nrenders, a verified sending domain for the sender, live lists, a\ntransport the node has, recipients, the daily quota) and returns every\nunmet one at once; otherwise fixes the template's and partials'\nversions, writes one queued delivery per recipient and answers the\ncampaign `sending` (`campaign.launched`), or `failed` with `last_error`\nwhen the recipients were gone by then (`campaign.failed`). A campaign\nleft `launching` by an interrupted launch is resumed. Needs scope\n`send`.",
        "operationId": "launch_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign after the launch: `sending`, or `failed`",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `send` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The campaign is not a `draft`, is being launched by another request, or its project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Unmet preconditions, every one in `errors` (`precondition_failed`): `template.<part>` / `subject_override` with a template code, `from_email` / `required` or `domain_not_verified`, `list_ids.<index>` / `invalid`, `transport` / `transport_unavailable`, `list_ids` / `no_recipients`, `settings.daily_send_quota` / `quota_exceeded`; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns/{campaign_id}/pause": {
      "post": {
        "tags": [
          "campaigns"
        ],
        "summary": "Pause a campaign",
        "description": "Pauses a sending campaign (`campaign.paused`): the delivery engine\ntakes no new delivery of it from its next look (a guard batch or a\npoll); the letters under way finish. A paused campaign answers as it\nis. Needs scope `send`.",
        "operationId": "pause_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign, `paused`",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `send` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The campaign is not `sending` (or `paused`), or its project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns/{campaign_id}/preflight": {
      "post": {
        "tags": [
          "campaigns"
        ],
        "summary": "Preflight a campaign",
        "description": "What launching the draft now would do: its recipients after\ndeduplication and suppression, its sender's domain and that domain's\nwarnings, its transport, how long it takes at its throttle, and every\nprecondition it does not meet. Changes nothing. Needs scope `read`.",
        "operationId": "preflight_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The preflight",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignPreflight"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The campaign is not a `draft` (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns/{campaign_id}/resume": {
      "post": {
        "tags": [
          "campaigns"
        ],
        "summary": "Resume a campaign",
        "description": "Resumes a paused campaign (`campaign.resumed`): it is `sending` again\nand the delivery engine takes its deliveries from its next poll. A\nsending campaign answers as it is. Needs scope `send`.",
        "operationId": "resume_campaign",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign, `sending`",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `send` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The campaign is not `paused` (or `sending`), or its project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/campaigns/{campaign_id}/stats": {
      "get": {
        "tags": [
          "campaigns"
        ],
        "summary": "Read a campaign's stats",
        "description": "Where the campaign's sending is: its status, its recipients, its\ndeliveries by status (`queued`, `deferred`, `sent`, `bounced`,\n`failed`, `suppressed`, `cancelled`), counted from the deliveries now,\nand its opens and clicks (`total`: every hit; `unique`: the distinct\nsubscribers). Any status; a draft's counts are zero. Needs scope `read`.",
        "operationId": "get_campaign_stats",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "campaign_id",
            "in": "path",
            "description": "The campaign's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign's stats",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignStats"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or campaign in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/domains": {
      "get": {
        "tags": [
          "domains"
        ],
        "summary": "List sending domains",
        "description": "The project's domains in creation order, a page at a time. Needs scope\n`read`.",
        "operationId": "list_sending_domains",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendingDomainList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "domains"
        ],
        "summary": "Add a sending domain",
        "description": "Adds a domain to the project, `pending`, with a DKIM key pair of its own\n(RSA 2048; the private key is stored sealed and no answer shows it), and\nrecords `domain.created`. The answer lists the DNS records to create.\nNeeds scope `write`. A domain is unique in its project.",
        "operationId": "add_sending_domain",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddSendingDomainRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The domain and the records it needs",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The domain's URL"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddedSendingDomain"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project has the domain, or is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): `unknown_field`, `required`, `invalid_type`, `invalid`, `unknown_ip_pool`; or the node has no usable `web.public_url` (`precondition_failed`, `not_configured`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/domains/{domain_id}": {
      "get": {
        "tags": [
          "domains"
        ],
        "summary": "Get a sending domain",
        "description": "One domain of the project: its status and settings. Needs scope `read`.",
        "operationId": "get_sending_domain",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "domain_id",
            "in": "path",
            "description": "The domain's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The domain",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendingDomain"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or domain in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "domains"
        ],
        "summary": "Delete a sending domain",
        "description": "Deletes the domain for good: no read finds it again, its name is free\nfor a new domain of the project, and its DKIM private key is not kept.\nRecords `domain.deleted`. The domain's records stay in DNS: remove them\nat the provider. A second call finds nothing (`404`). Needs scope\n`write`. A domain with a campaign in `sending` will be refused (`409`)\nonce campaigns exist (P5).",
        "operationId": "delete_sending_domain",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "domain_id",
            "in": "path",
            "description": "The domain's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The domain is deleted"
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or domain in it (a deleted domain included), or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "domains"
        ],
        "summary": "Change a sending domain",
        "description": "Changes a domain's `ip_pool`, `tracking_host` or `dns_provider`. A\nchanged `ip_pool` or `tracking_host` moves the domain back to `pending`\nand the answer carries the new `required_records`; a changed\n`dns_provider` does not. Needs scope `write`.",
        "operationId": "update_sending_domain",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "domain_id",
            "in": "path",
            "description": "The domain's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSendingDomainRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The domain after the change",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdatedSendingDomain"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or domain in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields (`validation_failed`): an `ip_pool` the node does not declare is `unknown_ip_pool`, clearing `ip_pool` or `dns_provider` is `invalid`; or a new `tracking_host` needs `web.public_url` or the change's records cannot be built (`precondition_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/domains/{domain_id}/dns/apply": {
      "post": {
        "tags": [
          "domains"
        ],
        "summary": "Apply a domain's DNS records",
        "description": "Applies the domain's required records through its DNS provider and answers\neach record's outcome. For a `manual`-provider domain nothing is changed\nand every record's outcome is `manual` (the records to enter by hand).\nPostwind creates and changes the SPF and MX of the bounce subdomain, the\nDKIM record and the tracking CNAME; it creates `_dmarc` only when absent\nand never changes it; and it refuses (never overwrites) a bounce-subdomain\nTXT that is not SPF. A provider failure on one record is that record's\noutcome, not an error; the first `unavailable` record stops the run, which\nends within 30 s, and a repeat converges. Until the node has a TLS client,\na Porkbun domain answers `precondition_failed`. Needs scope `write`.",
        "operationId": "apply_dns_records",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "domain_id",
            "in": "path",
            "description": "The domain's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Each record's outcome",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppliedDnsRecords"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or domain in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The node has no usable `web.public_url`, no longer declares the domain's IP pool, Porkbun is not configured for the domain or cannot be reached over HTTPS from this build, or the domain has no registrable domain (`precondition_failed`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/domains/{domain_id}/records": {
      "get": {
        "tags": [
          "domains"
        ],
        "summary": "Get a domain's required DNS records",
        "description": "The DNS records the domain needs, for entry by hand: SPF and MX of the\nbounce subdomain, DKIM, DMARC, and the tracking host's CNAME. Built\nfrom the domain and the node's config (the pool's addresses, relays,\n`web.public_url`). Needs scope `read`.",
        "operationId": "get_required_dns_records",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "domain_id",
            "in": "path",
            "description": "The domain's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The records",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequiredDnsRecords"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or domain in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The node has no usable `web.public_url`, or no longer declares the domain's IP pool (`precondition_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/domains/{domain_id}/verify": {
      "post": {
        "tags": [
          "domains"
        ],
        "summary": "Verify a sending domain",
        "description": "Checks the domain's DNS records through the resolver now and records the\noutcome: the domain becomes `verified` when every check but `ptr` and\n`tracking_tls` is `ok`, `failed` otherwise. The answer is the domain\nwith its `checks`. Needs scope `write`.",
        "operationId": "verify_sending_domain",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "domain_id",
            "in": "path",
            "description": "The domain's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The domain with its checks and new status",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendingDomain"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or domain in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The node has no usable `web.public_url`, or no longer declares the domain's IP pool (`precondition_failed`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/keys": {
      "get": {
        "tags": [
          "keys"
        ],
        "summary": "List a project's keys",
        "description": "The project's keys in id order, a page at a time, revoked and expired\nones included, without secrets. An admin key may list any project's;\na project key needs scope `keys`, in its own project.",
        "operationId": "list_api_keys",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `keys` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "keys"
        ],
        "summary": "Create a project key",
        "description": "Creates a key of the project with the given scopes and records\n`key.created`. The answer carries the key string, once. An admin key\nmay create one in any live project; a project key needs scope `keys`,\nworks in its own project only, and grants only scopes it holds (and,\nif it expires, no later expiry).",
        "operationId": "create_api_key",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The key and its key string, shown this once",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "`no-store`"
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The key's URL (`DELETE` revokes it)"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKey"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `keys`, or granting more than it holds (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/keys/{key_id}": {
      "delete": {
        "tags": [
          "keys"
        ],
        "summary": "Revoke a project key",
        "description": "Revokes the key for good: from then on it is refused with\n`key_revoked`. Records `key.revoked`. Revoking a revoked key changes\nnothing. An admin key may revoke any key of a live project; a project\nkey needs scope `keys`, in its own project, and may revoke itself.",
        "operationId": "revoke_api_key",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "key_id",
            "in": "path",
            "description": "The key's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The key is revoked"
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `keys` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or key in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/lists": {
      "get": {
        "tags": [
          "lists"
        ],
        "summary": "List a project's lists",
        "description": "The project's lists in creation order, a page at a time; deleted ones\nonly with `include_deleted=true`. Needs scope `read`; an admin key may\nlist any project's.",
        "operationId": "list_lists",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_deleted",
            "in": "query",
            "description": "Deleted lists too; default `false`",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "lists"
        ],
        "summary": "Create a list",
        "description": "Creates a mailing list in the project. Needs scope `write`; an admin\nkey may create one in any live project.",
        "operationId": "create_list",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateListRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The list",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The list's URL"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): `unknown_field`, `required` (also `confirmation_template_id` with `double_opt_in`), `invalid_type`, `invalid`; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/lists/{list_id}": {
      "get": {
        "tags": [
          "lists"
        ],
        "summary": "Read a list",
        "description": "One list of the project, deleted or not (a deleted one has\n`deleted_at`). Needs scope `read`.",
        "operationId": "get_list",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "path",
            "description": "The list's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or list in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "lists"
        ],
        "summary": "Delete a list",
        "description": "Deletes the list softly: it stays readable (`deleted_at`, and in the\nlist with `include_deleted=true`) and takes no change from now on; its\nsubscribers are kept. Deleting a deleted list changes nothing. Needs\nscope `write`.",
        "operationId": "delete_list",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "path",
            "description": "The list's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The list is deleted"
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or list in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "lists"
        ],
        "summary": "Update a list",
        "description": "Renames the list and changes the options the body names; the rest keep\ntheir values. A deleted list takes no change. Needs scope `write`.",
        "operationId": "update_list",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "path",
            "description": "The list's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateListRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The list after the update",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or list in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The list or its project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/lists/{list_id}/subscribers": {
      "get": {
        "tags": [
          "subscribers"
        ],
        "summary": "List a list's subscribers",
        "description": "The list's subscribers, ordered by id, a page at a time; an optional\n`status` filter. Needs scope `read`; an admin key may list any\nproject's.",
        "operationId": "list_subscribers",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "path",
            "description": "The list's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only this status: pending, active, unsubscribed, bounced, complained",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriberList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or list, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "subscribers"
        ],
        "summary": "Add a subscriber",
        "description": "Adds a subscriber to the list, or, if the address is already on it,\nmerges the attributes (new values win) and keeps its status. Needs scope\n`write`. A new subscriber answers `201`; a re-add answers `200`.",
        "operationId": "add_subscriber",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "path",
            "description": "The list's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSubscriberRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The address was already on the list; its attributes were merged",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "201": {
            "description": "The subscriber was created",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The subscriber's URL"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or list, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project or list is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): a bad `email`, an attribute that is not a scalar, a bad key, too many keys or too large; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/lists/{list_id}/subscribers/by-email/{email}": {
      "get": {
        "tags": [
          "subscribers"
        ],
        "summary": "Find a subscriber by email",
        "description": "The subscriber of this address on the list; the address is normalized,\nso any case or surrounding whitespace finds it. Needs scope `read`.",
        "operationId": "find_subscriber",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "path",
            "description": "The list's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "email",
            "in": "path",
            "description": "The subscriber's address",
            "required": true,
            "schema": {
              "type": "string",
              "format": "email"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber, list or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/lists/{list_id}/subscribers/import": {
      "post": {
        "tags": [
          "subscribers"
        ],
        "summary": "Import subscribers",
        "description": "Adds up to 10 000 records to the list in one request, each by the rule\nof `add_subscriber` (normalized email, attributes merged, status by the\nlist), as if added one by one in order, and answers record by record.\nAn invalid record is reported by its index and fails nothing else; two\nrecords of one address are merged into one subscriber in order. Needs\nscope `write`. A repeat adds nothing twice and changes no status.",
        "operationId": "import_subscribers",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "path",
            "description": "The list's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportSubscribersRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The import's report",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriberImportReport"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or list, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project or list is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The body's shape, every problem in `errors` (`validation_failed`; a record's field is named `subscribers.<index>.<field>`), or more than 10 000 records (`subscribers` / `invalid`); nothing is written; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/subscribers/{subscriber_id}": {
      "get": {
        "tags": [
          "subscribers"
        ],
        "summary": "Read a subscriber",
        "description": "One subscriber of the project by its id, whatever list it is on. Needs\nscope `read`.",
        "operationId": "get_subscriber",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "subscriber_id",
            "in": "path",
            "description": "The subscriber's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "subscribers"
        ],
        "summary": "Erase a subscriber",
        "description": "Erases the subscriber for good, on request: its row is deleted, and in\nits events (and in the address's suppression events) the address is\nreplaced by `erased:<hash>`, in the background; the erasure itself is\nrecorded as `subscriber.erased`. The address's suppressions stay, so it\ngets no mail it had opted out of if it is added again. Needs scope\n`write`; works on a subscriber of a deleted list too. A second call is\n`404`.",
        "operationId": "erase_subscriber",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "subscriber_id",
            "in": "path",
            "description": "The subscriber's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The subscriber is erased"
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber (an erased one included) or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "subscribers"
        ],
        "summary": "Change a subscriber's attributes",
        "description": "Merges the attributes the body names into the subscriber's (new values\nwin, `null` removes a key); the status is never changed. Needs scope\n`write`. A subscriber of a deleted list takes no change.",
        "operationId": "update_subscriber_attributes",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "subscriber_id",
            "in": "path",
            "description": "The subscriber's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSubscriberRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The subscriber after the change",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The subscriber's list or project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid attributes, every one in `errors` (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/subscribers/{subscriber_id}/events": {
      "get": {
        "tags": [
          "subscribers"
        ],
        "summary": "A subscriber's events",
        "description": "The history of one subscriber, oldest first, a page at a time: the\nevents about it (`subscriber.*`) since it was added. Events about the\naddress and no subscriber (suppressions) are in `list_suppressions`.\nNeeds scope `read`; an admin key may read any project's. An erased\nsubscriber has none to read (`404`).",
        "operationId": "get_subscriber_events",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "subscriber_id",
            "in": "path",
            "description": "The subscriber's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriberEventList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/subscribers/{subscriber_id}/reactivate": {
      "post": {
        "tags": [
          "subscribers"
        ],
        "summary": "Reactivate a bounced or complained subscriber",
        "description": "Takes a `bounced` or `complained` subscriber back to `active`, with a\n`reason`. Only an admin key may: a project key gets `403` whatever its\nscopes. Suppressions are not touched (remove a `hard_bounce` or\n`complaint` one with `DELETE .../suppressions/{email}`). An `active`\nsubscriber stays as it is.",
        "operationId": "reactivate_subscriber",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "subscriber_id",
            "in": "path",
            "description": "The subscriber's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReactivateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The subscriber after the call",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key, whatever its scopes (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project or list is deleted, or the subscriber is pending or unsubscribed (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "No or an invalid `reason` (`validation_failed`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/subscribers/{subscriber_id}/resubscribe": {
      "post": {
        "tags": [
          "subscribers"
        ],
        "summary": "Subscribe an unsubscribed subscriber again",
        "description": "Takes an `unsubscribed` subscriber back with fresh consent: `active`,\nor `pending` on a list with double opt-in; the suppression its\nunsubscribe left is removed. Needs scope `write`, a live list and\n`consent_source`. Another suppression of the list (the project's, a\nmanual one) refuses it: remove that first. An `active` or `pending`\nsubscriber stays as it is.",
        "operationId": "resubscribe_subscriber",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "subscriber_id",
            "in": "path",
            "description": "The subscriber's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResubscribeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The subscriber after the call",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project or list is deleted, the subscriber is bounced or complained, or another suppression holds the list back (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "No or an invalid `consent_source` (`validation_failed`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/subscribers/{subscriber_id}/unsubscribe": {
      "post": {
        "tags": [
          "subscribers"
        ],
        "summary": "Unsubscribe a subscriber",
        "description": "Unsubscribes the subscriber from its list (`scope: \"list\"`, the default)\nor the address from every list of the project (`scope: \"project\"`).\nFirst the address is suppressed (`list:<list_id>` or `project`), then\nthe subscriptions become `unsubscribed`. Needs scope `write`; works on a\ndeleted list too. A repeat changes nothing and answers the same.",
        "operationId": "unsubscribe_subscriber",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "subscriber_id",
            "in": "path",
            "description": "The subscriber's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnsubscribeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The subscriber after the call",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscriber"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such subscriber or project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "`scope` is not `list` or `project` (`validation_failed`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/suppressions": {
      "get": {
        "tags": [
          "suppressions"
        ],
        "summary": "List suppressions",
        "description": "The project's suppressions by address, then scope, a page at a time; or\nthose of one address (`email`, normalized). Needs scope `read`; an admin\nkey may list any project's.",
        "operationId": "list_suppressions",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "email",
            "in": "query",
            "description": "Only this address",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppressionList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "suppressions"
        ],
        "summary": "Suppress an address",
        "description": "Adds the address to the project's suppression list by hand (`reason`\n`manual`), for the project or one list: it gets no mail of that scope.\nSubscriber statuses are not changed. Needs scope `write`. An address\nalready suppressed for the scope keeps its suppression: `200`.",
        "operationId": "add_suppression",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSuppressionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The address was suppressed for the scope already; nothing changed",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Suppression"
                }
              }
            }
          },
          "201": {
            "description": "The address is suppressed",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Where `DELETE` removes this suppression"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Suppression"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): a bad `email`, a `scope` that is not `project` or a list of this project, a `reason` other than `manual`; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/suppressions/{email}": {
      "delete": {
        "tags": [
          "suppressions"
        ],
        "summary": "Remove an address's suppressions",
        "description": "Removes every suppression of the address, or only the one of `scope`.\nThe address is the path segment, percent-decoded and normalized (any\ncase and surrounding whitespace find it). Subscriber statuses are not\nchanged: an unsubscribed subscriber stays unsubscribed. Needs scope\n`write`.",
        "operationId": "remove_suppression",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "email",
            "in": "path",
            "description": "The address, percent-encoded",
            "required": true,
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "description": "Only this suppression: `project` or `list:<list_id>`; none: every one of the address",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The suppressions are removed"
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "The address has no such suppression, or no such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "`scope` is not `project` or `list:<list_id>` (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/templates": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "List templates",
        "description": "The project's templates in id order, each by its latest version,\nwithout sources, a page at a time. Needs scope `read`.",
        "operationId": "list_templates",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "description": "Only templates of this kind",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/TemplateKind"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500; default 50",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "maximum": 500,
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateList"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid (`validation_failed`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "templates"
        ],
        "summary": "Create a template",
        "description": "Creates a message template, its version 1, after checking it: it\nparses, uses only what Postwind templates offer and only variables of\nthe render context, renders with a sample context within the render\nlimits, and, when the project's `auto_footer` is false, each body shows\n`{{ unsubscribe_url }}`. Each problem is in `errors` with its line and\ncolumn. Needs scope `write`.",
        "operationId": "create_template",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTemplateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Version 1 of the template",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The template's URL"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted, or a template has the name (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): `unknown_field`, `required`, `invalid_type`, `invalid`, and for a source, with `line L, column C:` in `message`: `template_syntax`, `template_not_allowed`, `unknown_variable`, `template_limit`, `template_render`, `unsubscribe_link_missing`; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/templates/{template_id}": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Read a template",
        "description": "The template's latest version. Needs scope `read`.",
        "operationId": "get_template",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "template_id",
            "in": "path",
            "description": "The template's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The latest version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or template in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "put": {
        "tags": [
          "templates"
        ],
        "summary": "Save a new version of a template",
        "description": "Saves the sources as the template's next version (the latest version's\nnumber plus one), checked as at creation. Every save is a version. The\nname and the kind stay. Needs scope `write`.",
        "operationId": "update_template",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "template_id",
            "in": "path",
            "description": "The template's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTemplateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The new version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `write` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or template in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted, or another save took the version number at the same moment (`conflict`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields or sources, every one in `errors` (`validation_failed`), as at creation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/templates/{template_id}/preview": {
      "post": {
        "tags": [
          "templates"
        ],
        "summary": "Preview a message template",
        "description": "Renders the template's latest version, with the latest versions of the\npartials it includes, as a subscriber would get it: the subscriber\n`subscriber_id`'s email, attributes and list name, or\n`sample_attributes`, or, with neither, sample values. The unsubscribe\nand confirmation links are samples (`https://postwind.invalid/...`);\n`campaign.name` is `Sample campaign`. The text is the text body, or\nthe HTML made into text; when the project's `auto_footer` is true, the\nfooter with the unsubscribe link is added to each that does not show\n`{{ unsubscribe_url }}`. Nothing is written. Needs scope `read`.",
        "operationId": "preview_template",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "template_id",
            "in": "path",
            "description": "The message template's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewTemplateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The rendered message",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplatePreview"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, template or subscriber in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): both `subscriber_id` and `sample_attributes`, invalid attributes, a partial (`template_id`), or the template does not render with this data, with the codes of saving; or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/templates/{template_id}/test-send": {
      "post": {
        "tags": [
          "templates"
        ],
        "summary": "Send a test message",
        "description": "Renders the template's latest version as the preview does with\n`sample_attributes` (or sample values) and sends it to each address of\n`to` through the project's transport: from the project's\n`settings.from_email` (and `from_name`, `reply_to`), the subject\nprefixed `[TEST] `, with the header `X-Postwind-Test: 1`. An address\nin the project's suppression list gets nothing (`suppressed`). Each\naddress's outcome is in `results`; a refusal by the recipient's server\nis an outcome, not an error. Nothing is written. Needs scope `send`.\nA node that cannot send through the project's transport answers `422`\n`precondition_failed` (`transport_unavailable`); `available_transports`\nof `GET /api/v1/server` lists the ones it can.",
        "operationId": "send_test_email",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "template_id",
            "in": "path",
            "description": "The message template's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestSendRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The outcome for each address",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "`true` when the answer is a replay of an earlier request with the same `Idempotency-Key`; absent otherwise."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestSend"
                }
              }
            }
          },
          "400": {
            "description": "Not JSON (`invalid_json`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `send` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project or template in it, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The project is deleted (`conflict`); or a request with the same `Idempotency-Key` has not answered yet (`idempotency_key_in_progress`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB (`body_too_large`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Not `application/json` (`unsupported_media_type`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, every one in `errors` (`validation_failed`): no address or more than 10 in `to`, an invalid address (`to.<index>`), invalid attributes, a partial (`template_id`), or the template does not render with this data; or unmet preconditions (`precondition_failed`): no `settings.from_email`, a transport the node cannot send through (`transport` / `transport_unavailable`); or the `Idempotency-Key` was used in the last 24 h for another request (`idempotency_key_reused`), or is not 1 to 128 visible ASCII characters (`validation_failed`, field `Idempotency-Key`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/projects/{project_id}/templates/{template_id}/versions/{version}": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Read one version of a template",
        "description": "The version `version` of the template. Needs scope `read`.",
        "operationId": "get_template_version",
        "parameters": [
          {
            "name": "project_id",
            "in": "path",
            "description": "The project's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "template_id",
            "in": "path",
            "description": "The template's id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "The version number, from 1",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "That version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "401": {
            "description": "No valid key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "A project key without scope `read` (`forbidden`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such project, template in it or version, or another project's (`not_found`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/server": {
      "get": {
        "tags": [
          "server"
        ],
        "summary": "The node",
        "description": "Version, enabled subservices, delivery transports, DNS providers and\nIP pools of the node that answered, from its config. Any valid key may\ncall it; a pool's addresses are shown to an admin key only.",
        "operationId": "get_server_info",
        "responses": {
          "200": {
            "description": "The node",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerResponse"
                }
              }
            }
          },
          "401": {
            "description": "No key (`key_missing`), not a valid key (`key_invalid`), a revoked key (`key_revoked`) or an expired one (`key_expired`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/api/v1/whoami": {
      "get": {
        "tags": [
          "server"
        ],
        "summary": "The calling key",
        "description": "Kind, project, scopes and expiry of the key the request carries. Any\nvalid key may call it.",
        "operationId": "whoami",
        "responses": {
          "200": {
            "description": "The calling key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoamiResponse"
                }
              }
            }
          },
          "401": {
            "description": "No key (`key_missing`), not a valid key (`key_invalid`), a revoked key (`key_revoked`) or an expired one (`key_expired`)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "504": {
            "$ref": "#/components/responses/StorageTimeout"
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "AddSendingDomainRequest": {
        "type": "object",
        "description": "A new sending domain.",
        "required": [
          "domain",
          "dns_provider"
        ],
        "properties": {
          "bounce_subdomain": {
            "type": "string",
            "description": "The domain of the return path (`MAIL FROM`), below `domain`; left\nout: `bounce.<domain>`.",
            "example": "bounce.news.example.com"
          },
          "dkim_selector": {
            "type": "string",
            "description": "The DKIM selector: labels of letters, digits and inner hyphens, at\nmost 63 characters; left out: `pw1`.",
            "example": "pw1"
          },
          "dns_provider": {
            "$ref": "#/components/schemas/DnsProvider",
            "description": "Who applies the records: `porkbun`, or `manual` for entry by hand."
          },
          "domain": {
            "type": "string",
            "description": "The domain of the sender addresses: a host name, a subdomain or an\ninternational name (kept in punycode).",
            "example": "news.example.com"
          },
          "ip_pool": {
            "type": "string",
            "description": "The IP pool of the node's config to send from (`GET /api/v1/server`\nlists them); left out: `default`.",
            "example": "default"
          },
          "tracking_host": {
            "type": [
              "string",
              "null"
            ],
            "description": "A host for tracking and unsubscribe links; `null` or left out: none\n(links use the node's `web.public_url`).",
            "example": "links.example.com"
          }
        }
      },
      "AddedSendingDomain": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SendingDomain"
          },
          {
            "type": "object",
            "required": [
              "required_records"
            ],
            "properties": {
              "required_records": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RequiredRecord"
                },
                "description": "What to create in DNS, in this order."
              }
            }
          }
        ],
        "description": "A sending domain just added, and the records to create for it."
      },
      "ApiKey": {
        "type": "object",
        "description": "An API key, without its secret.",
        "required": [
          "id",
          "name",
          "kind",
          "project_id",
          "prefix",
          "scopes",
          "created_at",
          "expires_at",
          "revoked_at",
          "last_used_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "`null`: never."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "What revoking the key takes.",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "kind": {
            "$ref": "#/components/schemas/ApiKeyKind"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key was last used; `null`: not recorded yet."
          },
          "name": {
            "type": "string",
            "example": "billing backend"
          },
          "prefix": {
            "type": "string",
            "description": "The public part of the key string, `pw_<adm|prj>_<prefix>_...`: how\nto tell which key a client holds.",
            "example": "1a2b3c4d"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The key's project; `null` for an admin key."
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key was revoked; `null` while it is not."
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "description": "What the key may do; every scope for an admin key."
          }
        }
      },
      "ApiKeyKind": {
        "type": "string",
        "description": "The kind of an API key.",
        "enum": [
          "admin",
          "project"
        ]
      },
      "ApiKeyList": {
        "type": "object",
        "description": "One page of keys.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            },
            "description": "In id order; revoked and expired keys included."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "AppliedDnsRecord": {
        "type": "object",
        "description": "One required record and what `apply` did to it.",
        "required": [
          "purpose",
          "type",
          "name",
          "content",
          "outcome",
          "detail"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "The value the record should hold."
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "A message for the outcomes that carry one (`conflict`,\n`provider_error`, `unavailable`); `null` otherwise."
          },
          "name": {
            "type": "string",
            "description": "The owner name, without a trailing dot.",
            "example": "bounce.news.example.com"
          },
          "outcome": {
            "$ref": "#/components/schemas/DnsApplyOutcome"
          },
          "purpose": {
            "$ref": "#/components/schemas/RecordPurpose"
          },
          "type": {
            "$ref": "#/components/schemas/RecordType"
          }
        }
      },
      "AppliedDnsRecords": {
        "type": "object",
        "description": "The result of applying a domain's DNS records.",
        "required": [
          "dns_provider",
          "records"
        ],
        "properties": {
          "dns_provider": {
            "$ref": "#/components/schemas/DnsProvider",
            "description": "Who applied the records: `porkbun`, or `manual` (nothing was changed;\neach record's outcome is `manual`)."
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppliedDnsRecord"
            },
            "description": "One entry per required record, in the order they are created."
          }
        }
      },
      "Branding": {
        "type": "object",
        "description": "How the public pages look.",
        "required": [
          "brand_name",
          "support_email",
          "language",
          "text_overrides"
        ],
        "properties": {
          "brand_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "$ref": "#/components/schemas/Language"
          },
          "support_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "text_overrides": {
            "type": "object",
            "description": "Texts of the pages by key.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          }
        }
      },
      "BrandingInput": {
        "type": "object",
        "description": "Branding of the public pages to set.",
        "properties": {
          "brand_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The name the pages show, 1 to 128 characters."
          },
          "language": {
            "type": "string",
            "description": "`en` or `ru`.",
            "example": "ru"
          },
          "support_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "The address the pages give for questions."
          },
          "text_overrides": {
            "type": "object",
            "description": "Texts of the pages by key, replacing every override at once: at most\n64, keys `[a-z][a-z0-9_]*`, texts 1 to 2000 characters.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          }
        }
      },
      "Campaign": {
        "type": "object",
        "description": "A campaign of a project.",
        "required": [
          "id",
          "project_id",
          "name",
          "status",
          "list_ids",
          "template_id",
          "from_name",
          "from_email",
          "reply_to",
          "subject_override",
          "transport_override",
          "tracking_override",
          "throttle_per_hour",
          "scheduled_at",
          "template_version",
          "partial_versions",
          "shard_count",
          "total_recipients",
          "last_error",
          "created_at",
          "updated_at",
          "launched_at",
          "completed_at"
        ],
        "properties": {
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "from_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "`null`: the project's `settings.from_email`."
          },
          "from_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null`: the project's `settings.from_name`."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the launch failed; `null` unless `failed`."
          },
          "launched_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "list_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "In the order given."
          },
          "name": {
            "type": "string",
            "example": "Spring sale"
          },
          "partial_versions": {
            "type": "object",
            "description": "The versions of the partials fixed at launch, by name; `{}` before.",
            "additionalProperties": {
              "type": "integer",
              "format": "int32",
              "minimum": 0
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "reply_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "`null`: the project's `settings.reply_to`."
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a scheduled campaign launches; `null` unless scheduled."
          },
          "shard_count": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Fixed at launch; `null` before.",
            "minimum": 0
          },
          "status": {
            "type": "string",
            "description": "`draft`, `scheduled`, `launching`, `sending`, `paused`,\n`completed`, `cancelled` or `failed`.",
            "example": "draft"
          },
          "subject_override": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null`: the template's subject."
          },
          "template_id": {
            "type": "string",
            "format": "uuid"
          },
          "template_version": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "The template's version fixed at launch; `null` before.",
            "minimum": 0
          },
          "throttle_per_hour": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "`null`: the project's `default_throttle_per_hour`, or no limit.",
            "minimum": 0
          },
          "total_recipients": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Recipients of the launch's snapshot; `null` before.",
            "minimum": 0
          },
          "tracking_override": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/TrackingOverride",
                "description": "`null`: the project's `settings.tracking`."
              },
              {
                "type": "null"
              }
            ]
          },
          "transport_override": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null`: the project's transport, then the node's default."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The latest change: creation, fields, status."
          }
        }
      },
      "CampaignDeliveryCounts": {
        "type": "object",
        "description": "How many deliveries of a campaign are in each status; all zero before\nthe launch.",
        "required": [
          "queued",
          "deferred",
          "sent",
          "bounced",
          "failed",
          "suppressed",
          "cancelled",
          "total"
        ],
        "properties": {
          "bounced": {
            "type": "integer",
            "format": "int64",
            "description": "Refused for good at `RCPT` (a hard bounce).",
            "minimum": 0
          },
          "cancelled": {
            "type": "integer",
            "format": "int64",
            "description": "Not sent: the campaign was cancelled.",
            "minimum": 0
          },
          "deferred": {
            "type": "integer",
            "format": "int64",
            "description": "Refused for now; tried again later.",
            "minimum": 0
          },
          "failed": {
            "type": "integer",
            "format": "int64",
            "description": "Refused for good otherwise, out of attempts, or not made.",
            "minimum": 0
          },
          "queued": {
            "type": "integer",
            "format": "int64",
            "description": "Waiting for their first attempt.",
            "minimum": 0
          },
          "sent": {
            "type": "integer",
            "format": "int64",
            "description": "Taken by the recipient's server.",
            "minimum": 0
          },
          "suppressed": {
            "type": "integer",
            "format": "int64",
            "description": "Held back by a suppression or the subscriber's status.",
            "minimum": 0
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "Every delivery: the sum of the above.",
            "minimum": 0
          }
        }
      },
      "CampaignEventCounts": {
        "type": "object",
        "description": "How many events of a kind a campaign has: every one, and the distinct\nsubscribers among them (two opens by one subscriber: `total` 2,\n`unique` 1).",
        "required": [
          "total",
          "unique"
        ],
        "properties": {
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "Every event, repeats included.",
            "minimum": 0
          },
          "unique": {
            "type": "integer",
            "format": "int64",
            "description": "The distinct subscribers of those events.",
            "minimum": 0
          }
        }
      },
      "CampaignList": {
        "type": "object",
        "description": "One page of campaigns.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Campaign"
            },
            "description": "In creation order."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "CampaignPreflight": {
        "type": "object",
        "description": "What a launch of a draft would do now, and what stops it.",
        "required": [
          "campaign_id",
          "recipients",
          "from_email",
          "domain",
          "transport",
          "throttle_per_hour",
          "estimated_duration_secs",
          "violations"
        ],
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "domain": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CampaignPreflightDomain",
                "description": "The project's sending domain of the sender; `null` when it has none."
              },
              {
                "type": "null"
              }
            ]
          },
          "estimated_duration_secs": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "How long sending takes at `throttle_per_hour`, in seconds; `null`\nwithout a limit or a count.",
            "minimum": 0
          },
          "from_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "The sender: the campaign's, else the project's `settings.from_email`;\n`null` when neither names one."
          },
          "recipients": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Recipients after deduplication and suppression: the deliveries the\nlaunch would write. `null` when a list is missing or deleted.",
            "minimum": 0
          },
          "throttle_per_hour": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Most messages an hour: the campaign's, else the project's\n`default_throttle_per_hour`; `null`: no limit.",
            "minimum": 0
          },
          "transport": {
            "type": "string",
            "description": "The transport the messages go through: `direct` or `relay:<name>`.",
            "example": "direct"
          },
          "violations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProblemFieldError"
            },
            "description": "Every unmet precondition of the launch, as `422\nprecondition_failed` would list them; empty: it can be launched."
          }
        }
      },
      "CampaignPreflightDomain": {
        "type": "object",
        "description": "The sending domain of a campaign's sender.",
        "required": [
          "id",
          "domain",
          "status",
          "warnings"
        ],
        "properties": {
          "domain": {
            "type": "string",
            "example": "acme.example"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "`pending`, `verified` or `failed`; only a `verified` one sends.",
            "example": "verified"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampaignPreflightWarning"
            },
            "description": "Checks that do not decide the status and are not `ok` (`ptr`,\n`tracking_tls`): the launch goes ahead, delivery may suffer."
          }
        }
      },
      "CampaignPreflightWarning": {
        "type": "object",
        "description": "One check of a preflight's domain warnings.",
        "required": [
          "check",
          "status",
          "detail"
        ],
        "properties": {
          "check": {
            "type": "string",
            "example": "ptr"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "description": "`missing`, `mismatch` or `warning`.",
            "example": "warning"
          }
        }
      },
      "CampaignStats": {
        "type": "object",
        "description": "A campaign's stats: where its sending is, and its opens and clicks.",
        "required": [
          "campaign_id",
          "status",
          "total_recipients",
          "deliveries",
          "opens",
          "clicks"
        ],
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "clicks": {
            "$ref": "#/components/schemas/CampaignEventCounts",
            "description": "Its clicks: follows of its tracked links (`message.clicked`)."
          },
          "deliveries": {
            "$ref": "#/components/schemas/CampaignDeliveryCounts",
            "description": "Its deliveries by status."
          },
          "opens": {
            "$ref": "#/components/schemas/CampaignEventCounts",
            "description": "Its opens: loads of the open pixel (`message.opened`)."
          },
          "status": {
            "type": "string",
            "description": "The campaign's status now.",
            "example": "sending"
          },
          "total_recipients": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "How many recipients its launch wrote; `null` before the launch.",
            "minimum": 0
          }
        }
      },
      "CheckStatus": {
        "type": "string",
        "description": "How one verification check came out.",
        "enum": [
          "ok",
          "missing",
          "mismatch",
          "warning"
        ]
      },
      "CreateAdminKeyRequest": {
        "type": "object",
        "description": "A new admin key.",
        "required": [
          "name"
        ],
        "properties": {
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key stops working, RFC 3339, later than now; `null` or\nleft out: never."
          },
          "name": {
            "type": "string",
            "description": "What the key is for: 1 to 128 characters, trimmed.",
            "example": "ops"
          }
        }
      },
      "CreateApiKeyRequest": {
        "type": "object",
        "description": "A new project key.",
        "required": [
          "name",
          "scopes"
        ],
        "properties": {
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key stops working, RFC 3339, later than now; `null` or\nleft out: never. A project key that expires grants no later expiry.",
            "example": "2027-01-01T00:00:00Z"
          },
          "name": {
            "type": "string",
            "description": "What the key is for: 1 to 128 characters, trimmed.",
            "example": "billing backend"
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "description": "What the key may do: at least one scope, each once. A project key\ngrants only scopes it holds itself.",
            "example": [
              "read",
              "write"
            ]
          }
        }
      },
      "CreateCampaignRequest": {
        "type": "object",
        "description": "A new campaign: a draft. Fields left out or `null` take the project's\nvalue when the campaign is launched.",
        "required": [
          "name",
          "list_ids",
          "template_id"
        ],
        "properties": {
          "from_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "example": "news@acme.example"
          },
          "from_name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Acme News"
          },
          "list_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The lists it goes to: 1 to 16 distinct ids of live lists of the\nproject. A recipient on several is sent once, as a subscriber of the\nfirst.",
            "maxItems": 16,
            "minItems": 1
          },
          "name": {
            "type": "string",
            "description": "What people call the campaign: 1 to 128 characters, trimmed. Two\ncampaigns may share a name.",
            "example": "Spring sale"
          },
          "reply_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "subject_override": {
            "type": [
              "string",
              "null"
            ],
            "description": "The subject source in place of the template's."
          },
          "template_id": {
            "type": "string",
            "format": "uuid",
            "description": "The template it sends: the id of a message template of the project."
          },
          "throttle_per_hour": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Most messages an hour, 1 to 4294967295.",
            "maximum": 4294967295,
            "minimum": 1
          },
          "tracking_override": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/TrackingOverrideInput"
              },
              {
                "type": "null"
              }
            ]
          },
          "transport_override": {
            "type": [
              "string",
              "null"
            ],
            "description": "`direct` or `relay:<name>` (a relay of the node).",
            "example": "direct"
          }
        }
      },
      "CreateListRequest": {
        "type": "object",
        "description": "A new list.",
        "required": [
          "name"
        ],
        "properties": {
          "confirmation_template_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The template of the confirmation letter: the id of a message\ntemplate of the project; required with `double_opt_in`. `null` or\nleft out: none."
          },
          "double_opt_in": {
            "type": "boolean",
            "description": "Subscribers confirm by a link before they get campaigns; default\n`false`. Needs `confirmation_template_id`."
          },
          "from_override": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/FromOverrideInput",
                "description": "The sender of the list's own letters (confirmations) in place of\nthe project's; `null` or left out: the project's."
              },
              {
                "type": "null"
              }
            ]
          },
          "name": {
            "type": "string",
            "description": "What people call the list: 1 to 128 characters, trimmed. Two lists\nmay share a name.",
            "example": "Weekly news"
          }
        }
      },
      "CreateProjectRequest": {
        "type": "object",
        "description": "A new project.",
        "required": [
          "name",
          "slug"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "What people call the project: 1 to 128 characters, trimmed.",
            "example": "Acme News"
          },
          "settings": {
            "$ref": "#/components/schemas/ProjectSettingsInput",
            "description": "Settings to set; the rest are defaults."
          },
          "slug": {
            "type": "string",
            "description": "Unique among live projects: 3 to 63 lower-case letters, digits and\n`-`. It never changes.",
            "example": "acme-news",
            "pattern": "^[a-z0-9-]{3,63}$"
          }
        }
      },
      "CreateSubscriberRequest": {
        "type": "object",
        "description": "A new subscriber, or a re-add of one already on the list.",
        "required": [
          "email",
          "attributes"
        ],
        "properties": {
          "attributes": {
            "type": "object",
            "description": "Attributes to set: a map of string, number or boolean values. `null`\nfor a key removes it. Up to 64 keys, up to 4 KB of JSON."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The address; any case and surrounding whitespace are normalized.",
            "example": "ann@example.com"
          }
        }
      },
      "CreateSuppressionRequest": {
        "type": "object",
        "description": "A suppression to add by hand.",
        "required": [
          "email",
          "scope"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "The address; any case and surrounding whitespace are normalized.",
            "example": "ann@example.com"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "`manual`, the only reason a client gives (the default).",
            "example": "manual"
          },
          "scope": {
            "type": "string",
            "description": "`project` (every list) or `list:<list_id>` (one list of this\nproject).",
            "example": "project"
          }
        }
      },
      "CreateTemplateRequest": {
        "type": "object",
        "description": "A new template: its version 1.",
        "required": [
          "name",
          "html_body"
        ],
        "properties": {
          "html_body": {
            "type": "string",
            "description": "The HTML body: 1 byte to 1 MiB. Values are HTML-escaped. A message's\nmust show `{{ unsubscribe_url }}`, itself or through the partials it\nincludes, when the project's `auto_footer` is false.",
            "example": "<p>Hi!</p><a href=\"{{ unsubscribe_url }}\">Unsubscribe</a>"
          },
          "kind": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/TemplateKind",
                "description": "`message` (the default) or `partial`: a fragment without a subject\nthat other templates include as `{% include \"name\" %}`."
              },
              {
                "type": "null"
              }
            ]
          },
          "name": {
            "type": "string",
            "description": "Unique in the project: 1 to 128 characters, trimmed.",
            "example": "Welcome"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "The subject, a template too: 1 to 998 characters, one line. A\nmessage needs one; a partial has none.",
            "example": "Welcome, {{ subscriber.attributes.first_name|default('friend') }}"
          },
          "text_body": {
            "type": [
              "string",
              "null"
            ],
            "description": "The text body: 1 byte to 1 MiB; `null` or left out: made from the\nHTML when the message is sent."
          }
        }
      },
      "CreatedApiKey": {
        "type": "object",
        "description": "A key just created: the key, and its key string, shown this once.",
        "required": [
          "id",
          "name",
          "kind",
          "project_id",
          "prefix",
          "scopes",
          "created_at",
          "expires_at",
          "revoked_at",
          "last_used_at",
          "key"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": [
              "string",
              "null"
            ],
            "description": "The key string to send as `Authorization: Bearer`. It is not\nstored and no other answer shows it: keep it now. `null` in a\nreplay (`Idempotent-Replayed: true`) of a request sent again with\nthe same `Idempotency-Key`: the key was created by the first\nrequest, whose answer carried its string; revoke it and create\nanother with a new `Idempotency-Key` if that answer was lost.",
            "example": "pw_prj_1a2b3c4d_..."
          },
          "kind": {
            "$ref": "#/components/schemas/ApiKeyKind"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          }
        }
      },
      "DnsApplyOutcome": {
        "type": "string",
        "description": "What `apply` did to one record.",
        "enum": [
          "created",
          "updated",
          "unchanged",
          "skipped_exists",
          "manual",
          "conflict",
          "provider_error",
          "unavailable"
        ]
      },
      "DnsCredentials": {
        "type": "object",
        "description": "Which DNS providers the project set its own credentials for; the\ncredentials are never returned.",
        "required": [
          "porkbun"
        ],
        "properties": {
          "porkbun": {
            "type": "boolean",
            "description": "Whether the project set its own Porkbun credentials."
          }
        }
      },
      "DnsCredentialsInput": {
        "type": "object",
        "description": "DNS-provider credentials to set.",
        "properties": {
          "porkbun": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PorkbunCredentialsBody",
                "description": "Porkbun credentials; set them, `null` clears them, absent keeps them.\nThey override the node's global `[dns.porkbun]`."
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "DnsProvider": {
        "type": "string",
        "description": "Who applies a domain's DNS records.",
        "enum": [
          "porkbun",
          "manual"
        ]
      },
      "DomainCheck": {
        "type": "object",
        "description": "One verification check: which record, how it came out, and the detail.",
        "required": [
          "name",
          "status",
          "detail"
        ],
        "properties": {
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "What was found instead of what was expected; `null` when `ok`."
          },
          "name": {
            "type": "string",
            "description": "`spf_bounce`, `mx_bounce`, `dkim`, `dmarc`, `tracking_cname`,\n`tracking_tls` or `ptr`.",
            "example": "dkim"
          },
          "status": {
            "$ref": "#/components/schemas/CheckStatus"
          }
        }
      },
      "DomainStatus": {
        "type": "string",
        "description": "Where a sending domain stands.",
        "enum": [
          "pending",
          "verified",
          "failed"
        ]
      },
      "Event": {
        "type": "object",
        "description": "One event of a subscriber's history.",
        "required": [
          "event_id",
          "ts",
          "project_id",
          "type",
          "list_id",
          "subscriber_id",
          "payload"
        ],
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "list_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The list the event is about; `null` for none."
          },
          "payload": {
            "type": "object",
            "description": "What the event says, by type: plain values (strings, integers,\nbooleans, `null`); `actor_key_id` is the key that acted."
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "subscriber_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The subscriber the event is about; `null` for none."
          },
          "ts": {
            "type": "string",
            "format": "date-time"
          },
          "type": {
            "type": "string",
            "description": "`subscriber.created`, `subscriber.unsubscribed`,\n`subscriber.resubscribed`, `subscriber.reactivated`, ...",
            "example": "subscriber.created"
          }
        }
      },
      "FromOverride": {
        "type": "object",
        "description": "The sender of a list's own letters.",
        "required": [
          "from_email",
          "from_name"
        ],
        "properties": {
          "from_email": {
            "type": "string",
            "format": "email"
          },
          "from_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null`: the project's `settings.from_name`."
          }
        }
      },
      "FromOverrideInput": {
        "type": "object",
        "description": "The sender of a list's own letters.",
        "required": [
          "from_email"
        ],
        "properties": {
          "from_email": {
            "type": "string",
            "format": "email",
            "description": "The sender address.",
            "example": "news@acme.example"
          },
          "from_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The sender name, 1 to 128 characters, no line breaks; `null` or left\nout: the project's `settings.from_name`.",
            "example": "Acme News"
          }
        }
      },
      "ImportSubscribersRequest": {
        "type": "object",
        "description": "A batch of subscribers to add to a list: each record as the body of\n`add_subscriber`.",
        "required": [
          "subscribers"
        ],
        "properties": {
          "subscribers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateSubscriberRequest"
            },
            "description": "The records, up to 10 000, in order; the response names an invalid\none by its index here.",
            "maxItems": 10000
          }
        }
      },
      "InvalidImportRecord": {
        "type": "object",
        "description": "A record the import did not add, with the first problem of its values.",
        "required": [
          "index",
          "email",
          "field",
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "`invalid` or `invalid_type`, as in `errors` of a `422`.",
            "example": "invalid"
          },
          "email": {
            "type": "string",
            "description": "The email as the record had it."
          },
          "field": {
            "type": "string",
            "description": "The record's field: `email`, `attributes` or `attributes.<key>`.",
            "example": "email"
          },
          "index": {
            "type": "integer",
            "description": "The record's index in `subscribers`, from 0.",
            "minimum": 0
          },
          "message": {
            "type": "string"
          }
        }
      },
      "IpPool": {
        "type": "object",
        "description": "An IP pool: a named set of outbound addresses.",
        "required": [
          "name",
          "address_count",
          "addresses"
        ],
        "properties": {
          "address_count": {
            "type": "integer",
            "example": 1,
            "minimum": 0
          },
          "addresses": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/IpPoolAddress"
            },
            "description": "The addresses; shown to an admin key only, `null` for a project\nkey."
          },
          "name": {
            "type": "string",
            "example": "default"
          }
        }
      },
      "IpPoolAddress": {
        "type": "object",
        "description": "One outbound address of an IP pool.",
        "required": [
          "ip",
          "public_ip",
          "hostname"
        ],
        "properties": {
          "hostname": {
            "type": "string",
            "description": "The `EHLO` name; the address's PTR points to it.",
            "example": "mx1.example.com"
          },
          "ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "The local address the node sends from; `null`: the system route\nbehind NAT.",
            "example": "203.0.113.10"
          },
          "public_ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "The public address behind NAT; `null` when there is no NAT."
          }
        }
      },
      "Language": {
        "type": "string",
        "description": "The language of the public pages.",
        "enum": [
          "en",
          "ru"
        ]
      },
      "List": {
        "type": "object",
        "description": "A mailing list of a project.",
        "required": [
          "id",
          "project_id",
          "name",
          "double_opt_in",
          "confirmation_template_id",
          "from_override",
          "created_at",
          "updated_at",
          "deleted_at"
        ],
        "properties": {
          "confirmation_template_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The template of the confirmation letter; `null`: none."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the list was deleted; `null` while it is live. A deleted list\ncan only be read."
          },
          "double_opt_in": {
            "type": "boolean",
            "description": "Subscribers confirm by a link before they get campaigns."
          },
          "from_override": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/FromOverride",
                "description": "The sender of the list's own letters; `null`: the project's."
              },
              {
                "type": "null"
              }
            ]
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "name": {
            "type": "string",
            "example": "Weekly news"
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "description": "The list's project."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The latest change: creation, name, options, deletion."
          }
        }
      },
      "ListList": {
        "type": "object",
        "description": "One page of lists.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/List"
            },
            "description": "In creation order."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "PorkbunCredentialsBody": {
        "type": "object",
        "description": "Porkbun API credentials to set.",
        "required": [
          "api_key",
          "secret_api_key"
        ],
        "properties": {
          "api_key": {
            "type": "string"
          },
          "secret_api_key": {
            "type": "string"
          }
        }
      },
      "PreviewPart": {
        "type": "string",
        "description": "The part of a message a warning is about.",
        "enum": [
          "subject",
          "html",
          "text"
        ]
      },
      "PreviewTemplateRequest": {
        "type": "object",
        "description": "Whose data a preview renders with: a subscriber of the project, sample\nattributes, or (neither) the sample context of the check at saving.",
        "properties": {
          "sample_attributes": {
            "type": [
              "object",
              "null"
            ],
            "description": "Attributes to render with, as a subscriber's: string, number or\nboolean values, up to 64 keys, up to 4 KB of JSON; any other\nattribute the template names is undefined. Not with `subscriber_id`;\n`null` or left out: none."
          },
          "subscriber_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "A subscriber of the project: its email, attributes and list name.\nNot with `sample_attributes`; `null` or left out: none."
          }
        }
      },
      "PreviewWarning": {
        "type": "object",
        "description": "Something about the preview to look at.",
        "required": [
          "code",
          "part",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "`subscriber_not_active`, `subscriber_list_deleted`, `empty_subject`,\n`html_too_large`, `auto_footer_added`, `text_from_html`.",
            "example": "auto_footer_added"
          },
          "message": {
            "type": "string"
          },
          "part": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PreviewPart",
                "description": "The part it is about; `null`: the message as a whole."
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "An error response (RFC 9457 problem details).",
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "The error code, stable; the table is in `docs/spec/07-api.md`.",
            "example": "key_invalid"
          },
          "detail": {
            "type": "string",
            "description": "What went wrong this time, for people.",
            "example": "the API key is not valid"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProblemFieldError"
            },
            "description": "Every invalid field or unmet precondition (`422` only); left out\notherwise."
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "description": "The HTTP status code.",
            "example": 401,
            "minimum": 0
          },
          "title": {
            "type": "string",
            "description": "A short summary, the same for every occurrence of the code.",
            "example": "API key invalid"
          },
          "type": {
            "type": "string",
            "description": "`https://postwind.dev/errors/<code>`.",
            "example": "https://postwind.dev/errors/key_invalid"
          }
        }
      },
      "ProblemFieldError": {
        "type": "object",
        "description": "One invalid field or unmet precondition.",
        "required": [
          "field",
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "What is wrong with it, `snake_case`.",
            "example": "invalid"
          },
          "field": {
            "type": "string",
            "description": "The field, as named in the request.",
            "example": "name"
          },
          "message": {
            "type": "string",
            "example": "must not be empty"
          }
        }
      },
      "Project": {
        "type": "object",
        "description": "A project.",
        "required": [
          "id",
          "name",
          "slug",
          "settings",
          "created_at",
          "updated_at",
          "deleted_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the project was deleted; `null` while it is live. A deleted\nproject can only be read."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "name": {
            "type": "string",
            "example": "Acme News"
          },
          "settings": {
            "$ref": "#/components/schemas/ProjectSettings"
          },
          "slug": {
            "type": "string",
            "example": "acme-news"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The latest change: creation, rename, settings, deletion."
          }
        }
      },
      "ProjectList": {
        "type": "object",
        "description": "One page of projects.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Project"
            },
            "description": "In creation order."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "ProjectSettings": {
        "type": "object",
        "description": "A project's settings, every field present.",
        "required": [
          "from_name",
          "from_email",
          "reply_to",
          "transport",
          "tracking",
          "auto_footer",
          "branding",
          "daily_send_quota",
          "default_throttle_per_hour",
          "soft_bounce_suppress_after",
          "dns_credentials"
        ],
        "properties": {
          "auto_footer": {
            "type": "boolean",
            "description": "Add an unsubscribe footer when a template has none."
          },
          "branding": {
            "$ref": "#/components/schemas/Branding"
          },
          "daily_send_quota": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Most messages a day; `null`: no quota.",
            "minimum": 0
          },
          "default_throttle_per_hour": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Default campaign speed, messages an hour; `null`: no limit.",
            "minimum": 0
          },
          "dns_credentials": {
            "$ref": "#/components/schemas/DnsCredentials"
          },
          "from_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "Default sender address of campaigns."
          },
          "from_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Default sender name of campaigns."
          },
          "reply_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "Default `Reply-To`."
          },
          "soft_bounce_suppress_after": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Soft bounces in a row after which an address is suppressed; `null`:\noff.",
            "minimum": 0
          },
          "tracking": {
            "$ref": "#/components/schemas/Tracking"
          },
          "transport": {
            "type": [
              "string",
              "null"
            ],
            "description": "Default transport, `direct` or `relay:<name>`; `null`: the node's\n`delivery.default_transport`."
          }
        }
      },
      "ProjectSettingsInput": {
        "type": "object",
        "description": "Project settings to set: a field left out keeps its value (the default\nfor a new project), `null` clears a field that may be empty.",
        "properties": {
          "auto_footer": {
            "type": "boolean",
            "description": "Add an unsubscribe footer when a template has none."
          },
          "branding": {
            "$ref": "#/components/schemas/BrandingInput"
          },
          "daily_send_quota": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Most messages a day, 1 or more; `null`: no quota.",
            "maximum": 4294967295,
            "minimum": 1
          },
          "default_throttle_per_hour": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Default campaign speed, messages an hour; `null`: no limit.",
            "maximum": 4294967295,
            "minimum": 1
          },
          "dns_credentials": {
            "$ref": "#/components/schemas/DnsCredentialsInput",
            "description": "DNS-provider credentials of the project. Write-only: a read never\nreturns them."
          },
          "from_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "Default sender address of campaigns.",
            "example": "news@acme.example"
          },
          "from_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Default sender name of campaigns, 1 to 128 characters, no line\nbreaks.",
            "example": "Acme News"
          },
          "reply_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "Default `Reply-To`."
          },
          "soft_bounce_suppress_after": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Soft bounces in a row after which an address is suppressed; `null`:\noff.",
            "maximum": 4294967295,
            "minimum": 1
          },
          "tracking": {
            "$ref": "#/components/schemas/TrackingInput"
          },
          "transport": {
            "type": [
              "string",
              "null"
            ],
            "description": "Default transport: `direct` or `relay:<name>` of a relay the node\noffers (`GET /api/v1/server`); `null`: the node's default.",
            "example": "direct"
          }
        }
      },
      "ReactivateRequest": {
        "type": "object",
        "description": "Why a bounced or complained subscriber may get mail again.",
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "description": "1 to 256 characters. Recorded in the event `subscriber.reactivated`.",
            "example": "the mailbox works again (ticket 4711)"
          }
        }
      },
      "RecordPurpose": {
        "type": "string",
        "description": "What a required record is for.",
        "enum": [
          "spf_bounce",
          "mx_bounce",
          "dkim",
          "dmarc",
          "tracking_cname"
        ]
      },
      "RecordType": {
        "type": "string",
        "description": "The type of a DNS record.",
        "enum": [
          "txt",
          "mx",
          "cname"
        ]
      },
      "RejectStage": {
        "type": "string",
        "description": "The session stage a refusal happened at.",
        "enum": [
          "connect",
          "rcpt",
          "data"
        ]
      },
      "RequiredDnsRecords": {
        "type": "object",
        "description": "The records a sending domain needs.",
        "required": [
          "required_records"
        ],
        "properties": {
          "required_records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RequiredRecord"
            },
            "description": "What to create in DNS, in this order."
          }
        }
      },
      "RequiredRecord": {
        "type": "object",
        "description": "A DNS record a sending domain needs.",
        "required": [
          "purpose",
          "type",
          "name",
          "content",
          "priority",
          "create_only_if_absent"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "The whole value; a TXT value over 255 characters is split into\nstrings by the DNS provider, not here.",
            "example": "v=spf1 ip4:203.0.113.10 -all"
          },
          "create_only_if_absent": {
            "type": "boolean",
            "description": "Create it only if the name has no record of this type, and never\nchange one that is there (the DMARC record)."
          },
          "name": {
            "type": "string",
            "description": "The owner name, without a trailing dot.",
            "example": "bounce.news.example.com"
          },
          "priority": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "The MX priority; `null` for other types.",
            "example": 10,
            "minimum": 0
          },
          "purpose": {
            "$ref": "#/components/schemas/RecordPurpose"
          },
          "type": {
            "$ref": "#/components/schemas/RecordType"
          }
        }
      },
      "ResubscribeRequest": {
        "type": "object",
        "description": "The new consent of a resubscribe.",
        "required": [
          "consent_source"
        ],
        "properties": {
          "consent_source": {
            "type": "string",
            "description": "Where the new consent came from (a form, a support ticket); 1 to 256\ncharacters. Recorded in the event `subscriber.resubscribed`.",
            "example": "signup form, 2026-11-07"
          }
        }
      },
      "Scope": {
        "type": "string",
        "description": "A right of a project key. An admin key has all of them.",
        "enum": [
          "read",
          "write",
          "send",
          "keys"
        ]
      },
      "SendingDomain": {
        "type": "object",
        "description": "A sending domain. Its DKIM private key is in no answer.",
        "required": [
          "id",
          "project_id",
          "domain",
          "status",
          "dkim_selector",
          "dkim_public_key",
          "bounce_subdomain",
          "tracking_host",
          "dns_provider",
          "ip_pool",
          "checks",
          "last_checked_at",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "bounce_subdomain": {
            "type": "string",
            "example": "bounce.news.example.com"
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainCheck"
            },
            "description": "One entry per check that ran, in the canonical order; empty until the\ndomain is first verified."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "dkim_public_key": {
            "type": "string",
            "description": "The DKIM public key: base64 of its DER `SubjectPublicKeyInfo`, the\n`p=` of the DKIM record (RSA 2048)."
          },
          "dkim_selector": {
            "type": "string",
            "example": "pw1"
          },
          "dns_provider": {
            "$ref": "#/components/schemas/DnsProvider"
          },
          "domain": {
            "type": "string",
            "description": "In punycode for an international name.",
            "example": "news.example.com"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "ip_pool": {
            "type": "string",
            "example": "default"
          },
          "last_checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the records were last checked (RFC 3339); `null` until then."
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/DomainStatus"
          },
          "tracking_host": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null`: none."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SendingDomainList": {
        "type": "object",
        "description": "One page of sending domains.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SendingDomain"
            },
            "description": "In creation order."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "ServerResponse": {
        "type": "object",
        "description": "What the node that answered runs and offers.",
        "required": [
          "version",
          "enabled_subservices",
          "transports",
          "default_transport",
          "available_transports",
          "dns_providers",
          "ip_pools"
        ],
        "properties": {
          "available_transports": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The transports this node can send through now; a message whose\ntransport is not here is not sent (`test-send` answers `422`\n`transport_unavailable`). Empty until the built-in `direct` and\n`relay` transports ship.",
            "example": []
          },
          "default_transport": {
            "type": "string",
            "description": "The transport a project without its own uses.",
            "example": "direct"
          },
          "dns_providers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "DNS providers a sending domain may name: `manual` always,\n`porkbun` when it is configured.",
            "example": [
              "manual",
              "porkbun"
            ]
          },
          "enabled_subservices": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Subservices enabled on this node, in start order: `api`, `mcp`,\n`web`, `smtp.inbound`, `delivery`.",
            "example": [
              "api",
              "mcp",
              "web",
              "smtp.inbound",
              "delivery"
            ]
          },
          "ip_pools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IpPool"
            },
            "description": "IP pools by name, for a sending domain's `ip_pool`."
          },
          "transports": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Delivery transports a project may use: `direct` (built in) and\n`relay:<name>` for each configured relay.",
            "example": [
              "direct",
              "relay:ses"
            ]
          },
          "version": {
            "type": "string",
            "description": "Postwind version and git commit, as `postwind --version` prints\nthem.",
            "example": "0.1.0 (abc1234)"
          }
        }
      },
      "Subscriber": {
        "type": "object",
        "description": "A subscriber of a list.",
        "required": [
          "id",
          "project_id",
          "list_id",
          "email",
          "attributes",
          "status",
          "source",
          "soft_bounce_streak",
          "created_at",
          "updated_at",
          "confirmed_at",
          "unsubscribed_at"
        ],
        "properties": {
          "attributes": {
            "type": "object",
            "description": "The subscriber's attributes, a map of string, number or boolean\nvalues."
          },
          "confirmed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the subscriber confirmed a double opt-in; `null` otherwise."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "ann@example.com"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "list_id": {
            "type": "string",
            "format": "uuid",
            "description": "The list the subscriber is on."
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "description": "The subscriber's project."
          },
          "soft_bounce_streak": {
            "type": "integer",
            "format": "int32",
            "description": "Soft bounces in a row; reset by a delivery (P4+).",
            "minimum": 0
          },
          "source": {
            "type": "string",
            "description": "How the subscriber was added: `api`, `import` or `confirm`."
          },
          "status": {
            "type": "string",
            "description": "`pending`, `active`, `unsubscribed`, `bounced` or `complained`.",
            "example": "active"
          },
          "unsubscribed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the subscriber unsubscribed; `null` otherwise."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The latest change: creation, an attribute merge, a status change."
          }
        }
      },
      "SubscriberEventList": {
        "type": "object",
        "description": "One page of a subscriber's events.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            },
            "description": "Oldest first: by time, then by event id."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "SubscriberImportReport": {
        "type": "object",
        "description": "What an import did, record by record: `created + updated +\ninvalid.length` is the number of records.",
        "required": [
          "created",
          "updated",
          "invalid",
          "pending_confirmation"
        ],
        "properties": {
          "created": {
            "type": "integer",
            "description": "Records whose address was not on the list: new subscribers.",
            "minimum": 0
          },
          "invalid": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvalidImportRecord"
            },
            "description": "Records not added, by index."
          },
          "pending_confirmation": {
            "type": "integer",
            "description": "Of the created subscribers, those `pending` (the list has double\nopt-in).",
            "minimum": 0
          },
          "updated": {
            "type": "integer",
            "description": "Valid records whose address was already on the list or came earlier\nin the batch: merged into that subscriber, its status kept.",
            "minimum": 0
          }
        }
      },
      "SubscriberList": {
        "type": "object",
        "description": "One page of subscribers.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subscriber"
            },
            "description": "Ordered by id."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "Suppression": {
        "type": "object",
        "description": "An address on the project's suppression list: it gets no mail of the\nscope.",
        "required": [
          "project_id",
          "email",
          "scope",
          "reason",
          "source",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "ann@example.com"
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "description": "The suppression's project."
          },
          "reason": {
            "type": "string",
            "description": "`unsubscribed_all`, `unsubscribed_list`, `hard_bounce`,\n`soft_bounce_limit`, `complaint` or `manual`.",
            "example": "manual"
          },
          "scope": {
            "type": "string",
            "description": "`project` (every list) or `list:<list_id>`.",
            "example": "project"
          },
          "source": {
            "type": "string",
            "description": "Where it came from: `api` (the API or MCP), `web`, `delivery` or\n`inbound`.",
            "example": "api"
          }
        }
      },
      "SuppressionList": {
        "type": "object",
        "description": "One page of suppressions.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Suppression"
            },
            "description": "By address, then by scope."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "Template": {
        "type": "object",
        "description": "One version of a template.",
        "required": [
          "id",
          "project_id",
          "name",
          "kind",
          "version",
          "subject",
          "html_body",
          "text_body",
          "engine",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this version was saved."
          },
          "engine": {
            "$ref": "#/components/schemas/TemplateEngine"
          },
          "html_body": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The template's id, the same in every version.",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "kind": {
            "$ref": "#/components/schemas/TemplateKind"
          },
          "name": {
            "type": "string",
            "example": "Welcome"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null` for a partial."
          },
          "text_body": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null`: the text is made from the HTML when the message is sent."
          },
          "version": {
            "type": "integer",
            "format": "int32",
            "description": "From 1, one more at each save.",
            "example": 2,
            "minimum": 1
          }
        }
      },
      "TemplateEngine": {
        "type": "string",
        "description": "The template engine.",
        "enum": [
          "minijinja"
        ]
      },
      "TemplateKind": {
        "type": "string",
        "description": "What a template is.",
        "enum": [
          "message",
          "partial"
        ]
      },
      "TemplateList": {
        "type": "object",
        "description": "One page of templates.",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateSummary"
            },
            "description": "In id order, each by its latest version."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "TemplatePreview": {
        "type": "object",
        "description": "A message as a subscriber would get it.",
        "required": [
          "subject",
          "html",
          "text",
          "warnings"
        ],
        "properties": {
          "html": {
            "type": "string",
            "description": "The HTML part, with the automatic footer when it was added."
          },
          "subject": {
            "type": "string"
          },
          "text": {
            "type": "string",
            "description": "The text part: the text body, or the HTML made into text; with the\nautomatic footer when it was added."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PreviewWarning"
            }
          }
        }
      },
      "TemplateSummary": {
        "type": "object",
        "description": "A template as a list shows it: its latest version, without sources.",
        "required": [
          "id",
          "project_id",
          "name",
          "kind",
          "version",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the latest version was saved."
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "$ref": "#/components/schemas/TemplateKind"
          },
          "name": {
            "type": "string"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer",
            "format": "int32",
            "description": "The latest version.",
            "minimum": 1
          }
        }
      },
      "TestSend": {
        "type": "object",
        "description": "What a test send did.",
        "required": [
          "transport",
          "from_email",
          "subject",
          "results"
        ],
        "properties": {
          "from_email": {
            "type": "string",
            "description": "The sender: the project's `settings.from_email`.",
            "example": "news@example.com"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TestSendResult"
            },
            "description": "One per distinct address, in the order of the request."
          },
          "subject": {
            "type": "string",
            "description": "The subject every message carries: `[TEST] ` and the rendered one.",
            "example": "[TEST] Welcome, Ann"
          },
          "transport": {
            "type": "string",
            "description": "The transport the messages went through: `direct`, `relay:<name>`.",
            "example": "direct"
          }
        }
      },
      "TestSendOutcome": {
        "type": "string",
        "description": "How the test message to one address went.",
        "enum": [
          "accepted",
          "deferred",
          "rejected",
          "suppressed",
          "failed"
        ]
      },
      "TestSendRequest": {
        "type": "object",
        "description": "The addresses a test message goes to, and the data to render it with.",
        "required": [
          "to"
        ],
        "properties": {
          "sample_attributes": {
            "type": [
              "object",
              "null"
            ],
            "description": "Attributes to render with, as a subscriber's: string, number or\nboolean values, up to 64 keys, up to 4 KB of JSON; any other\nattribute the template names is undefined. `null` or left out:\nevery attribute is the text `sample`."
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "1 to 10 addresses; a repeated address (after normalization) gets\none message.",
            "example": [
              "me@example.com"
            ]
          }
        }
      },
      "TestSendResult": {
        "type": "object",
        "description": "The test message to one address.",
        "required": [
          "email",
          "outcome",
          "message_id",
          "smtp_code",
          "smtp_text",
          "stage"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "The address, normalized.",
            "example": "me@example.com"
          },
          "message_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The message's `Message-ID`, without angle brackets; `null` when it\nwas not sent (`suppressed`)."
          },
          "outcome": {
            "$ref": "#/components/schemas/TestSendOutcome"
          },
          "smtp_code": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "The reply code of a refusal (`deferred`, `rejected`); otherwise\n`null`.",
            "example": 550,
            "minimum": 0
          },
          "smtp_text": {
            "type": [
              "string",
              "null"
            ],
            "description": "The reply text of a refusal (`deferred`, `rejected`); otherwise\n`null`."
          },
          "stage": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/RejectStage",
                "description": "The stage of a `rejected`; otherwise `null`."
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Tracking": {
        "type": "object",
        "description": "What campaigns track by default.",
        "required": [
          "opens",
          "clicks"
        ],
        "properties": {
          "clicks": {
            "type": "boolean"
          },
          "opens": {
            "type": "boolean"
          }
        }
      },
      "TrackingInput": {
        "type": "object",
        "description": "Tracking defaults to set.",
        "properties": {
          "clicks": {
            "type": "boolean",
            "description": "Click redirects."
          },
          "opens": {
            "type": "boolean",
            "description": "Open pixel."
          }
        }
      },
      "TrackingOverride": {
        "type": "object",
        "description": "What a campaign tracks.",
        "required": [
          "opens",
          "clicks"
        ],
        "properties": {
          "clicks": {
            "type": "boolean"
          },
          "opens": {
            "type": "boolean"
          }
        }
      },
      "TrackingOverrideInput": {
        "type": "object",
        "description": "What a campaign tracks in place of the project's `settings.tracking`.",
        "required": [
          "opens",
          "clicks"
        ],
        "properties": {
          "clicks": {
            "type": "boolean",
            "description": "Click redirects."
          },
          "opens": {
            "type": "boolean",
            "description": "The open pixel."
          }
        }
      },
      "UnsubscribeRequest": {
        "type": "object",
        "description": "From what to unsubscribe.",
        "properties": {
          "scope": {
            "type": [
              "string",
              "null"
            ],
            "description": "`list` (default): from the subscriber's list; `project`: from every\nlist of the project.",
            "example": "list"
          }
        }
      },
      "UpdateCampaignRequest": {
        "type": "object",
        "description": "A partial update (JSON merge patch): a field left out keeps its value,\n`null` clears a field that may be empty. `list_ids` and\n`tracking_override` are replaced as a whole. Only a `draft` or\n`scheduled` campaign takes a change.",
        "properties": {
          "from_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "from_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "list_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "1 to 16 distinct ids of live lists of the project.",
            "maxItems": 16,
            "minItems": 1
          },
          "name": {
            "type": "string",
            "example": "Spring sale, final"
          },
          "reply_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "subject_override": {
            "type": [
              "string",
              "null"
            ]
          },
          "template_id": {
            "type": "string",
            "format": "uuid",
            "description": "A message template of the project."
          },
          "throttle_per_hour": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "maximum": 4294967295,
            "minimum": 1
          },
          "tracking_override": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/TrackingOverrideInput"
              },
              {
                "type": "null"
              }
            ]
          },
          "transport_override": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "UpdateListRequest": {
        "type": "object",
        "description": "A partial update (JSON merge patch): a field left out keeps its value,\n`null` clears a field that may be empty. `from_override` is replaced as\na whole.",
        "properties": {
          "confirmation_template_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The template of the confirmation letter, a message template of the\nproject; `null` clears it (not while `double_opt_in` is on)."
          },
          "double_opt_in": {
            "type": "boolean",
            "description": "Turn double opt-in on (needs a `confirmation_template_id`, the\nlist's or this one) or off."
          },
          "from_override": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/FromOverrideInput",
                "description": "The sender of the list's own letters, replaced as a whole; `null`:\nthe project's."
              },
              {
                "type": "null"
              }
            ]
          },
          "name": {
            "type": "string",
            "description": "The new name.",
            "example": "Monthly news"
          }
        }
      },
      "UpdateProjectRequest": {
        "type": "object",
        "description": "A partial update (JSON merge patch): a field left out keeps its value,\n`null` clears a field that may be empty.",
        "properties": {
          "name": {
            "type": "string",
            "description": "The new name.",
            "example": "Acme Inc"
          },
          "settings": {
            "$ref": "#/components/schemas/ProjectSettingsInput",
            "description": "Settings to change; the rest keep their values."
          }
        }
      },
      "UpdateSendingDomainRequest": {
        "type": "object",
        "description": "A change to a sending domain. Every field is optional: left out keeps\nits value; `null` clears a `tracking_host` (the others refuse `null`).",
        "required": [
          "dns_provider"
        ],
        "properties": {
          "dns_provider": {
            "$ref": "#/components/schemas/DnsProvider",
            "description": "Who applies the records: `porkbun` or `manual`."
          },
          "ip_pool": {
            "type": "string",
            "description": "The IP pool to send from (`GET /api/v1/server` lists them). Changing\nit moves the domain to `pending`.",
            "example": "default"
          },
          "tracking_host": {
            "type": [
              "string",
              "null"
            ],
            "description": "A host for tracking and unsubscribe links; `null` clears it. Changing\nit moves the domain to `pending`.",
            "example": "links.example.com"
          }
        }
      },
      "UpdateSubscriberRequest": {
        "type": "object",
        "description": "A change to a subscriber's attributes (JSON merge patch): a key sets or\n(with `null`) removes that attribute; keys left out are kept.",
        "required": [
          "attributes"
        ],
        "properties": {
          "attributes": {
            "type": "object",
            "description": "Attributes to set or remove (`null`)."
          }
        }
      },
      "UpdateTemplateRequest": {
        "type": "object",
        "description": "The sources of a template's new version. The name and the kind stay.",
        "required": [
          "html_body"
        ],
        "properties": {
          "html_body": {
            "type": "string",
            "description": "The HTML body, as at creation."
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "The subject, as at creation."
          },
          "text_body": {
            "type": [
              "string",
              "null"
            ],
            "description": "The text body; `null` or left out: made from the HTML."
          }
        }
      },
      "UpdatedSendingDomain": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SendingDomain"
          },
          {
            "type": "object",
            "required": [
              "required_records"
            ],
            "properties": {
              "required_records": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "$ref": "#/components/schemas/RequiredRecord"
                },
                "description": "The records to create after an `ip_pool` or `tracking_host` change;\n`null` when the change left the domain verified or failed."
              }
            }
          }
        ],
        "description": "A sending domain after a change, and, when the change moved it back to\n`pending`, the records it now needs."
      },
      "WhoamiResponse": {
        "type": "object",
        "description": "The key the request was made with.",
        "required": [
          "key_id",
          "name",
          "kind",
          "project_id",
          "scopes",
          "expires_at"
        ],
        "properties": {
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key stops working; `null`: never."
          },
          "key_id": {
            "type": "string",
            "format": "uuid",
            "description": "The key's id: what revoking it takes.",
            "example": "01900000-0000-7000-8000-000000000001"
          },
          "kind": {
            "$ref": "#/components/schemas/ApiKeyKind"
          },
          "name": {
            "type": "string",
            "description": "The name its owner gave it.",
            "example": "ops"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The key's project; `null` for an admin key."
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "description": "What the key may do. An admin key has every scope, and may manage\nprojects and admin keys besides."
          }
        }
      }
    },
    "responses": {
      "RateLimited": {
        "description": "The key is over its rate limit, `api.rate_limit_per_minute` requests a minute on this node (`rate_limited`)",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Seconds until the key's next request passes; rounded up."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "StorageTimeout": {
        "description": "Postwind's storage did not answer within its time limit (`storage_timeout`). A change the request makes may still take effect: check before repeating it. A repeat with the same `Idempotency-Key` never runs it twice: it is `409 idempotency_key_in_progress` until the key's 24 h end",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "A key of the client's choosing, one per request it means to send once (a UUID will do). A repeat with the same key and the same body within 24 h creates nothing again and gets the first answer, marked `Idempotent-Replayed: true`. The key belongs to the API key that sent it. Only a `2xx` answer is kept: after any other answer the key is free again.",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 128,
          "minLength": 1,
          "pattern": "^[!-~]{1,128}$"
        }
      }
    },
    "securitySchemes": {
      "api_key": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "pw_<adm|prj>_<prefix>_<secret>",
        "description": "A Postwind API key, shown once when it is created (`postwind keys create-admin` for the first admin key)."
      }
    }
  },
  "tags": [
    {
      "name": "server",
      "description": "The node and the calling key"
    },
    {
      "name": "projects",
      "description": "Projects, the units of isolation"
    },
    {
      "name": "keys",
      "description": "API keys: a project's keys, and admin keys"
    },
    {
      "name": "lists",
      "description": "Mailing lists of a project"
    },
    {
      "name": "subscribers",
      "description": "Subscribers of a project's lists"
    },
    {
      "name": "suppressions",
      "description": "The project's suppression list: addresses that get no mail"
    },
    {
      "name": "domains",
      "description": "Sending domains, their DKIM keys and DNS records"
    },
    {
      "name": "templates",
      "description": "Message templates and their versions"
    },
    {
      "name": "campaigns",
      "description": "Campaigns: a template sent to lists of the project"
    }
  ]
}
