{
  "components": {
    "schemas": {
      "ProtectedResourceMetadata": {
        "properties": {
          "authorization_servers": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "bearer_methods_supported": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "resource": {
            "type": "string"
          },
          "resource_name": {
            "type": "string"
          }
        },
        "required": [
          "authorization_servers",
          "bearer_methods_supported",
          "resource"
        ],
        "type": "object"
      },
      "FoundingOffer": {
        "properties": {
          "available": {
            "type": "boolean"
          },
          "deadline": {
            "format": "date-time",
            "type": "string"
          },
          "months": {
            "format": "int32",
            "type": "integer"
          },
          "percentOff": {
            "format": "int32",
            "type": "integer"
          },
          "seats": {
            "format": "int32",
            "type": "integer"
          },
          "seatsLeft": {
            "format": "int32",
            "type": "integer"
          }
        },
        "required": [
          "available",
          "deadline",
          "months",
          "percentOff",
          "seats",
          "seatsLeft"
        ],
        "type": "object"
      },
      "Health": {
        "properties": {
          "status": {
            "enum": [
              "ok"
            ],
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "type": "object"
      },
      "PipelineHealth": {
        "properties": {
          "canary": {
            "enum": [
              "ok",
              "failing",
              "not_configured"
            ],
            "type": "string"
          },
          "mta": {
            "enum": [
              "ok",
              "failing"
            ],
            "type": "string"
          },
          "queue": {
            "enum": [
              "ok",
              "failing"
            ],
            "type": "string"
          }
        },
        "required": [
          "canary",
          "mta",
          "queue"
        ],
        "type": "object"
      },
      "Error": {
        "properties": {
          "code": {
            "description": "Stable; clients translate by code",
            "example": "domain_not_verified",
            "type": "string"
          },
          "message": {
            "description": "English, for logs and API users",
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "WebhookIps": {
        "properties": {
          "ipv4": {
            "example": [
              "88.99.60.54"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "ipv6": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "updatedAt": {
            "format": "date",
            "type": "string"
          }
        },
        "required": [
          "ipv4",
          "ipv6",
          "updatedAt"
        ],
        "type": "object"
      },
      "Domain": {
        "properties": {
          "failedChecks": {
            "format": "int32",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "lastCheckedAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "records": {
            "items": {
              "$ref": "#/components/schemas/DnsRecord"
            },
            "type": "array"
          },
          "status": {
            "enum": [
              "pending",
              "verified",
              "failed"
            ],
            "type": "string"
          },
          "stopsSendingAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "failedChecks",
          "id",
          "name",
          "records",
          "status"
        ],
        "type": "object"
      },
      "CreateDomainRequest": {
        "properties": {
          "name": {
            "example": "mail.example.com",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "EmailPage": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Email"
            },
            "type": "array"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "SendEmailRequest": {
        "description": "At least one of html or text. At most 50 recipients across to, cc and bcc; at most 15 MB as sent, the MIME message letr.dev builds, which leaves room for about 10 MB of attachments (message_too_large). Test recipients (see x-test-recipients) never leave letr.dev.",
        "properties": {
          "attachments": {
            "items": {
              "$ref": "#/components/schemas/Attachment"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "bcc": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "cc": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "from": {
            "description": "An address, optionally with a display name",
            "example": "Ada Lovelace <ada@example.com>",
            "type": [
              "string",
              "null"
            ]
          },
          "headers": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Extra headers, e.g. In-Reply-To, References, List-Unsubscribe, X-Entity-Ref-ID. Names must be RFC 5322 field names. letr.dev owns From, Sender, To, Cc, Bcc, Reply-To, Subject, Date, Message-ID, Return-Path, MIME-Version, Content-*, DKIM-Signature, Received, Feedback-ID and any X-Letr-* header; those are refused with 422.",
            "type": [
              "object",
              "null"
            ]
          },
          "html": {
            "type": [
              "string",
              "null"
            ]
          },
          "replyTo": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "scheduledAt": {
            "description": "Send later: at least 1 minute and at most 7 days ahead (422 invalid_scheduled_at). The email is created with status scheduled, counts toward usage when it is sent, and can be cancelled until then. Not allowed for test recipients only.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "subject": {
            "maxLength": 998,
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "additionalProperties": {
              "type": "string"
            },
            "type": [
              "object",
              "null"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "to": {
            "description": "One address or an array of them",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "required": [
          "from",
          "subject",
          "to"
        ],
        "type": "object"
      },
      "SendEmailResponse": {
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "description": "scheduled: waits for scheduledAt; canceled: cancelled before it was sent, or after it was partly sent when an address is erased while other recipients still wait. The status never moves back to an earlier stage (scheduled or queued, sent, deferred, then delivered, bounced or failed, then complained, then canceled) but can skip one. delivered, bounced and failed can still change into one another or into complained: for one recipient through a late bounce report, for several recipients through the event letr.dev processed last.",
            "enum": [
              "scheduled",
              "canceled",
              "queued",
              "sent",
              "delivered",
              "deferred",
              "bounced",
              "complained",
              "failed"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "status"
        ],
        "type": "object"
      },
      "SendBatchResponse": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/SendEmailResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "type": "object"
      },
      "Email": {
        "properties": {
          "createdAt": {
            "format": "date-time",
            "type": "string"
          },
          "events": {
            "items": {
              "$ref": "#/components/schemas/EmailEvent"
            },
            "type": "array"
          },
          "from": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "operatorViews": {
            "items": {
              "$ref": "#/components/schemas/OperatorView"
            },
            "type": "array"
          },
          "scheduledAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "scheduled: waits for scheduledAt; canceled: cancelled before it was sent, or after it was partly sent when an address is erased while other recipients still wait. The status never moves back to an earlier stage (scheduled or queued, sent, deferred, then delivered, bounced or failed, then complained, then canceled) but can skip one. delivered, bounced and failed can still change into one another or into complained: for one recipient through a late bounce report, for several recipients through the event letr.dev processed last.",
            "enum": [
              "scheduled",
              "canceled",
              "queued",
              "sent",
              "delivered",
              "deferred",
              "bounced",
              "complained",
              "failed"
            ],
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "tags": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object"
          },
          "to": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "createdAt",
          "events",
          "from",
          "id",
          "operatorViews",
          "status",
          "subject",
          "tags",
          "to"
        ],
        "type": "object"
      },
      "EmailContent": {
        "properties": {
          "attachments": {
            "items": {
              "$ref": "#/components/schemas/EmailAttachment"
            },
            "type": "array"
          },
          "headers": {
            "items": {
              "$ref": "#/components/schemas/EmailHeader"
            },
            "type": "array"
          },
          "html": {
            "type": [
              "string",
              "null"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "attachments",
          "headers",
          "html",
          "text"
        ],
        "type": "object"
      },
      "Metrics": {
        "properties": {
          "daily": {
            "items": {
              "$ref": "#/components/schemas/DailyOutcomes"
            },
            "type": "array"
          },
          "delivery": {
            "$ref": "#/components/schemas/DeliveryTotals"
          },
          "domains": {
            "items": {
              "$ref": "#/components/schemas/DomainOutcomes"
            },
            "type": "array"
          },
          "from": {
            "format": "date-time",
            "type": "string"
          },
          "minimumSample": {
            "format": "int32",
            "type": "integer"
          },
          "range": {
            "enum": [
              "7d",
              "30d"
            ],
            "type": "string"
          },
          "reputation": {
            "$ref": "#/components/schemas/Reputation"
          },
          "to": {
            "format": "date-time",
            "type": "string"
          },
          "webhooks": {
            "items": {
              "$ref": "#/components/schemas/WebhookHealth"
            },
            "type": "array"
          }
        },
        "required": [
          "daily",
          "delivery",
          "domains",
          "from",
          "minimumSample",
          "range",
          "reputation",
          "to",
          "webhooks"
        ],
        "type": "object"
      },
      "RecipientErasure": {
        "properties": {
          "emails": {
            "description": "Emails the address was erased from",
            "format": "int32",
            "type": "integer"
          },
          "suppressionRemoved": {
            "type": "boolean"
          }
        },
        "required": [
          "emails",
          "suppressionRemoved"
        ],
        "type": "object"
      },
      "SuppressionPage": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Suppression"
            },
            "type": "array"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "AddSuppressionRequest": {
        "properties": {
          "email": {
            "format": "email",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "email"
        ],
        "type": "object"
      },
      "Suppression": {
        "properties": {
          "createdAt": {
            "format": "date-time",
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "reason": {
            "enum": [
              "bounced",
              "complained",
              "manual"
            ],
            "type": "string"
          }
        },
        "required": [
          "createdAt",
          "email",
          "reason"
        ],
        "type": "object"
      },
      "SuppressionImport": {
        "properties": {
          "added": {
            "format": "int32",
            "type": "integer"
          },
          "alreadySuppressed": {
            "format": "int32",
            "type": "integer"
          },
          "invalidCount": {
            "format": "int32",
            "type": "integer"
          },
          "invalidLines": {
            "description": "Line numbers (1-based) that held no valid address, the first 100",
            "items": {
              "format": "int32",
              "type": "integer"
            },
            "maxItems": 100,
            "type": "array"
          }
        },
        "required": [
          "added",
          "alreadySuppressed",
          "invalidCount",
          "invalidLines"
        ],
        "type": "object"
      },
      "Usage": {
        "properties": {
          "ceiling": {
            "description": "One of ceilingChoices; 10000 means no extra cost ever. A ceiling letr.dev grants becomes the account's ceiling at once and is listed in ceilingChoices; the customer can switch back to a lower choice",
            "format": "int32",
            "type": "integer"
          },
          "ceilingChoices": {
            "description": "The ceilings the account may choose, ascending: 10000, 25000, and the ceiling letr.dev granted it, if any, which became its ceiling when granted",
            "items": {
              "format": "int32",
              "type": "integer"
            },
            "type": "array"
          },
          "currency": {
            "type": "string"
          },
          "dailyLimit": {
            "description": "The daily limit of the account's trust step (daily_limit_reached past it); null on step 3, where only the monthly ceiling applies",
            "format": "int32",
            "type": [
              "integer",
              "null"
            ]
          },
          "included": {
            "format": "int32",
            "type": "integer"
          },
          "month": {
            "type": "string"
          },
          "nextStep": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/NextStep"
              },
              {
                "type": "null"
              }
            ]
          },
          "overageAmount": {
            "format": "int32",
            "type": "integer"
          },
          "overageBlocks": {
            "format": "int32",
            "type": "integer"
          },
          "overageRecipients": {
            "format": "int32",
            "type": "integer"
          },
          "sent": {
            "format": "int32",
            "type": "integer"
          },
          "sentToday": {
            "format": "int32",
            "type": "integer"
          },
          "step": {
            "description": "The account's trust step",
            "format": "int32",
            "maximum": 3,
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "ceiling",
          "ceilingChoices",
          "currency",
          "dailyLimit",
          "included",
          "month",
          "nextStep",
          "overageAmount",
          "overageBlocks",
          "overageRecipients",
          "sent",
          "sentToday",
          "step"
        ],
        "type": "object"
      },
      "Webhook": {
        "properties": {
          "clientCertificate": {
            "$ref": "#/components/schemas/ClientCertificate"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "events": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "failingSince": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "heldDeliveries": {
            "format": "int32",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "lastFailureAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "lastFailureReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastSuccessAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "paused": {
            "type": "boolean"
          },
          "pausedAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "previousSecretExpiresAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "serverCa": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ServerCa"
              },
              {
                "type": "null"
              }
            ]
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "clientCertificate",
          "createdAt",
          "enabled",
          "events",
          "heldDeliveries",
          "id",
          "paused",
          "serverCa",
          "url"
        ],
        "type": "object"
      },
      "WebhookUpdate": {
        "properties": {
          "enabled": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "events": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "paused": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "WebhookEvent": {
        "properties": {
          "createdAt": {
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/EmailEventData"
              },
              {
                "$ref": "#/components/schemas/AccountEventData"
              }
            ]
          },
          "id": {
            "type": "string"
          },
          "type": {
            "description": "webhook.test comes from the test action; its data.emailId and data.recipient are empty strings",
            "enum": [
              "email.sent",
              "email.delivered",
              "email.deferred",
              "email.bounced",
              "email.complained",
              "email.failed",
              "account.paused",
              "account.reactivated",
              "webhook.test"
            ],
            "type": "string"
          }
        },
        "required": [
          "createdAt",
          "data",
          "id",
          "type"
        ],
        "type": "object"
      },
      "DnsRecord": {
        "properties": {
          "name": {
            "type": "string"
          },
          "ok": {
            "type": "boolean"
          },
          "purpose": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "ok",
          "purpose",
          "type",
          "value"
        ],
        "type": "object"
      },
      "Attachment": {
        "properties": {
          "content": {
            "contentEncoding": "base64",
            "type": [
              "string",
              "null"
            ]
          },
          "contentId": {
            "description": "Makes the attachment inline; the html references it as cid:<contentId>.",
            "maxLength": 128,
            "type": [
              "string",
              "null"
            ]
          },
          "contentType": {
            "type": [
              "string",
              "null"
            ]
          },
          "filename": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "content",
          "filename"
        ],
        "type": "object"
      },
      "EmailEvent": {
        "properties": {
          "createdAt": {
            "format": "date-time",
            "type": "string"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "recipient": {
            "type": "string"
          },
          "type": {
            "enum": [
              "sent",
              "delivered",
              "deferred",
              "bounced",
              "complained",
              "failed"
            ],
            "type": "string"
          }
        },
        "required": [
          "createdAt",
          "recipient",
          "type"
        ],
        "type": "object"
      },
      "OperatorView": {
        "properties": {
          "at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "at"
        ],
        "type": "object"
      },
      "EmailAttachment": {
        "properties": {
          "contentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "contentType": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "index": {
            "format": "int32",
            "type": "integer"
          },
          "inline": {
            "type": "boolean"
          },
          "size": {
            "format": "int32",
            "type": "integer"
          }
        },
        "required": [
          "contentId",
          "contentType",
          "filename",
          "index",
          "inline",
          "size"
        ],
        "type": "object"
      },
      "EmailHeader": {
        "properties": {
          "name": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "value"
        ],
        "type": "object"
      },
      "DailyOutcomes": {
        "properties": {
          "bounced": {
            "format": "int32",
            "type": "integer"
          },
          "date": {
            "format": "date",
            "type": "string"
          },
          "deferredOnce": {
            "format": "int32",
            "type": "integer"
          },
          "delivered": {
            "format": "int32",
            "type": "integer"
          },
          "failed": {
            "format": "int32",
            "type": "integer"
          },
          "pending": {
            "format": "int32",
            "type": "integer"
          },
          "recipients": {
            "format": "int32",
            "type": "integer"
          }
        },
        "required": [
          "bounced",
          "date",
          "deferredOnce",
          "delivered",
          "failed",
          "pending",
          "recipients"
        ],
        "type": "object"
      },
      "DeliveryTotals": {
        "properties": {
          "bounced": {
            "format": "int32",
            "type": "integer"
          },
          "complained": {
            "format": "int32",
            "type": "integer"
          },
          "deferredOnce": {
            "format": "int32",
            "type": "integer"
          },
          "delivered": {
            "format": "int32",
            "type": "integer"
          },
          "failed": {
            "format": "int32",
            "type": "integer"
          },
          "latencyP50Seconds": {
            "format": "double",
            "type": [
              "number",
              "null"
            ]
          },
          "latencyP95Seconds": {
            "format": "double",
            "type": [
              "number",
              "null"
            ]
          },
          "pending": {
            "format": "int32",
            "type": "integer"
          },
          "recipients": {
            "format": "int32",
            "type": "integer"
          }
        },
        "required": [
          "bounced",
          "complained",
          "deferredOnce",
          "delivered",
          "failed",
          "latencyP50Seconds",
          "latencyP95Seconds",
          "pending",
          "recipients"
        ],
        "type": "object"
      },
      "DomainOutcomes": {
        "properties": {
          "bounced": {
            "format": "int32",
            "type": "integer"
          },
          "deferredOnce": {
            "format": "int32",
            "type": "integer"
          },
          "delivered": {
            "format": "int32",
            "type": "integer"
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "failed": {
            "format": "int32",
            "type": "integer"
          },
          "pending": {
            "format": "int32",
            "type": "integer"
          },
          "recipients": {
            "format": "int32",
            "type": "integer"
          }
        },
        "required": [
          "bounced",
          "deferredOnce",
          "delivered",
          "domain",
          "failed",
          "pending",
          "recipients"
        ],
        "type": "object"
      },
      "Reputation": {
        "properties": {
          "bounceRateThreshold": {
            "format": "double",
            "type": "number"
          },
          "bounced": {
            "format": "int64",
            "type": "integer"
          },
          "bouncesUntilPause": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "complained": {
            "format": "int64",
            "type": "integer"
          },
          "complaintMinimum": {
            "format": "int32",
            "type": "integer"
          },
          "complaintRateThreshold": {
            "format": "double",
            "type": "number"
          },
          "complaintsUntilPause": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "daily": {
            "items": {
              "$ref": "#/components/schemas/ReputationDay"
            },
            "type": "array"
          },
          "pauseReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "sent": {
            "format": "int64",
            "type": "integer"
          },
          "windowDays": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "bounceRateThreshold",
          "bounced",
          "bouncesUntilPause",
          "complained",
          "complaintMinimum",
          "complaintRateThreshold",
          "complaintsUntilPause",
          "daily",
          "pauseReason",
          "sent",
          "windowDays"
        ],
        "type": "object"
      },
      "WebhookHealth": {
        "properties": {
          "delivered": {
            "format": "int32",
            "type": "integer"
          },
          "enabled": {
            "type": "boolean"
          },
          "failed": {
            "format": "int32",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "lastFailureAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "lastFailureReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastSuccessAt": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "pending": {
            "format": "int32",
            "type": "integer"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "delivered",
          "enabled",
          "failed",
          "id",
          "pending",
          "url"
        ],
        "type": "object"
      },
      "NextStep": {
        "properties": {
          "dailyLimit": {
            "format": "int32",
            "type": [
              "integer",
              "null"
            ]
          },
          "daysLeft": {
            "format": "int64",
            "type": "integer"
          },
          "deliveredLeft": {
            "format": "int64",
            "type": "integer"
          },
          "ratesClean": {
            "type": "boolean"
          },
          "renewalNeeded": {
            "type": "boolean"
          },
          "reviewNeeded": {
            "type": "boolean"
          },
          "step": {
            "format": "int32",
            "type": "integer"
          }
        },
        "required": [
          "dailyLimit",
          "daysLeft",
          "deliveredLeft",
          "ratesClean",
          "renewalNeeded",
          "reviewNeeded",
          "step"
        ],
        "type": "object"
      },
      "ClientCertificate": {
        "properties": {
          "certificatePem": {
            "type": [
              "string",
              "null"
            ]
          },
          "csrPem": {
            "type": [
              "string",
              "null"
            ]
          },
          "issuer": {
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "type": "string"
          },
          "notAfter": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "sha256Fingerprint": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "mode"
        ],
        "type": "object"
      },
      "ServerCa": {
        "properties": {
          "notAfter": {
            "format": "date-time",
            "type": "string"
          },
          "subjects": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "notAfter",
          "subjects"
        ],
        "type": "object"
      },
      "EmailEventData": {
        "description": "email.* and webhook.test events",
        "properties": {
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "emailId": {
            "type": "string"
          },
          "recipient": {
            "type": "string"
          },
          "tags": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object"
          }
        },
        "required": [
          "emailId",
          "recipient",
          "tags"
        ],
        "type": "object"
      },
      "AccountEventData": {
        "description": "account.paused and account.reactivated",
        "properties": {
          "accountId": {
            "type": "string"
          },
          "detail": {
            "description": "For operator pauses the reason the operator gave",
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "description": "Why the account was paused; resumed for account.reactivated",
            "enum": [
              "bounce_rate",
              "complaint_rate",
              "payment_failed",
              "operator",
              "resumed"
            ],
            "type": "string"
          }
        },
        "required": [
          "accountId",
          "reason"
        ],
        "type": "object"
      },
      "ReputationDay": {
        "properties": {
          "bounced": {
            "format": "int32",
            "type": "integer"
          },
          "complained": {
            "format": "int32",
            "type": "integer"
          },
          "date": {
            "format": "date",
            "type": "string"
          },
          "sent": {
            "format": "int32",
            "type": "integer"
          }
        },
        "required": [
          "bounced",
          "complained",
          "date",
          "sent"
        ],
        "type": "object"
      }
    },
    "parameters": {},
    "responses": {
      "Unauthorized": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "Missing, unknown or revoked API key",
        "headers": {
          "WWW-Authenticate": {
            "description": "Bearer",
            "schema": {
              "type": "string"
            }
          },
          "X-Request-Id": {
            "description": "The id of this request; quote it when you contact support",
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "Forbidden": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "insufficient_permission: the key permission does not allow this operation",
        "headers": {
          "X-Request-Id": {
            "description": "The id of this request; quote it when you contact support",
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "RateLimited": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "rate_limited: too many requests for this account, or for this IP address (IPv6 /64) without a valid key; more than 5 times the account limit without a valid key blocks the address before its key is checked; retry after the Retry-After header",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the next request is accepted",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "description": "Requests a minute; also on every accepted answer",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "description": "Requests left in this minute; also on every accepted answer",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "description": "Seconds until the minute starts over; also on every accepted answer",
            "schema": {
              "type": "integer"
            }
          },
          "X-Request-Id": {
            "description": "The id of this request; quote it when you contact support",
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "NotFound": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "not_found: no such resource in this account; a resource of another account answers the same",
        "headers": {
          "X-Request-Id": {
            "description": "The id of this request; quote it when you contact support",
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "BadRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "invalid_request: an unreadable body, an Idempotency-Key over 255 characters, or a query parameter out of range",
        "headers": {
          "X-Request-Id": {
            "description": "The id of this request; quote it when you contact support",
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "ContentBusy": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "content_busy: too many content reads at once; retry after a moment",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before the next try",
            "schema": {
              "type": "integer"
            }
          },
          "X-Request-Id": {
            "description": "The id of this request; quote it when you contact support",
            "schema": {
              "type": "string"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "description": "letr_ API key",
        "scheme": "bearer",
        "type": "http"
      },
      "mcpOAuth": {
        "bearerFormat": "JWT",
        "description": "OAuth 2.1 access token from WorkOS AuthKit for the resource https://api.letr.dev/mcp. MCP clients discover the authorization server from GET /.well-known/oauth-protected-resource/mcp (PKCE S256, client ID metadata documents and dynamic client registration); the token acts with full_access for the signed-in user's account.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "title": "letr.dev API",
    "version": "v1",
    "description": "Send transactional email and follow its delivery. Authenticate with a letr.dev API key as a Bearer token; its permission decides which operations it may call."
  },
  "openapi": "3.1.0",
  "paths": {
    "/.well-known/oauth-protected-resource/mcp": {
      "get": {
        "operationId": "protectedResourceMetadata",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProtectedResourceMetadata"
                }
              }
            },
            "description": "Metadata"
          }
        },
        "summary": "RFC 9728 protected resource metadata for /mcp (also served at /.well-known/oauth-protected-resource)",
        "tags": [
          "MCP"
        ]
      }
    },
    "/mcp": {
      "post": {
        "description": "JSON-RPC over POST. Authorization is either a letr.dev API key (Bearer letr_...) governed by its permission, or an OAuth access token from WorkOS AuthKit with audience https://api.letr.dev/mcp, acting for the signed-in user with full_access on their account. A missing or invalid credential answers 401 with `WWW-Authenticate: Bearer resource_metadata=\"https://api.letr.dev/.well-known/oauth-protected-resource/mcp\"`. GET and DELETE answer 405. Origin is validated. A request other than initialize whose MCP-Protocol-Version the server does not speak answers 400.",
        "responses": {
          "200": {
            "description": "JSON-RPC response (application/json)"
          },
          "400": {
            "description": "Unsupported MCP-Protocol-Version on a request other than initialize (JSON-RPC -32600)"
          },
          "401": {
            "description": "No or invalid credential"
          },
          "405": {
            "description": "GET or DELETE"
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "mcpOAuth": []
          }
        ],
        "summary": "MCP server, Streamable HTTP, stateless, protocol 2025-11-25 (see x-mcp)",
        "tags": [
          "MCP"
        ]
      }
    },
    "/meta/founding-offer": {
      "get": {
        "description": "The first `seats` accounts that sign up before `deadline` get percentOff off the plan price for their first `months` months; overage is billed in full. A checkout holds a seat until its session is paid or expired; a seat whose account is declined or withdrawn is free again, one whose account cancels later stays taken. `available` is true while `seatsLeft` is above 0 and the deadline is ahead; `seatsLeft` is 0 once the deadline passed. The answer may be cached for 30 seconds; requests are rate limited per client address (429).",
        "operationId": "foundingOffer",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FoundingOffer"
                }
              }
            },
            "description": "The offer"
          }
        },
        "summary": "The founding offer and how many of its seats are left, no authentication",
        "tags": [
          "Meta"
        ]
      }
    },
    "/meta/health": {
      "get": {
        "operationId": "health",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            },
            "description": "The API answers and reaches its database"
          },
          "503": {
            "description": "Not healthy"
          }
        },
        "summary": "API availability for the status page (Starwatch HTTP monitor), no authentication",
        "tags": [
          "Meta"
        ]
      }
    },
    "/meta/health/pipeline": {
      "get": {
        "description": "200 only when all three hold: the oldest email waiting for handoff is younger than 5 minutes; KumoMTA accepts an SMTP connection on the submission port; a canary system mail sent every 5 minutes to a mailbox letr.dev owns at an external provider was accepted by that provider within the last 15 minutes. 503 otherwise, with which check failed. No counts, no addresses.",
        "operationId": "pipelineHealth",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PipelineHealth"
                }
              }
            },
            "description": "Healthy"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PipelineHealth"
                }
              }
            },
            "description": "A check failed"
          }
        },
        "summary": "Mail pipeline health for the status page, no authentication",
        "tags": [
          "Meta"
        ]
      }
    },
    "/meta/webhook-client-ca.pem": {
      "get": {
        "operationId": "webhookClientCa",
        "responses": {
          "200": {
            "content": {
              "application/x-pem-file": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "PEM"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No client CA configured"
          }
        },
        "summary": "The letr.dev webhook client CA certificate (PEM), to trust letr_ca client certificates",
        "tags": [
          "Meta"
        ]
      }
    },
    "/meta/webhook-ips": {
      "get": {
        "description": "No authentication. Webhook requests leave only from these addresses; letr.dev connects over IPv6 only when an IPv6 address is listed here. Changes are announced by email to account owners at least 14 days ahead and show up here with the new address before it is used.",
        "operationId": "webhookIps",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookIps"
                }
              }
            },
            "description": "Current addresses"
          }
        },
        "summary": "The addresses letr.dev sends webhooks from, for allowlists",
        "tags": [
          "Meta"
        ]
      }
    },
    "/v1/domains": {
      "get": {
        "operationId": "domains",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Domain"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Domains"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Domains of the account (read_only, full_access)",
        "tags": [
          "Domains"
        ]
      },
      "post": {
        "operationId": "createDomain",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDomainRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            },
            "description": "Domain with the DNS records to add; the DKIM key is generated now"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "domain_exists: this account already has the domain"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "invalid_domain: not a domain name; domain_limit_reached: the account has 20 domains"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Add a sending domain (full_access)",
        "tags": [
          "Domains"
        ]
      }
    },
    "/v1/domains/{id}": {
      "delete": {
        "operationId": "deleteDomain",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Remove a domain (full_access)",
        "tags": [
          "Domains"
        ]
      },
      "get": {
        "operationId": "domain",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            },
            "description": "The domain"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "One domain with its DNS records (read_only, full_access)",
        "tags": [
          "Domains"
        ]
      }
    },
    "/v1/domains/{id}/verify": {
      "post": {
        "operationId": "verifyDomain",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            },
            "description": "The domain with fresh record status"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Check the DNS records now (full_access)",
        "tags": [
          "Domains"
        ]
      }
    },
    "/v1/emails": {
      "get": {
        "operationId": "list",
        "parameters": [
          {
            "description": "Any of these statuses, comma separated",
            "explode": false,
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "items": {
                "description": "scheduled: waits for scheduledAt; canceled: cancelled before it was sent, or after it was partly sent when an address is erased while other recipients still wait. The status never moves back to an earlier stage (scheduled or queued, sent, deferred, then delivered, bounced or failed, then complained, then canceled) but can skip one. delivered, bounced and failed can still change into one another or into complained: for one recipient through a late bounce report, for several recipients through the event letr.dev processed last.",
                "enum": [
                  "scheduled",
                  "canceled",
                  "queued",
                  "sent",
                  "delivered",
                  "deferred",
                  "bounced",
                  "complained",
                  "failed"
                ],
                "type": "string"
              },
              "type": "array"
            }
          },
          {
            "description": "nextCursor of the previous page",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The email id, or a part of a recipient or the subject",
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "maxLength": 200,
              "minLength": 3,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailPage"
                }
              }
            },
            "description": "A page"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Emails of the account, newest first, within its history: 30 days, 365 with the history add-on (read_only, full_access)",
        "tags": [
          "Emails"
        ]
      },
      "post": {
        "operationId": "send",
        "parameters": [
          {
            "description": "letr.dev remembers a key for at least 32 days and frees it within a day after that. Until then the same key with the same body answers with the ids and current status of the emails it created and sends nothing; with another body it is a 422 (idempotency_key_reused).",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmailRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendEmailResponse"
                }
              }
            },
            "description": "Stored and queued"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Account not active or paused, insufficient_permission, or domain_not_allowed for a key limited to other domains"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Unverified from-domain, suppressed or invalid recipients, limits exceeded"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Monthly ceiling (monthly_ceiling_reached) or the account's daily limit (daily_limit_reached) reached"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Queue one email",
        "tags": [
          "Emails"
        ]
      }
    },
    "/v1/emails/batch": {
      "post": {
        "description": "All or nothing: every email is validated, counted against the limits and stored in one transaction, or none is. The Idempotency-Key covers the whole batch.",
        "operationId": "sendBatch",
        "parameters": [
          {
            "description": "letr.dev remembers a key for at least 32 days and frees it within a day after that. Until then the same key with the same body answers with the ids and current status of the emails it created and sends nothing; with another body it is a 422 (idempotency_key_reused).",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "items": {
                  "$ref": "#/components/schemas/SendEmailRequest"
                },
                "maxItems": 100,
                "minItems": 1,
                "type": "array"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendBatchResponse"
                }
              }
            },
            "description": "Stored and queued"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Account not active or paused"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "One email is invalid; message names its index, e.g. \"[3].to\""
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The batch would pass the monthly cap or daily limit"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Queue up to 100 emails in one call",
        "tags": [
          "Emails"
        ]
      }
    },
    "/v1/emails/{id}": {
      "get": {
        "operationId": "get",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Email"
                }
              }
            },
            "description": "The email"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such email in the account"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Status and events of one email",
        "tags": [
          "Emails"
        ]
      }
    },
    "/v1/emails/{id}/cancel": {
      "post": {
        "operationId": "cancel",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Email"
                }
              }
            },
            "description": "The email with status canceled"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such email in the account"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "not_scheduled: the email is not waiting for its scheduled time any more"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Cancel a scheduled email before it is sent (full_access, or the sending key that created it)",
        "tags": [
          "Emails"
        ]
      }
    },
    "/v1/emails/{id}/content": {
      "get": {
        "operationId": "content",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailContent"
                }
              }
            },
            "description": "The content"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such email in the account, or its content is gone"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ContentBusy"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "What letr.dev sent: the headers, text, HTML and attachments of one email",
        "tags": [
          "Emails"
        ]
      }
    },
    "/v1/emails/{id}/content/attachments/{index}": {
      "get": {
        "operationId": "attachment",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "index",
            "required": true,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The bytes"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such email, content or attachment"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ContentBusy"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "The bytes of one attachment of a sent email",
        "tags": [
          "Emails"
        ]
      }
    },
    "/v1/metrics": {
      "get": {
        "operationId": "metrics",
        "parameters": [
          {
            "in": "query",
            "name": "range",
            "required": false,
            "schema": {
              "enum": [
                "7d",
                "30d"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metrics"
                }
              }
            },
            "description": "Metrics"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Delivery, reputation and webhook metrics (read_only, full_access)",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/recipients/{email}": {
      "delete": {
        "description": "For a recipient's erasure request. Replaces the address with `erased` in every email of the account (to, cc, bcc), in their events and in the webhook deliveries that name it, and deletes its suppression entry unless keepSuppression is true. The emails, their counts toward usage and the bounce and complaint rates stay, so erasure never changes limits, billing or reputation.",
        "operationId": "eraseRecipient",
        "parameters": [
          {
            "in": "path",
            "name": "email",
            "required": true,
            "schema": {
              "format": "email",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "keepSuppression",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecipientErasure"
                }
              }
            },
            "description": "What was deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Erase one recipient's address from what letr.dev holds (full_access)",
        "tags": [
          "Recipients"
        ]
      }
    },
    "/v1/suppressions": {
      "get": {
        "operationId": "suppressions",
        "parameters": [
          {
            "description": "nextCursor of the previous page",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "A part of the address",
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "maxLength": 200,
              "minLength": 3,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppressionPage"
                }
              }
            },
            "description": "A page"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Suppressed recipients, newest first (read_only, full_access)",
        "tags": [
          "Suppressions"
        ]
      },
      "post": {
        "operationId": "addSuppression",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddSuppressionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Suppression"
                }
              }
            },
            "description": "The address was already suppressed; the existing entry"
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Suppression"
                }
              }
            },
            "description": "Added"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "invalid_email"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Add one address to the suppression list with reason manual (full_access)",
        "tags": [
          "Suppressions"
        ]
      }
    },
    "/v1/suppressions/import": {
      "post": {
        "description": "One address per line, CSV or plain text; an optional header line `email`, the first column is used. At most 10'000 lines and 1 MB. Addresses already suppressed are counted, not changed.",
        "operationId": "importSuppressions",
        "requestBody": {
          "content": {
            "text/csv": {
              "schema": {
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppressionImport"
                }
              }
            },
            "description": "Result"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "import_too_large: more than 10'000 lines or 1 MB"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Import a list of addresses to suppress, reason manual (full_access)",
        "tags": [
          "Suppressions"
        ]
      }
    },
    "/v1/suppressions/{email}": {
      "delete": {
        "operationId": "deleteSuppression",
        "parameters": [
          {
            "in": "path",
            "name": "email",
            "required": true,
            "schema": {
              "format": "email",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Allow sending to an address again (full_access)",
        "tags": [
          "Suppressions"
        ]
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "usage",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            },
            "description": "Usage"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Current month usage (read_only, full_access)",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/webhooks": {
      "get": {
        "operationId": "webhooks",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Webhook"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Endpoints, oldest first"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Webhook endpoints, never their secrets (read_only, full_access)",
        "tags": [
          "Webhooks"
        ]
      }
    },
    "/v1/webhooks/{id}": {
      "delete": {
        "operationId": "deleteWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Delete an endpoint (full_access); deliveries still pending for it are dropped",
        "tags": [
          "Webhooks"
        ]
      },
      "patch": {
        "description": "paused: true holds every delivery to the endpoint and paused: false queues the held ones oldest first; either is a no-op when the endpoint is already in that state.",
        "operationId": "updateWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            },
            "description": "Saved"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "webhook_disabled: a disabled endpoint cannot be paused or resumed"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid url or events, or webhook_url_taken"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Change url, events, enabled or paused (full_access); creating endpoints, secrets and certificates stay in the dashboard",
        "tags": [
          "Webhooks"
        ]
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Queued; the result shows up as lastSuccessAt or lastFailureAt"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "summary": "Queue a webhook.test event to this endpoint, even when it is disabled (full_access)",
        "tags": [
          "Webhooks"
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://api.letr.dev"
    }
  ],
  "tags": [
    {
      "description": "Sending domains and the DNS records that verify them",
      "name": "Domains"
    },
    {
      "description": "Usage and delivery metrics of the account",
      "name": "Account"
    },
    {
      "description": "What letr.dev holds about one recipient",
      "name": "Recipients"
    },
    {
      "description": "Endpoints that receive delivery events",
      "name": "Webhooks"
    },
    {
      "description": "Send email and follow its delivery",
      "name": "Emails"
    },
    {
      "description": "Recipients letr.dev no longer sends to",
      "name": "Suppressions"
    },
    {
      "description": "The hosted MCP server and how clients sign in to it",
      "name": "MCP"
    },
    {
      "description": "Public facts about letr.dev, no authentication",
      "name": "Meta"
    }
  ],
  "webhooks": {
    "emailEvent": {
      "post": {
        "description": "Sent to every enabled endpoint of the account that subscribed to the event's type. Signed as Standard\nWebhooks (standardwebhooks.com): headers `webhook-id` (the event id), `webhook-timestamp` (unix seconds)\nand `webhook-signature: v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed\nwith the base64-decoded part of the endpoint's `whsec_` signing secret; `letr-endpoint-id` names the\nendpoint. During a secret rotation the header carries two space-separated signatures; accept the request\nwhen any matches, and reject a timestamp more than 5 minutes off. The official Standard Webhooks libraries\nverify it.\nletr.dev waits 10 seconds for an answer; any non-2xx answer, or none in that time, is retried with exponential\nbackoff for 24 hours. The event id is the same on every endpoint and every retry. Events can arrive out of\norder and more than once: order them by createdAt, drop duplicates by id and never move a status back.\nTransport: https only, TLS 1.2 or later, the hostname is always verified,\nredirects are not followed, private and loopback addresses are refused at connect time, and the request\nleaves from the addresses in GET /meta/webhook-ips. With a client certificate configured, letr.dev presents\nit for mutual TLS.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges"
          }
        },
        "summary": "Event sent to the customer's webhook endpoint"
      }
    }
  }
}