{
  "openapi": "3.1.0",
  "info": {
    "title": "AgentPostage API",
    "version": "1.1.0",
    "description": "Send PDFs as physical US mail through REST, MCP or CLI. Prices are fixed at enqueue from PDF pages and print options. POST /v1/price checks the total without sending or reserving funds. All money uses integer USD cents."
  },
  "servers": [
    {
      "url": "https://agentpostage.com"
    }
  ],
  "paths": {
    "/v1/chatgpt/letters": {
      "post": {
        "operationId": "sendChatGPTLetter",
        "summary": "Mail one PDF attached in ChatGPT",
        "description": "Queue real paid US mail from one ChatGPT PDF attachment. Save one idempotency_key before sending and reuse it for the same PDF, addresses, service and print options on retries. A queued response is not proof of mailing. This is the Custom GPT Actions adapter, not an arbitrary URL upload endpoint.",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "idempotency_key",
                  "openaiFileIdRefs",
                  "sender",
                  "recipient"
                ],
                "properties": {
                  "idempotency_key": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9._:-]{8,128}$",
                    "description": "Unique to this intended letter. Preserve it across uncertain results, including when refreshing an expired file link."
                  },
                  "openaiFileIdRefs": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 1,
                    "description": "Exactly one PDF uploaded by the user or created in this conversation. Use its file reference, not a typed URL. ChatGPT supplies a short-lived files.oaiusercontent.com download link. PDF limit 10 MiB.",
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "id",
                        "name",
                        "mime_type",
                        "download_link"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "mime_type": {
                          "type": "string",
                          "const": "application/pdf"
                        },
                        "download_link": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    }
                  },
                  "sender": {
                    "$ref": "#/components/schemas/Address"
                  },
                  "recipient": {
                    "$ref": "#/components/schemas/Address"
                  },
                  "service": {
                    "$ref": "#/components/schemas/Service"
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "black",
                      "color"
                    ],
                    "default": "black",
                    "description": "Black-and-white or full color. Color adds 30 cents per PDF page."
                  },
                  "duplex": {
                    "type": "boolean",
                    "default": false,
                    "description": "Print on both sides. Pricing is per PDF page, not per physical sheet."
                  },
                  "paper": {
                    "type": "string",
                    "enum": [
                      "white",
                      "yellow",
                      "blue",
                      "green",
                      "orange",
                      "red",
                      "ivory",
                      "perforated",
                      "statement",
                      "check_blue",
                      "check_red",
                      "check_green",
                      "coupon"
                    ],
                    "default": "white",
                    "description": "US letter paper stock. Non-white choices add 10 cents per document sheet. Specialty stocks require an appropriately laid-out PDF."
                  },
                  "return_envelope": {
                    "type": "string",
                    "enum": [
                      "none",
                      "right_window",
                      "left_window",
                      "small",
                      "coupon_pack"
                    ],
                    "default": "none",
                    "description": "Unstamped reply envelope. Window/small envelopes add 50 cents, twelve-pack adds 150 cents."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Persisted letter queued for sending, including idempotent replay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "400": {
            "description": "Invalid attachment, addresses, service or PDF. Refresh an expired attachment using the same idempotency key and identical PDF.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/letters": {
      "post": {
        "operationId": "sendLetter",
        "summary": "Upload a PDF and queue physical sending",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "202": {
            "description": "Persisted letter queued for sending (including idempotent replay).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input or PDF. Service page limits are enforced before storage or charging.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "One request saves an immutable PDF and queues mailing. No per-letter approval. Exact cost becomes available after pricing. The service fee is the greater of 200 cents or 25% of provider cost, rounded up. Repeat identical input with the same Idempotency-Key. Source PDF: 10 MiB. Print PDF: 20 MiB. Document page limits: first_class 500. certified_return_receipt 150. Certified PDFs over 150 pages return HTTP 400 certified_page_limit before storage or charging. Provider adds an address coversheet.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{8,128}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendJSON"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "pdf": {
                    "type": "string",
                    "format": "binary"
                  },
                  "sender": {
                    "type": "string",
                    "description": "JSON-string Address object."
                  },
                  "recipient": {
                    "type": "string",
                    "description": "JSON-string Address object."
                  },
                  "service": {
                    "$ref": "#/components/schemas/Service"
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "black",
                      "color"
                    ],
                    "default": "black",
                    "description": "Black-and-white or full color. Color adds 30 cents per PDF page."
                  },
                  "duplex": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ],
                    "default": "false",
                    "description": "Multipart text value. true prints on both sides."
                  },
                  "paper": {
                    "type": "string",
                    "enum": [
                      "white",
                      "yellow",
                      "blue",
                      "green",
                      "orange",
                      "red",
                      "ivory",
                      "perforated",
                      "statement",
                      "check_blue",
                      "check_red",
                      "check_green",
                      "coupon"
                    ],
                    "default": "white",
                    "description": "US letter paper stock. Non-white choices add 10 cents per document sheet. Specialty stocks require an appropriately laid-out PDF."
                  },
                  "return_envelope": {
                    "type": "string",
                    "enum": [
                      "none",
                      "right_window",
                      "left_window",
                      "small",
                      "coupon_pack"
                    ],
                    "default": "none",
                    "description": "Unstamped reply envelope. Window/small envelopes add 50 cents, twelve-pack adds 150 cents."
                  }
                },
                "required": [
                  "pdf",
                  "sender",
                  "recipient"
                ],
                "additionalProperties": false
              },
              "encoding": {
                "pdf": {
                  "contentType": "application/pdf"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listLetters",
        "summary": "List letters",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "List letters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "letters": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Letter"
                      }
                    },
                    "next_cursor": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "letters",
                    "next_cursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque next_cursor from the previous page."
          }
        ]
      }
    },
    "/v1/letters/{id}": {
      "get": {
        "operationId": "getLetter",
        "summary": "Read letter status, cost and provider evidence",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Read letter status, cost and provider evidence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/v1/letters/{id}/cancel": {
      "post": {
        "operationId": "cancelLetter",
        "summary": "Cancel before authorization starts",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Cancel before authorization starts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "No request body required. Cancellation must win atomically against dispatch. A conflict does not confirm cancellation."
      }
    },
    "/v1/letters/{id}/pdf": {
      "get": {
        "operationId": "getLetterPDF",
        "summary": "Download the private submitted print PDF",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Normalized print PDF, excluding the provider address coversheet.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "410": {
            "description": "Document retention period ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/balance": {
      "get": {
        "operationId": "getBalance",
        "summary": "Read available, reserved and debt balances in USD cents",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Read available, reserved and debt balances in USD cents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions": {
      "get": {
        "operationId": "listTransactions",
        "summary": "Read account ledger history",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Read account ledger history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transactions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Transaction"
                      }
                    },
                    "next_cursor": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "transactions",
                    "next_cursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque next_cursor from the previous page."
          }
        ]
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "getMe",
        "summary": "Read account identity and balance",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Read account identity and balance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id"
                      ],
                      "additionalProperties": false
                    },
                    "balance": {
                      "$ref": "#/components/schemas/Balance"
                    },
                    "environment": {
                      "type": "string"
                    },
                    "mail_mode": {
                      "type": "string"
                    },
                    "test_mode": {
                      "type": "boolean"
                    },
                    "terms_accepted": {
                      "type": "boolean"
                    },
                    "terms_version": {
                      "const": "2026-09-17-retail"
                    }
                  },
                  "required": [
                    "account_id",
                    "balance",
                    "terms_accepted",
                    "terms_version"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/terms/accept": {
      "post": {
        "operationId": "acceptTerms",
        "summary": "Record owner acceptance of the current service terms",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "201": {
            "description": "Versioned acceptance recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "const": true
                    },
                    "version": {
                      "const": "2026-09-17-retail"
                    }
                  },
                  "required": [
                    "accepted",
                    "version"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "Structured API error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "accepted": {
                    "const": true
                  },
                  "version": {
                    "const": "2026-09-17-retail"
                  }
                },
                "required": [
                  "accepted",
                  "version"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/keys": {
      "get": {
        "operationId": "listKeys",
        "summary": "List API-key metadata",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "List API-key metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Key"
                      }
                    }
                  },
                  "required": [
                    "keys"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createKey",
        "summary": "Create a named key; secret returned once",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "201": {
            "description": "Key created. Store the key now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NewKey"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/keys/{id}": {
      "delete": {
        "operationId": "revokeKey",
        "summary": "Revoke an API key",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Revoke an API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "revoked_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "id",
                    "revoked_at"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/v1/billing/link": {
      "get": {
        "operationId": "getBillingLink",
        "summary": "Get the owner billing page link",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Get the owner billing page link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostedURL"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing/settings": {
      "get": {
        "operationId": "getBillingSettings",
        "summary": "Read saved-card and recharge settings",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Read saved-card and recharge settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSettings"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateBillingSettings",
        "summary": "Save recharge settings with explicit owner consent",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Save recharge settings with explicit owner consent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSettings"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Starts disabled. Initial amount 2500, threshold 500. Enabling requires consent:true and a saved card. This authorizes future off-session charges until disabled.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "threshold_cents": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 99999999
                  },
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 99999999
                  },
                  "consent": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "enabled",
                  "threshold_cents",
                  "amount_cents"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/billing/checkout": {
      "post": {
        "operationId": "createCheckout",
        "summary": "Open Stripe Checkout for prepaid USD credit",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "201": {
            "description": "Hosted Stripe URL. Return navigation is not evidence of payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostedURL"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{8,128}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 99999999
                  },
                  "save_card": {
                    "type": "boolean",
                    "default": false
                  }
                },
                "required": [
                  "amount_cents"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/billing/setup": {
      "post": {
        "operationId": "createSetup",
        "summary": "Open Stripe Checkout to save or replace a card",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "201": {
            "description": "Hosted Stripe URL. Return navigation is not evidence of payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostedURL"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{8,128}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "consent": {
                    "const": true
                  }
                },
                "required": [
                  "consent"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/billing/portal": {
      "post": {
        "operationId": "createPortal",
        "summary": "Open the Stripe billing portal",
        "security": [
          {
            "OwnerSession": []
          }
        ],
        "responses": {
          "201": {
            "description": "Hosted Stripe URL. Return navigation is not evidence of payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostedURL"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error. Preserve the idempotency key after an uncertain result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{8,128}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {},
                "required": [],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/letters/{id}/receipt": {
      "get": {
        "operationId": "getLetterReceipt",
        "summary": "Download the private Certified return-receipt PDF",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Provider return-receipt PDF, when evidence confirms availability.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "410": {
            "description": "Document retention period ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Structured error, including receipt not yet available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/price": {
      "post": {
        "operationId": "priceLetter",
        "summary": "Check an exact letter price without sending",
        "description": "Read-only price lookup from actual PDF page count and print options. No PDF, addresses or Idempotency-Key required. Does not create a letter or reserve money or a future rate. A send fixes its current tariff when queued. Browser owner requests require the same-origin Origin header.",
        "security": [
          {
            "AgentKey": []
          },
          {
            "OwnerSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PriceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Exact current total in USD cents.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Price"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "AgentKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Revocable ap_ key."
      },
      "OwnerSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-agentpostage.session",
        "description": "Verified WorkOS AuthKit owner browser session. Mutations require same-origin Origin."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "next_action": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "Address": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "address_line1": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "address_line2": {
            "type": "string",
            "maxLength": 100
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "state": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$"
          },
          "postal_code": {
            "type": "string",
            "pattern": "^[0-9]{5}(-[0-9]{4})?$"
          },
          "country": {
            "const": "US"
          }
        },
        "required": [
          "name",
          "address_line1",
          "city",
          "state",
          "postal_code",
          "country"
        ],
        "additionalProperties": false
      },
      "Service": {
        "type": "string",
        "enum": [
          "first_class",
          "certified",
          "certified_return_receipt",
          "first_class_flat",
          "first_class_hse"
        ],
        "default": "first_class",
        "description": "First-Class and HSE up to 500 PDF pages. Both Certified services up to 150. First-Class flat up to 75 single-sided or 150 duplex. HSE adds a Homeowner Statement Enclosed endorsement. Only certified_return_receipt includes an electronic return receipt."
      },
      "LetterStatus": {
        "type": "string",
        "enum": [
          "queued",
          "preauthorizing",
          "awaiting_funds",
          "authorizing",
          "accepted",
          "mailed",
          "delivered",
          "cancelled",
          "rejected",
          "submission_unknown"
        ]
      },
      "Cost": {
        "type": "object",
        "properties": {
          "provider_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy letters only, retained for compatibility."
          },
          "fee_cents": {
            "type": "integer",
            "minimum": 100,
            "description": "Legacy letters only, retained for compatibility."
          },
          "total_cents": {
            "type": "integer",
            "minimum": 100
          },
          "currency": {
            "const": "USD"
          }
        },
        "required": [
          "total_cents",
          "currency"
        ],
        "additionalProperties": false
      },
      "Evidence": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string"
          },
          "provider_batch_id": {
            "type": "string"
          },
          "provider_document_id": {
            "type": "string"
          },
          "provider_job_id": {
            "type": "string"
          },
          "tracking_number": {
            "type": "string"
          },
          "tracking_url": {
            "type": "string"
          },
          "provider_status": {
            "type": "string"
          },
          "mailed_at": {
            "type": "string"
          },
          "delivered_at": {
            "type": "string"
          },
          "return_receipt_available": {
            "type": "boolean"
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "Letter": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/LetterStatus"
          },
          "service": {
            "$ref": "#/components/schemas/Service"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "cost": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Cost"
              },
              {
                "type": "null"
              }
            ]
          },
          "page_count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "description": "Customer PDF pages, excluding the included address coversheet. First-Class/HSE up to 500, Certified up to 150, explicit flat 75 simplex or 150 duplex."
          },
          "pdf_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "source_sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "pdf_sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "address_coversheet": {
            "const": true
          },
          "next_action": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "mode": {
            "const": "live"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "evidence": {
            "$ref": "#/components/schemas/Evidence"
          },
          "receipt_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Account-authenticated Certified return-receipt PDF URL, null until evidence.return_receipt_available is true. Can become available after delivery."
          },
          "print_options": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PrintOptions"
              },
              {
                "type": "null"
              }
            ],
            "description": "Resolved options. Null after document and address retention ends."
          },
          "pricing_version": {
            "type": "string",
            "description": "Frozen tariff version. New letters use 2026-09-17-retail. Older letters retain legacy pricing."
          }
        },
        "required": [
          "id",
          "status",
          "service",
          "created_at",
          "updated_at",
          "cost",
          "page_count",
          "pdf_url",
          "source_sha256",
          "pdf_sha256",
          "address_coversheet",
          "next_action",
          "mode",
          "events",
          "evidence",
          "receipt_url",
          "print_options",
          "pricing_version"
        ],
        "additionalProperties": false,
        "allOf": [
          {
            "if": {
              "properties": {
                "service": {
                  "enum": [
                    "certified",
                    "certified_return_receipt"
                  ]
                }
              },
              "required": [
                "service"
              ]
            },
            "then": {
              "properties": {
                "page_count": {
                  "maximum": 150
                }
              }
            }
          }
        ]
      },
      "Balance": {
        "type": "object",
        "properties": {
          "available_cents": {
            "type": "integer"
          },
          "reserved_cents": {
            "type": "integer"
          },
          "debt_cents": {
            "type": "integer"
          },
          "dispatch_available": {
            "type": "boolean"
          },
          "currency": {
            "const": "USD"
          }
        },
        "required": [
          "available_cents",
          "reserved_cents",
          "currency"
        ],
        "additionalProperties": false
      },
      "Transaction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "available_delta_cents": {
            "type": "integer"
          },
          "reserved_delta_cents": {
            "type": "integer"
          },
          "debt_delta_cents": {
            "type": "integer"
          },
          "letter_id": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "kind",
          "available_delta_cents",
          "reserved_delta_cents",
          "debt_delta_cents",
          "letter_id",
          "created_at"
        ],
        "additionalProperties": false
      },
      "Key": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "created_at",
          "revoked_at"
        ],
        "additionalProperties": false
      },
      "NewKey": {
        "allOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "revoked_at": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "key": {
                "type": "string"
              }
            },
            "required": [
              "key"
            ]
          }
        ]
      },
      "BillingSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "threshold_cents": {
            "type": "integer",
            "minimum": 1,
            "maximum": 99999999
          },
          "amount_cents": {
            "type": "integer",
            "minimum": 50,
            "maximum": 99999999,
            "description": "Stored recharge amount. New settings require at least 1000 cents. A lower value can appear only on a grandfathered active setting until the owner updates it."
          },
          "has_payment_method": {
            "type": "boolean"
          },
          "recharge_status": {
            "type": "string"
          },
          "paused_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "payment_method": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "brand": {
                    "type": "string"
                  },
                  "last4": {
                    "type": "string"
                  },
                  "exp_month": {
                    "type": "integer"
                  },
                  "exp_year": {
                    "type": "integer"
                  }
                },
                "required": [
                  "brand",
                  "last4",
                  "exp_month",
                  "exp_year"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "enabled",
          "threshold_cents",
          "amount_cents"
        ],
        "additionalProperties": false
      },
      "HostedURL": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "SendJSON": {
        "type": "object",
        "properties": {
          "pdf_base64": {
            "type": "string",
            "contentEncoding": "base64",
            "contentMediaType": "application/pdf",
            "minLength": 1,
            "maxLength": 13981016
          },
          "sender": {
            "$ref": "#/components/schemas/Address"
          },
          "recipient": {
            "$ref": "#/components/schemas/Address"
          },
          "service": {
            "$ref": "#/components/schemas/Service"
          },
          "color": {
            "type": "string",
            "enum": [
              "black",
              "color"
            ],
            "default": "black",
            "description": "Black-and-white or full color. Color adds 30 cents per PDF page."
          },
          "duplex": {
            "type": "boolean",
            "default": false,
            "description": "Print on both sides. Pricing is per PDF page, not per physical sheet."
          },
          "paper": {
            "type": "string",
            "enum": [
              "white",
              "yellow",
              "blue",
              "green",
              "orange",
              "red",
              "ivory",
              "perforated",
              "statement",
              "check_blue",
              "check_red",
              "check_green",
              "coupon"
            ],
            "default": "white",
            "description": "US letter paper stock. Non-white choices add 10 cents per document sheet. Specialty stocks require an appropriately laid-out PDF."
          },
          "return_envelope": {
            "type": "string",
            "enum": [
              "none",
              "right_window",
              "left_window",
              "small",
              "coupon_pack"
            ],
            "default": "none",
            "description": "Unstamped reply envelope. Window/small envelopes add 50 cents, twelve-pack adds 150 cents."
          }
        },
        "required": [
          "pdf_base64",
          "sender",
          "recipient"
        ],
        "additionalProperties": false
      },
      "PrintOptions": {
        "type": "object",
        "properties": {
          "color": {
            "type": "string",
            "enum": [
              "black",
              "color"
            ],
            "default": "black",
            "description": "Black-and-white or full color. Color adds 30 cents per PDF page."
          },
          "duplex": {
            "type": "boolean",
            "default": false,
            "description": "Print on both sides. Pricing is per PDF page, not per physical sheet."
          },
          "paper": {
            "type": "string",
            "enum": [
              "white",
              "yellow",
              "blue",
              "green",
              "orange",
              "red",
              "ivory",
              "perforated",
              "statement",
              "check_blue",
              "check_red",
              "check_green",
              "coupon"
            ],
            "default": "white",
            "description": "US letter paper stock. Non-white choices add 10 cents per document sheet. Specialty stocks require an appropriately laid-out PDF."
          },
          "return_envelope": {
            "type": "string",
            "enum": [
              "none",
              "right_window",
              "left_window",
              "small",
              "coupon_pack"
            ],
            "default": "none",
            "description": "Unstamped reply envelope. Window/small envelopes add 50 cents, twelve-pack adds 150 cents."
          }
        },
        "required": [
          "color",
          "duplex",
          "paper",
          "return_envelope"
        ],
        "additionalProperties": false
      },
      "PriceInput": {
        "type": "object",
        "properties": {
          "page_count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "service": {
            "$ref": "#/components/schemas/Service"
          },
          "color": {
            "type": "string",
            "enum": [
              "black",
              "color"
            ],
            "default": "black",
            "description": "Black-and-white or full color. Color adds 30 cents per PDF page."
          },
          "duplex": {
            "type": "boolean",
            "default": false,
            "description": "Print on both sides. Pricing is per PDF page, not per physical sheet."
          },
          "paper": {
            "type": "string",
            "enum": [
              "white",
              "yellow",
              "blue",
              "green",
              "orange",
              "red",
              "ivory",
              "perforated",
              "statement",
              "check_blue",
              "check_red",
              "check_green",
              "coupon"
            ],
            "default": "white",
            "description": "US letter paper stock. Non-white choices add 10 cents per document sheet. Specialty stocks require an appropriately laid-out PDF."
          },
          "return_envelope": {
            "type": "string",
            "enum": [
              "none",
              "right_window",
              "left_window",
              "small",
              "coupon_pack"
            ],
            "default": "none",
            "description": "Unstamped reply envelope. Window/small envelopes add 50 cents, twelve-pack adds 150 cents."
          }
        },
        "required": [
          "page_count"
        ],
        "additionalProperties": false
      },
      "Price": {
        "type": "object",
        "properties": {
          "page_count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "service": {
            "$ref": "#/components/schemas/Service"
          },
          "color": {
            "type": "string",
            "enum": [
              "black",
              "color"
            ],
            "default": "black",
            "description": "Black-and-white or full color. Color adds 30 cents per PDF page."
          },
          "duplex": {
            "type": "boolean",
            "default": false,
            "description": "Print on both sides. Pricing is per PDF page, not per physical sheet."
          },
          "paper": {
            "type": "string",
            "enum": [
              "white",
              "yellow",
              "blue",
              "green",
              "orange",
              "red",
              "ivory",
              "perforated",
              "statement",
              "check_blue",
              "check_red",
              "check_green",
              "coupon"
            ],
            "default": "white",
            "description": "US letter paper stock. Non-white choices add 10 cents per document sheet. Specialty stocks require an appropriately laid-out PDF."
          },
          "return_envelope": {
            "type": "string",
            "enum": [
              "none",
              "right_window",
              "left_window",
              "small",
              "coupon_pack"
            ],
            "default": "none",
            "description": "Unstamped reply envelope. Window/small envelopes add 50 cents, twelve-pack adds 150 cents."
          },
          "total_cents": {
            "type": "integer",
            "minimum": 1
          },
          "currency": {
            "const": "USD"
          },
          "pricing_version": {
            "const": "2026-09-17-retail"
          },
          "address_coversheet": {
            "const": true
          }
        },
        "required": [
          "page_count",
          "service",
          "color",
          "duplex",
          "paper",
          "return_envelope",
          "total_cents",
          "currency",
          "pricing_version",
          "address_coversheet"
        ],
        "additionalProperties": false
      }
    }
  }
}
