{
  "components": {
    "schemas": {
      "APIKeyList": {
        "properties": {
          "data": {
            "description": "Every key on the project, revoked ones included.",
            "items": {
              "$ref": "#/components/schemas/APIKeyResponse"
            },
            "title": "Data",
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "APIKeyList",
        "type": "object"
      },
      "APIKeyResponse": {
        "description": "A key as the API describes it - prefix only, never the secret.",
        "properties": {
          "created_at": {
            "description": "UTC.",
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "description": "Opaque and stable, prefixed `key_`. Not the key itself.",
            "examples": [
              "key_01J8XQ2K3M4N5P6Q7R8S9T0V1W"
            ],
            "title": "Id",
            "type": "string"
          },
          "key_prefix": {
            "description": "The first characters of the key, for recognising it in a list.",
            "examples": [
              "sk_3nK9vQ2m"
            ],
            "title": "Key Prefix",
            "type": "string"
          },
          "last_used_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Accurate to the minute; not updated on every request.",
            "title": "Last Used At"
          },
          "name": {
            "description": "Whatever you called it. Only ever for your own recognition.",
            "examples": [
              "production"
            ],
            "title": "Name",
            "type": "string"
          },
          "revoked_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null while the key is active. Revocation is permanent.",
            "title": "Revoked At"
          }
        },
        "required": [
          "id",
          "name",
          "key_prefix",
          "created_at"
        ],
        "title": "APIKeyResponse",
        "type": "object"
      },
      "AttachmentRequest": {
        "description": "One attachment, base64 encoded.\n\nJSON has no byte type, so content arrives encoded. The size limit is applied\nto the *assembled message* rather than to this field, because base64 inflates\ncontent by about a third and it is the assembled size SES rejects.",
        "properties": {
          "content": {
            "description": "Base64-encoded file content.",
            "title": "Content",
            "type": "string"
          },
          "content_type": {
            "default": "application/octet-stream",
            "description": "MIME type. Defaults to `application/octet-stream`, which most clients offer as a download rather than displaying.",
            "examples": [
              "text/csv"
            ],
            "title": "Content Type",
            "type": "string"
          },
          "filename": {
            "description": "The name the recipient sees. Max 255 characters.",
            "examples": [
              "report.csv"
            ],
            "maxLength": 255,
            "title": "Filename",
            "type": "string"
          }
        },
        "required": [
          "filename",
          "content"
        ],
        "title": "AttachmentRequest",
        "type": "object"
      },
      "DependencyStatus": {
        "properties": {
          "database": {
            "description": "Whether Postgres answered.",
            "title": "Database",
            "type": "boolean"
          },
          "redis": {
            "description": "Whether Redis answered.",
            "title": "Redis",
            "type": "boolean"
          }
        },
        "required": [
          "database",
          "redis"
        ],
        "title": "DependencyStatus",
        "type": "object"
      },
      "DnsRecordResponse": {
        "description": "One record the customer has to publish.\n\n``from_attributes`` because the source is a frozen dataclass, not a dict -\npydantic will not read a nested dataclass by attribute without it.",
        "properties": {
          "name": {
            "description": "The fully-qualified record name. Some DNS providers want only the part before your domain, so check whether yours appends it for you.",
            "examples": [
              "abc123._domainkey.example.com"
            ],
            "title": "Name",
            "type": "string"
          },
          "record_type": {
            "description": "`CNAME` \u2014 the three records SES Easy DKIM needs are all CNAMEs.",
            "examples": [
              "CNAME"
            ],
            "title": "Record Type",
            "type": "string"
          },
          "value": {
            "description": "What the record should point at.",
            "examples": [
              "abc123.dkim.amazonses.com"
            ],
            "title": "Value",
            "type": "string"
          }
        },
        "required": [
          "record_type",
          "name",
          "value"
        ],
        "title": "DnsRecordResponse",
        "type": "object"
      },
      "DomainList": {
        "properties": {
          "data": {
            "description": "Every domain on the project.",
            "items": {
              "$ref": "#/components/schemas/DomainResponse"
            },
            "title": "Data",
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "DomainList",
        "type": "object"
      },
      "DomainResponse": {
        "description": "A sending domain and where its verification has got to.",
        "properties": {
          "created_at": {
            "description": "UTC.",
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "dkim_status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Same vocabulary. Mail sends without DKIM, but signing it is what stops receivers treating it as unauthenticated.",
            "examples": [
              "success"
            ],
            "title": "Dkim Status"
          },
          "dns_records": {
            "description": "The records to publish, built from the stored DKIM tokens so you can render setup instructions without a second request.",
            "items": {
              "$ref": "#/components/schemas/DnsRecordResponse"
            },
            "title": "Dns Records",
            "type": "array"
          },
          "id": {
            "description": "Opaque and stable, prefixed `dom_`.",
            "examples": [
              "dom_01J8XQ2K3M4N5P6Q7R8S9T0V1W"
            ],
            "title": "Id",
            "type": "string"
          },
          "last_checked_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When SESKit last asked SES about this domain. Null until first checked. UTC.",
            "title": "Last Checked At"
          },
          "mail_from_status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Same vocabulary, for a custom MAIL FROM domain. Optional.",
            "examples": [
              "not_started"
            ],
            "title": "Mail From Status"
          },
          "region": {
            "description": "The AWS region the identity lives in. SES identities are per-region, so a domain verified in one region is not verified in another.",
            "examples": [
              "us-east-1"
            ],
            "title": "Region",
            "type": "string"
          },
          "value": {
            "description": "The domain itself.",
            "examples": [
              "example.com"
            ],
            "title": "Value",
            "type": "string"
          },
          "verification_status": {
            "description": "`pending`, `success`, `failed`, `temporary_failure` or `not_started`. You can only send from the domain once this is `success`.",
            "examples": [
              "success"
            ],
            "title": "Verification Status",
            "type": "string"
          }
        },
        "required": [
          "id",
          "value",
          "region",
          "verification_status",
          "created_at"
        ],
        "title": "DomainResponse",
        "type": "object"
      },
      "EmailList": {
        "description": "One page of a project's messages, newest first.",
        "properties": {
          "data": {
            "description": "The page, ordered by id descending - newest first.",
            "items": {
              "$ref": "#/components/schemas/EmailResponse"
            },
            "title": "Data",
            "type": "array"
          },
          "has_more": {
            "description": "Whether another page exists. Returned so a caller can stop without a final request that comes back empty. Pass the last id in `data` as `starting_after` to fetch it.",
            "examples": [
              false
            ],
            "title": "Has More",
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "has_more"
        ],
        "title": "EmailList",
        "type": "object"
      },
      "EmailResponse": {
        "description": "A stored message, as a customer sees it.\n\nBcc is deliberately absent. It is recorded - support gets asked - but a\nblind copy readable from the API is not blind.",
        "properties": {
          "cc": {
            "description": "Empty when the message had none. Bcc is never returned.",
            "items": {
              "type": "string"
            },
            "title": "Cc",
            "type": "array"
          },
          "created_at": {
            "description": "When SESKit accepted the message. UTC.",
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "delivered_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the receiving server accepted it. Null unless a delivery event has arrived, which needs event reporting to be set up. UTC.",
            "title": "Delivered At"
          },
          "from": {
            "description": "As sent, display name included.",
            "examples": [
              "hello@example.com"
            ],
            "title": "From",
            "type": "string"
          },
          "headers": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "The custom headers the message was sent with, as they were stored. Empty when none were set. Headers SESKit sets itself - `From`, `Message-ID`, `List-Unsubscribe` - are not included: they are built at send time and are not the caller's to read back here.",
            "examples": [
              {
                "X-Entity-Ref-Id": "order-1234"
              }
            ],
            "title": "Headers",
            "type": "object"
          },
          "html": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null when the message was text only.",
            "title": "Html"
          },
          "id": {
            "description": "Opaque and stable, prefixed `email_`.",
            "examples": [
              "email_01J8XQ2K3M4N5P6Q7R8S9T0V1W"
            ],
            "title": "Id",
            "type": "string"
          },
          "last_error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the last attempt failed, normalised. Null unless `status` is `failed`.",
            "title": "Last Error"
          },
          "provider_message_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The id SES gave the message. Null until it is accepted. This is the id to quote to AWS support, and the one delivery events carry.",
            "title": "Provider Message Id"
          },
          "reply_to": {
            "description": "Empty when the message did not set one.",
            "items": {
              "type": "string"
            },
            "title": "Reply To",
            "type": "array"
          },
          "sent_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When SES accepted it. Null while queued. UTC.",
            "title": "Sent At"
          },
          "status": {
            "description": "`queued`, `sending`, `sent` or `failed`. Note that `sent` is the end of this vocabulary: it means a provider accepted the message, which is the last thing SESKit can observe by itself. Whether it *arrived* is a delivery event, so read `delivered_at` rather than expecting a `delivered` status.",
            "examples": [
              "sent"
            ],
            "title": "Status",
            "type": "string"
          },
          "subject": {
            "description": "As sent.",
            "title": "Subject",
            "type": "string"
          },
          "text": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null when the message was HTML only.",
            "title": "Text"
          },
          "to": {
            "description": "Always a list here, even if one address was sent.",
            "items": {
              "type": "string"
            },
            "title": "To",
            "type": "array"
          }
        },
        "required": [
          "id",
          "status",
          "from",
          "to",
          "cc",
          "reply_to",
          "subject",
          "created_at"
        ],
        "title": "EmailResponse",
        "type": "object"
      },
      "EmailStatus": {
        "description": "Where a message has got to inside SESKit.\n\nDeliberately not the same vocabulary as delivery: `sent` means a provider\naccepted it, which is the last thing SESKit can observe on its own.\nWhether it *arrived* is a delivery event, and that is Phase 7 - which is why\n`delivered_at` is a column here and not a status.",
        "enum": [
          "queued",
          "sending",
          "sent",
          "failed"
        ],
        "title": "EmailStatus",
        "type": "string"
      },
      "ErrorBody": {
        "properties": {
          "message": {
            "description": "Human-readable explanation. Not intended for pattern matching.",
            "examples": [
              "Invalid or missing API key."
            ],
            "title": "Message",
            "type": "string"
          },
          "type": {
            "description": "Machine-readable error type.",
            "examples": [
              "authentication_failed"
            ],
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "message"
        ],
        "title": "ErrorBody",
        "type": "object"
      },
      "ErrorResponse": {
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorBody",
            "description": "Every error from this API has this shape, whatever the status code."
          }
        },
        "required": [
          "error"
        ],
        "title": "ErrorResponse",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "HealthResponse": {
        "properties": {
          "status": {
            "const": "ok",
            "description": "Always `ok`. This endpoint reports that the process is running and deliberately checks nothing else, so it never reports anything but this.",
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "HealthResponse",
        "type": "object"
      },
      "ReadinessResponse": {
        "properties": {
          "dependencies": {
            "$ref": "#/components/schemas/DependencyStatus",
            "description": "Which dependency is at fault, so a failing probe does not need a log dive."
          },
          "status": {
            "description": "`not_ready` is returned with a 503, so status code alone is enough to act on.",
            "enum": [
              "ready",
              "not_ready"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status",
          "dependencies"
        ],
        "title": "ReadinessResponse",
        "type": "object"
      },
      "SendEmailRequest": {
        "description": "\u00a711's request body.",
        "properties": {
          "attachments": {
            "description": "The size limit applies to the assembled message, not to each file: base64 inflates content by about a third, and it is the assembled size SES rejects. See `EMAIL_MAX_MESSAGE_BYTES` (10 MiB by default).",
            "items": {
              "$ref": "#/components/schemas/AttachmentRequest"
            },
            "title": "Attachments",
            "type": "array"
          },
          "bcc": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "string"
                  }
                ],
                "examples": [
                  [
                    "user@example.com"
                  ]
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Hidden from every recipient. Recorded, but never returned by the API \u2014 a blind copy readable from a `GET` is not blind.",
            "title": "Bcc"
          },
          "cc": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "string"
                  }
                ],
                "examples": [
                  [
                    "user@example.com"
                  ]
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Visible to every recipient.",
            "title": "Cc"
          },
          "from": {
            "description": "The sender, optionally with a display name. The address or its domain must be verified in SES \u2014 an unverified sender is refused by SES, not by SESKit, so it fails at send time rather than here.",
            "examples": [
              "Acme <hello@example.com>"
            ],
            "maxLength": 320,
            "title": "From",
            "type": "string"
          },
          "headers": {
            "description": "Custom headers to add to the message. Names are RFC 5322 field-names: printable ASCII with no colon, spaces or line breaks.",
            "examples": [
              {
                "X-Entity-Ref-Id": "order-1234"
              }
            ],
            "patternProperties": {
              "^[!-9;-~]+$": {
                "type": "string"
              }
            },
            "propertyNames": {
              "maxLength": 76
            },
            "title": "Headers",
            "type": "object"
          },
          "html": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "HTML body. Provide `html`, `text`, or both; a message with neither is refused.",
            "examples": [
              "<h1>Welcome!</h1>"
            ],
            "title": "Html"
          },
          "reply_to": {
            "anyOf": [
              {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "string"
                  }
                ],
                "examples": [
                  [
                    "user@example.com"
                  ]
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Where replies go, if not to `from`. Needs no SES verification.",
            "title": "Reply To"
          },
          "subject": {
            "description": "Max 998 characters, which is the RFC 5322 line limit.",
            "examples": [
              "Welcome to Acme"
            ],
            "maxLength": 998,
            "title": "Subject",
            "type": "string"
          },
          "text": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Plain-text body. Sending both makes a multipart message, which is what clients that will not render HTML fall back to.",
            "examples": [
              "Welcome to Acme"
            ],
            "title": "Text"
          },
          "to": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "string"
              }
            ],
            "description": "One recipient, or a list of them. While your account is in the SES sandbox every recipient must also be verified.",
            "examples": [
              [
                "user@example.com"
              ]
            ],
            "title": "To"
          }
        },
        "required": [
          "from",
          "to",
          "subject"
        ],
        "title": "SendEmailRequest",
        "type": "object"
      },
      "SendEmailResponse": {
        "description": "\u00a711's response: the id, and that it was accepted.\n\n``queued`` rather than ``sent`` because that is what has happened. The\nmessage is durable and will be attempted; claiming it had gone out would be\na promise this response cannot make.",
        "properties": {
          "id": {
            "description": "Use it to fetch the message later, and to match delivery events to it.",
            "examples": [
              "email_01J8XQ2K3M4N5P6Q7R8S9T0V1W"
            ],
            "title": "Id",
            "type": "string"
          },
          "status": {
            "description": "Always `queued` here. The message is durable and will be attempted; whether it was accepted by SES is reported later, through events.",
            "examples": [
              "queued"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "id",
          "status"
        ],
        "title": "SendEmailResponse",
        "type": "object"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      },
      "WebhookDeliveryList": {
        "properties": {
          "data": {
            "description": "Most recent first.",
            "items": {
              "$ref": "#/components/schemas/WebhookDeliveryResponse"
            },
            "title": "Data",
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "WebhookDeliveryList",
        "type": "object"
      },
      "WebhookDeliveryResponse": {
        "description": "One attempt to deliver one event to one endpoint.",
        "properties": {
          "attempt_count": {
            "description": "Attempts made so far, including the first.",
            "examples": [
              1
            ],
            "title": "Attempt Count",
            "type": "integer"
          },
          "created_at": {
            "description": "UTC.",
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The transport failure, normalised. Never a raw exception, which can carry an address or a URL.",
            "examples": [
              null
            ],
            "title": "Error"
          },
          "event_id": {
            "description": "The event being delivered. The same event to two endpoints is two deliveries.",
            "examples": [
              "evt_01J8XQ2K3M4N5P6Q7R8S9T0V1W"
            ],
            "title": "Event Id",
            "type": "string"
          },
          "id": {
            "description": "Opaque and stable, prefixed `whd_`.",
            "examples": [
              "whd_01J8XQ2K3M4N5P6Q7R8S9T0V1W"
            ],
            "title": "Id",
            "type": "string"
          },
          "last_attempt_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null until the first attempt. UTC.",
            "title": "Last Attempt At"
          },
          "next_attempt_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the next retry is due. Null once the delivery has settled. UTC.",
            "title": "Next Attempt At"
          },
          "response_status": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the endpoint answered. Null when the request never got that far \u2014 a timeout, a refused connection, a destination that failed validation.",
            "examples": [
              200
            ],
            "title": "Response Status"
          },
          "status": {
            "description": "`pending`, `delivered` or `failed`. `failed` means the retries are exhausted.",
            "examples": [
              "delivered"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "id",
          "event_id",
          "status",
          "attempt_count",
          "created_at"
        ],
        "title": "WebhookDeliveryResponse",
        "type": "object"
      },
      "WebhookEndpointList": {
        "properties": {
          "data": {
            "description": "Every endpoint on the project, disabled ones included.",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpointResponse"
            },
            "title": "Data",
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "WebhookEndpointList",
        "type": "object"
      },
      "WebhookEndpointResponse": {
        "description": "A registered destination and whether SESKit is still sending to it.",
        "properties": {
          "consecutive_failures": {
            "default": 0,
            "description": "Deliveries that gave up after exhausting their retries, counted since the last success \u2014 one success clears it. Exposed so an application can watch this climbing before SESKit switches the endpoint off.",
            "examples": [
              0
            ],
            "title": "Consecutive Failures",
            "type": "integer"
          },
          "created_at": {
            "description": "UTC.",
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "description": "Opaque and stable, prefixed `wh_`.",
            "examples": [
              "wh_01J8XQ2K3M4N5P6Q7R8S9T0V1W"
            ],
            "title": "Id",
            "type": "string"
          },
          "status": {
            "description": "`active`, `disabled_by_user` or `disabled_after_failures`. Three values rather than a boolean, because *SESKit gave up on it* and *you turned it off* are different facts and an integration should be able to tell them apart without asking a human.",
            "examples": [
              "active"
            ],
            "title": "Status",
            "type": "string"
          },
          "url": {
            "description": "Where SESKit POSTs events. Must be https, and must not resolve to a loopback, private or link-local address \u2014 delivery responses are shown in the dashboard, so an internal URL would turn a webhook into a read primitive against your own network. Local development relaxes both rules so you can point one at your own machine.",
            "examples": [
              "https://example.com/webhooks/email"
            ],
            "title": "Url",
            "type": "string"
          }
        },
        "required": [
          "id",
          "url",
          "status",
          "created_at"
        ],
        "title": "WebhookEndpointResponse",
        "type": "object"
      }
    }
  },
  "info": {
    "description": "A Python-native developer email platform built on Amazon SES.",
    "title": "SESKit",
    "version": "0.1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/healthz": {
      "get": {
        "description": "Return 200 whenever the process is running. Checks no dependencies.",
        "operationId": "healthz_healthz_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Liveness probe",
        "tags": [
          "health"
        ]
      }
    },
    "/readyz": {
      "get": {
        "description": "Round-trip both Postgres and Redis.\n\nReturns 503 if either is unreachable, so a partially-started stack is not\nadvertised as ready.",
        "operationId": "readyz_readyz_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadinessResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadinessResponse"
                }
              }
            },
            "description": "A dependency is unavailable"
          }
        },
        "summary": "Readiness probe",
        "tags": [
          "health"
        ]
      }
    },
    "/v1/api-keys": {
      "get": {
        "description": "Every key belonging to the calling key's project.\n\nScoped by the authenticated project rather than by anything in the request,\nso there is no parameter a caller could change to see someone else's keys.",
        "operationId": "list_keys_v1_api_keys_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIKeyList"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid or missing API key."
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded."
          }
        },
        "summary": "List API keys",
        "tags": [
          "api-keys"
        ]
      }
    },
    "/v1/domains": {
      "get": {
        "description": "Every domain this key's project can send from.",
        "operationId": "list_domains_v1_domains_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainList"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid or missing API key."
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded."
          }
        },
        "summary": "List sending domains",
        "tags": [
          "domains"
        ]
      }
    },
    "/v1/emails": {
      "get": {
        "description": "This key's project, newest first.\n\n**Paged by cursor rather than by offset.** Ids sort in the order they were\ncreated, so ``starting_after`` names a fixed point in the list and stays\ncorrect while new messages arrive underneath it. An offset does not: a send\nbetween two pages shifts every row down one, and the reader silently skips\nthe message that moved across the boundary. For a send log that is data\nloss nobody can see.\n\nAn unknown ``starting_after`` is a 404 rather than an empty page. The\ncomparison is lexical, so an id from another project would otherwise return\na page of real messages positioned by an id the caller cannot see - a wrong\nanswer that looks like a right one.",
        "operationId": "list_emails_v1_emails_get",
        "parameters": [
          {
            "description": "How many messages to return, newest first.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 25,
              "description": "How many messages to return, newest first.",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Return messages older than this id - the last id from the previous page. The id must belong to this project.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Return messages older than this id - the last id from the previous page. The id must belong to this project.",
              "title": "Starting After"
            }
          },
          {
            "description": "Only messages in this status.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/EmailStatus"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only messages in this status.",
              "title": "Status"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailList"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid or missing API key."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded."
          }
        },
        "summary": "List emails",
        "tags": [
          "emails"
        ]
      },
      "post": {
        "description": "Accept a message for sending.",
        "operationId": "send_email_v1_emails_post",
        "parameters": [
          {
            "description": "Repeat a request safely. A second send with the same key returns the first message's id and sends nothing further, so a retry after a timeout cannot deliver twice. Scoped to the project.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Repeat a request safely. A second send with the same key returns the first message's id and sends nothing further, so a retry after a timeout cannot deliver twice. Scoped to the project.",
              "title": "Idempotency-Key"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmailRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendEmailResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid or missing API key."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded."
          }
        },
        "summary": "Send an email",
        "tags": [
          "emails"
        ]
      }
    },
    "/v1/emails/{email_id}": {
      "get": {
        "description": "One message, if it belongs to this key's project.\n\nOwnership is part of the query, so an id from another project is a 404\nrather than a 403 - which would confirm the id exists.",
        "operationId": "get_email_v1_emails__email_id__get",
        "parameters": [
          {
            "description": "The id returned when the message was accepted.",
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "description": "The id returned when the message was accepted.",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid or missing API key."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded."
          }
        },
        "summary": "Retrieve an email",
        "tags": [
          "emails"
        ]
      }
    },
    "/v1/webhooks": {
      "get": {
        "description": "Every webhook endpoint registered on this key's project.\n\nThe signing secret is not included - see the note in\n``schemas/webhooks.py``. It is available on the dashboard, where it is read\nonce by a person rather than returned to code on every call.",
        "operationId": "list_webhook_endpoints_v1_webhooks_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid or missing API key."
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded."
          }
        },
        "summary": "List webhook endpoints",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/v1/webhooks/{endpoint_id}/deliveries": {
      "get": {
        "description": "Recent delivery attempts for one endpoint, newest first.\n\nThe delivery row is the queue as well as the log, so what comes back is the\nactual state of each attempt - including one still pending a retry, with the\ntime it is next due.",
        "operationId": "list_webhook_deliveries_v1_webhooks__endpoint_id__deliveries_get",
        "parameters": [
          {
            "description": "The endpoint whose deliveries to list.",
            "in": "path",
            "name": "endpoint_id",
            "required": true,
            "schema": {
              "description": "The endpoint whose deliveries to list.",
              "title": "Endpoint Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid or missing API key."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Rate limit exceeded."
          }
        },
        "summary": "List recent webhook deliveries",
        "tags": [
          "webhooks"
        ]
      }
    }
  }
}
