{
  "openapi": "3.1.0",
  "info": {
    "title": "PostalForm Projects Public API",
    "version": "2026-05-06",
    "description": "Public PostalForm Projects API for customer SDKs. Includes document uploads, quotes, mail orders, credits, API keys, and signed customer webhooks."
  },
  "servers": [
    {
      "url": "https://projects.postalform.com"
    }
  ],
  "paths": {
    "/api/v1/documents/upload-intent": {
      "post": {
        "summary": "Create a PDF upload intent.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content_type": {
                    "type": "string",
                    "const": "application/pdf",
                    "byte_size": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "page_count": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "Optional known PDF page count. Used for quote validation and pricing if supplied."
                    }
                  }
                },
                "required": [
                  "content_type",
                  "byte_size"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload instructions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadIntent"
                }
              }
            }
          }
        },
        "operationId": "createUploadIntent"
      }
    },
    "/api/v1/documents/{document_id}/complete": {
      "post": {
        "summary": "Mark an uploaded document complete after the object is present.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DocumentId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "page_count": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Optional PDF page count if it was not supplied during upload-intent creation."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validated document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "document_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "validated"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "completeDocumentUpload"
      }
    },
    "/api/v1/letters/quotes": {
      "post": {
        "summary": "Quote a mailpiece.",
        "description": "Quote a letter from document size, country codes, mail class, and proof-mail settings. Country codes default to US when omitted. PostalForm automatically selects an eligible fulfillment path; API clients choose mailpiece options, not the underlying production network.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateQuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          }
        },
        "operationId": "createLetterQuote"
      }
    },
    "/api/v1/letters": {
      "post": {
        "summary": "Create a test or live mail order from a quote.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLetterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "201": {
            "description": "Created order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          }
        },
        "operationId": "createLetter"
      }
    },
    "/api/v1/letters/{order_id}": {
      "get": {
        "summary": "Retrieve a mail order, timeline, tracking fields, and customer webhook events.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "Order detail.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Letter"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "timeline": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MailOrderEvent"
                          }
                        },
                        "webhook_events": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookEvent"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "operationId": "getLetter"
      }
    },
    "/api/v1/letters/{order_id}/document.pdf": {
      "get": {
        "summary": "Preview or download the PDF for a letter order.",
        "description": "Streams the prepared PDF when available, otherwise the original uploaded PDF while preparation is pending. Documents follow the workspace document retention window.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "current",
                "original",
                "prepared"
              ],
              "default": "current"
            },
            "description": "current returns the prepared PDF when available and otherwise the original upload."
          },
          {
            "name": "disposition",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "inline",
                "attachment"
              ],
              "default": "inline"
            },
            "description": "Use attachment to download instead of previewing inline."
          }
        ],
        "responses": {
          "200": {
            "description": "PDF bytes.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        },
        "operationId": "getLetterDocument"
      }
    },
    "/api/v1/postcards/quotes": {
      "post": {
        "summary": "Quote a postcard mailpiece.",
        "description": "Postcards require a fully composed two-page PDF and a postcard_size of 4x6, 6x9, or 11x6. Postcards are first-class, color, double-sided mailpieces and do not support certified or registered mail.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePostcardQuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Postcard quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          }
        },
        "operationId": "createPostcardQuote"
      }
    },
    "/api/v1/postcards": {
      "post": {
        "summary": "Create a test or live postcard order from a postcard quote.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLetterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "201": {
            "description": "Created postcard order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          }
        },
        "operationId": "createPostcard"
      }
    },
    "/api/v1/postcards/{order_id}": {
      "get": {
        "summary": "Retrieve a postcard order, timeline, tracking fields, and customer webhook events.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "Postcard order detail.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Letter"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "timeline": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MailOrderEvent"
                          }
                        },
                        "webhook_events": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookEvent"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "operationId": "getPostcard"
      }
    },
    "/api/v1/postcards/{order_id}/document.pdf": {
      "get": {
        "summary": "Preview or download the PDF for a postcard order.",
        "description": "Streams the prepared postcard PDF when available, otherwise the original uploaded PDF while preparation is pending. Documents follow the workspace document retention window.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "current",
                "original",
                "prepared"
              ],
              "default": "current"
            },
            "description": "current returns the prepared PDF when available and otherwise the original upload."
          },
          {
            "name": "disposition",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "inline",
                "attachment"
              ],
              "default": "inline"
            },
            "description": "Use attachment to download instead of previewing inline."
          }
        ],
        "responses": {
          "200": {
            "description": "PDF bytes.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        },
        "operationId": "getPostcardDocument"
      }
    },
    "/api/v1/webhook-endpoints": {
      "get": {
        "summary": "List customer webhook endpoints for the workspace.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listWebhookEndpoints"
      },
      "post": {
        "summary": "Configure a customer webhook endpoint for status events.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "pattern": "^https://",
                    "description": "HTTPS endpoint that receives signed PostalForm status webhooks."
                  },
                  "signing_secret": {
                    "type": "string",
                    "description": "Optional. Generated if omitted."
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointSecretResponse"
                }
              }
            }
          }
        },
        "operationId": "createWebhookEndpoint"
      }
    },
    "/api/v1/webhook-endpoints/{endpoint_id}": {
      "delete": {
        "summary": "Disable a customer webhook endpoint.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disabled endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          }
        },
        "operationId": "disableWebhookEndpoint"
      }
    },
    "/api/v1/webhook-endpoints/{endpoint_id}/rotate-secret": {
      "post": {
        "summary": "Rotate a customer webhook endpoint signing secret.",
        "description": "Returns the new signing secret once. Store it securely; list endpoints does not expose secrets.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated endpoint signing secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointSecretResponse"
                }
              }
            }
          }
        },
        "operationId": "rotateWebhookEndpointSecret"
      }
    },
    "/api/v1/webhook-events": {
      "get": {
        "summary": "List customer webhook events for the workspace.",
        "description": "Customer webhook events are fulfillment status-change events only. Event names use `postalform.{letter|postcard}.{status}` and are emitted for `accepted`, `in_transit`, `delivered`, `returned`, `failed`, and `canceled`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEvent"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listWebhookEvents"
      }
    },
    "/api/v1/webhook-events/{event_id}/replay": {
      "post": {
        "summary": "Queue a customer webhook event for replay.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EventId"
          }
        ],
        "responses": {
          "200": {
            "description": "Replay queued."
          }
        },
        "operationId": "replayWebhookEvent"
      }
    },
    "/api/v1/credits/balance": {
      "get": {
        "summary": "Get prepaid credit balance.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditBalance"
                }
              }
            }
          }
        },
        "operationId": "getCreditBalance"
      }
    },
    "/api/v1/credits/payment-methods": {
      "get": {
        "summary": "List saved live billing payment methods.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saved payment methods for live auto-refill.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BillingPaymentMethod"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPaymentMethods"
      }
    },
    "/api/v1/credits/payment-methods/setup-session": {
      "post": {
        "summary": "Create a Checkout setup session for live credit auto-refill.",
        "description": "Requires a live API key. The returned Checkout URL lets the customer save a payment method for future off-session auto-refill charges.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Checkout setup session URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentMethodSetupSession"
                }
              }
            }
          }
        },
        "operationId": "createPaymentMethodSetupSession"
      }
    },
    "/api/v1/credits/auto-refill": {
      "get": {
        "summary": "Get credit auto-refill threshold policies.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Auto-refill policies for test and live credit balances.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditAutoRefillPolicies"
                }
              }
            }
          }
        },
        "operationId": "getCreditAutoRefillPolicies"
      },
      "post": {
        "summary": "Configure the credit auto-refill threshold for the authenticated key mode.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreditAutoRefillRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated auto-refill policy.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CreditAutoRefillPolicy"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "configureCreditAutoRefill"
      }
    },
    "/api/v1/credits/ledger": {
      "get": {
        "summary": "List prepaid credit ledger entries.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CreditLedgerEntry"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listCreditLedger"
      }
    },
    "/api/v1/api-keys": {
      "get": {
        "summary": "List API key prefixes and status.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "API keys.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listApiKeys"
      }
    },
    "/api/v1/api-keys/rotate": {
      "post": {
        "summary": "Rotate a test or live API key.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  }
                },
                "required": [
                  "mode"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New API key secret. Returned once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyRotation"
                }
              }
            }
          }
        },
        "operationId": "rotateApiKey"
      }
    },
    "/api/v1/credits/checkout-session": {
      "post": {
        "summary": "Create a prepaid credit top-up checkout session.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stripe Checkout URL in configured environments, or a local checkout stub URL for local development.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditCheckoutSession"
                }
              }
            }
          }
        },
        "operationId": "createCreditCheckoutSession"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "UploadIntent": {
        "type": "object",
        "properties": {
          "document_id": {
            "type": "string"
          },
          "upload_url": {
            "type": "string",
            "format": "uri"
          },
          "upload_method": {
            "type": "string",
            "enum": [
              "PUT"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateQuoteRequest": {
        "type": "object",
        "description": "Letter quote request. API clients choose mailpiece options; PostalForm handles fulfillment automatically.",
        "properties": {
          "document_id": {
            "type": "string"
          },
          "page_count": {
            "type": "integer",
            "minimum": 1,
            "description": "Optional explicit PDF page count. Used for deterministic pricing when present."
          },
          "mail_class": {
            "type": "string",
            "default": "usps_first_class",
            "description": "Standard/USPS First Class by default. Accepts standard/usps_first_class, priority/usps_priority, and express/usps_express. Priority/Express cannot be combined with certified or registered proof mail."
          },
          "color": {
            "type": "boolean",
            "default": false
          },
          "double_sided": {
            "type": "boolean",
            "default": true
          },
          "certified": {
            "type": "boolean",
            "default": false,
            "description": "Proof-mail add-on for letters only. Eligible U.S. standard letters request USPS Certified Mail. Eligible Canada standard letters request Canada Post Registered Mail. Eligible Belgium, Switzerland, and France standard letters request the available registered-mail option for that destination."
          },
          "certified_return_receipt": {
            "type": "boolean",
            "default": false,
            "description": "Electronic return receipt for eligible U.S. Certified Mail letters."
          },
          "signature_required": {
            "type": "boolean",
            "default": false,
            "description": "Signature confirmation for eligible U.S. Priority and Express letters."
          },
          "destination_country_code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "default": "US",
            "description": "ISO 3166-1 alpha-2 destination country code used for quote validation and pricing. Defaults to US. First-party Projects destinations are US, CA, AT, BE, CH, DE, FR, GB, IN, LU, and NL."
          },
          "origin_country_code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "default": "US",
            "description": "ISO 3166-1 alpha-2 sender/origin country code used for quote validation and pricing. Defaults to US."
          }
        },
        "required": [
          "document_id"
        ]
      },
      "Quote": {
        "type": "object",
        "properties": {
          "quote_id": {
            "type": "string"
          },
          "mailpiece_type": {
            "type": "string",
            "enum": [
              "letter",
              "postcard"
            ]
          },
          "postcard_size": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "4x6",
              "6x9",
              "11x6",
              null
            ]
          },
          "price_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "enum": [
              "usd"
            ]
          },
          "pricing_version": {
            "type": "string"
          },
          "certified_return_receipt": {
            "type": "boolean"
          },
          "signature_required": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateLetterRequest": {
        "type": "object",
        "description": "Create an order from a quote. Requires Idempotency-Key header. Sender is strongly recommended for all live mail and required for some destinations and mailpiece options.",
        "properties": {
          "quote_id": {
            "type": "string"
          },
          "recipient": {
            "$ref": "#/components/schemas/MailingAddress"
          },
          "sender": {
            "$ref": "#/components/schemas/MailingAddress"
          },
          "metadata": {
            "type": "object",
            "description": "Optional caller metadata stored on the order and surfaced in reads/webhook payloads.",
            "additionalProperties": true
          }
        },
        "required": [
          "quote_id",
          "recipient"
        ]
      },
      "Letter": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "letter",
              "postcard"
            ]
          },
          "mailpiece_type": {
            "type": "string",
            "enum": [
              "letter",
              "postcard"
            ]
          },
          "postcard_size": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "4x6",
              "6x9",
              "11x6",
              null
            ]
          },
          "status": {
            "$ref": "#/components/schemas/MailOrderStatus"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "price_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "funds_status": {
            "type": "string"
          },
          "billing_rail": {
            "type": "string"
          },
          "tracking_number": {
            "type": "string",
            "nullable": true,
            "description": "Carrier tracking number when available."
          },
          "tracking_status": {
            "type": "string",
            "nullable": true,
            "description": "Normalized tracking status when available."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "idempotent_replay": {
            "type": "boolean"
          }
        }
      },
      "MailOrderEvent": {
        "type": "object",
        "description": "Customer-facing order timeline event. Internal fulfillment identifiers are not exposed.",
        "properties": {
          "id": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "status_before": {
            "type": "string"
          },
          "status_after": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "workspaceId": {
            "type": "string"
          },
          "orderId": {
            "type": "string"
          },
          "eventType": {
            "$ref": "#/components/schemas/CustomerWebhookEventType"
          },
          "payload": {
            "$ref": "#/components/schemas/CustomerWebhookPayload"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "deliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDeliveryAttempt"
            }
          }
        }
      },
      "CreatePostcardQuoteRequest": {
        "type": "object",
        "description": "Postcard quote request. Postcards force first-class color double-sided mail and cannot use certified or registered mail.",
        "properties": {
          "document_id": {
            "type": "string"
          },
          "page_count": {
            "type": "integer",
            "minimum": 1,
            "description": "Optional explicit PDF page count. Postcard source PDFs should be fully composed."
          },
          "postcard_size": {
            "type": "string",
            "enum": [
              "4x6",
              "6x9",
              "11x6"
            ]
          },
          "destination_country_code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "default": "US",
            "description": "ISO 3166-1 alpha-2 destination country code used for quote validation and pricing. Defaults to US."
          },
          "origin_country_code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "default": "US",
            "description": "ISO 3166-1 alpha-2 sender/origin country code used for quote validation and pricing. Defaults to US."
          }
        },
        "required": [
          "document_id",
          "postcard_size"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointSecretResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "signing_secret": {
            "type": "string",
            "description": "Returned only when the endpoint is created or its signing secret is rotated."
          }
        }
      },
      "CreditBalance": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "billing_rail": {
            "type": "string",
            "enum": [
              "mock_test_credits",
              "prepaid_credits",
              "future_projects_spt",
              "future_metronome_streaming",
              "future_mpp_tempo",
              "manual_invoice"
            ]
          },
          "available_cents": {
            "type": "integer"
          },
          "reserved_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "test": {
            "$ref": "#/components/schemas/CreditBalanceSnapshot"
          },
          "live": {
            "$ref": "#/components/schemas/CreditBalanceSnapshot"
          },
          "auto_refill": {
            "$ref": "#/components/schemas/CreditAutoRefillPolicies"
          }
        }
      },
      "BillingPaymentMethod": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe"
            ]
          },
          "type": {
            "type": "string"
          },
          "brand": {
            "type": "string"
          },
          "last4": {
            "type": "string"
          },
          "exp_month": {
            "type": "integer"
          },
          "exp_year": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaymentMethodSetupSession": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "stripe_checkout",
              "local_checkout_stub"
            ]
          },
          "checkout_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "CreditAutoRefillPolicies": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "test": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/CreditAutoRefillPolicy"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "live": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/CreditAutoRefillPolicy"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          },
          "test": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CreditAutoRefillPolicy"
              },
              {
                "type": "null"
              }
            ]
          },
          "live": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CreditAutoRefillPolicy"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "CreditAutoRefillRequest": {
        "type": "object",
        "properties": {
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "threshold_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000
          },
          "refill_amount_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000
          }
        },
        "required": [
          "threshold_cents",
          "refill_amount_cents"
        ]
      },
      "CreditAutoRefillPolicy": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "workspace_id": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "billing_rail": {
            "type": "string",
            "enum": [
              "mock_test_credits",
              "prepaid_credits"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "threshold_cents": {
            "type": "integer"
          },
          "refill_amount_cents": {
            "type": "integer"
          },
          "provider": {
            "type": "string",
            "enum": [
              "test_grant",
              "stripe_checkout",
              "local_checkout_stub"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "payment_action_required",
              "payment_pending",
              "payment_failed"
            ]
          },
          "last_triggered_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreditLedgerEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "workspace_id": {
            "type": "string"
          },
          "credit_account_id": {
            "type": "string"
          },
          "order_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "billing_rail": {
            "type": "string",
            "enum": [
              "mock_test_credits",
              "prepaid_credits",
              "future_projects_spt",
              "future_metronome_streaming",
              "future_mpp_tempo",
              "manual_invoice"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "test_grant",
              "topup",
              "reserve",
              "capture",
              "release",
              "refund",
              "adjustment"
            ]
          },
          "amount_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "external_reference": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "prefix": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "revoked"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Mode": {
        "type": "string",
        "enum": [
          "test",
          "live"
        ]
      },
      "ApiKeyRotation": {
        "type": "object",
        "properties": {
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "api_key": {
            "type": "string",
            "description": "Full API key secret. Returned once."
          },
          "api_key_prefix": {
            "type": "string"
          }
        }
      },
      "CreditCheckoutSession": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "PostalForm top-up ID."
          },
          "mode": {
            "type": "string",
            "enum": [
              "stripe_checkout",
              "local_checkout_stub"
            ]
          },
          "checkout_url": {
            "type": "string",
            "format": "uri"
          },
          "amount_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          }
        }
      },
      "MailingAddress": {
        "type": "object",
        "description": "Flexible mailing address object. Pass countryCode, country_code, country, address_country, or addressCountry for international destinations; country defaults to US when omitted.",
        "additionalProperties": true,
        "properties": {
          "name": {
            "type": "string"
          },
          "company": {
            "type": "string"
          },
          "line1": {
            "type": "string",
            "description": "Also accepts street1, address_line1, or addressLine1."
          },
          "line2": {
            "type": "string",
            "description": "Also accepts street2, address_line2, or addressLine2."
          },
          "city": {
            "type": "string",
            "description": "Also accepts address_city or addressCity."
          },
          "state": {
            "type": "string",
            "description": "Also accepts province, provinceOrState, address_state, or addressState."
          },
          "postal_code": {
            "type": "string",
            "description": "Also accepts postalCode, postalOrZip, zip, address_zip, or addressZip."
          },
          "countryCode": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "default": "US",
            "description": "ISO 3166-1 alpha-2 country code. Defaults to US when omitted."
          }
        }
      },
      "MailOrderStatus": {
        "type": "string",
        "enum": [
          "queued",
          "document_preparing",
          "document_prepared",
          "submitted",
          "accepted",
          "in_transit",
          "delivered",
          "returned",
          "submission_pending",
          "failed",
          "canceled"
        ],
        "description": "Order timeline status. `submission_pending` means the fulfillment submit job entered a physical-mail safety window where PostalForm cannot blindly retry without risking duplicate mail; it requires reconciliation if it remains the current order status."
      },
      "CustomerWebhookEventType": {
        "type": "string",
        "description": "Customer webhook event names emitted for fulfillment status changes. Replace `letter` with `postcard` for postcard mailpieces.",
        "enum": [
          "postalform.letter.accepted",
          "postalform.letter.in_transit",
          "postalform.letter.delivered",
          "postalform.letter.returned",
          "postalform.letter.failed",
          "postalform.letter.canceled",
          "postalform.postcard.accepted",
          "postalform.postcard.in_transit",
          "postalform.postcard.delivered",
          "postalform.postcard.returned",
          "postalform.postcard.failed",
          "postalform.postcard.canceled"
        ],
        "x-enumDescriptions": {
          "postalform.letter.accepted": "Letter accepted for production or mailing after the order leaves PostalForm's preparation queue.",
          "postalform.letter.in_transit": "Letter entered the mail stream. The payload may include or update `mailpiece.tracking_number` when tracking is available.",
          "postalform.letter.delivered": "Letter reported delivered by the carrier or delivery network.",
          "postalform.letter.returned": "Letter returned or otherwise marked undeliverable.",
          "postalform.letter.failed": "Letter could not be produced or mailed. Inspect the order timeline and retry with a corrected request when appropriate.",
          "postalform.letter.canceled": "Letter canceled before delivery completion.",
          "postalform.postcard.accepted": "Postcard accepted for production or mailing after the order leaves PostalForm's preparation queue.",
          "postalform.postcard.in_transit": "Postcard entered the mail stream. The payload may include or update `mailpiece.tracking_number` when tracking is available.",
          "postalform.postcard.delivered": "Postcard reported delivered by the carrier or delivery network.",
          "postalform.postcard.returned": "Postcard returned or otherwise marked undeliverable.",
          "postalform.postcard.failed": "Postcard could not be produced or mailed. Inspect the order timeline and retry with a corrected request when appropriate.",
          "postalform.postcard.canceled": "Postcard canceled before delivery completion."
        }
      },
      "CustomerWebhookPayload": {
        "type": "object",
        "description": "JSON body POSTed to customer webhook endpoints.",
        "properties": {
          "id": {
            "type": "string",
            "example": "evt_123"
          },
          "type": {
            "$ref": "#/components/schemas/CustomerWebhookEventType"
          },
          "data": {
            "type": "object",
            "properties": {
              "object": {
                "$ref": "#/components/schemas/Letter"
              }
            }
          },
          "mailpiece": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "nullable": true
              },
              "tracking_number": {
                "type": "string",
                "nullable": true
              },
              "tracking_status": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "WebhookDeliveryAttempt": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "webhookEventId": {
            "type": "string"
          },
          "endpointId": {
            "type": "string"
          },
          "attemptNumber": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed"
            ]
          },
          "httpStatus": {
            "type": "integer"
          },
          "responseBodySnippet": {
            "type": "string"
          },
          "attemptedAt": {
            "type": "string",
            "format": "date-time"
          },
          "nextRetryAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreditBalanceSnapshot": {
        "type": "object",
        "properties": {
          "available_cents": {
            "type": "integer"
          },
          "reserved_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        }
      }
    },
    "parameters": {
      "DocumentId": {
        "name": "document_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "OrderId": {
        "name": "order_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "EventId": {
        "name": "event_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    }
  }
}