{
  "openapi": "3.1.0",
  "info": {
    "title": "Andrii Co. public API",
    "version": "1.0.0",
    "summary": "Public, read-only endpoints for the Andrii Co. notary & apostille booking funnel.",
    "description": "The unauthenticated, read-only surface of the andrii.net API: appointment availability, address autocomplete/reverse-geocode, a server-authoritative price quote, international shipping estimates + dated apostille/carrier promise dates, and non-secret runtime config. These are the same public endpoints the marketing booking widget uses.\n\nThis document deliberately contains NO payment operations and NO `x-payment-info` extensions — the site takes no machine-initiated payments; money only moves via signed webhooks or an operator-confirmed allocation. It also omits every authenticated route (customer/partner/admin/DAV) by design.\n\nFor a richer, typed agent interface (including booking-request and apostille tools), use the Model Context Protocol server at https://andrii.net/mcp (server card: https://andrii.net/.well-known/mcp/server-card.json).",
    "contact": {
      "name": "Andrii Co.",
      "email": "notary@andrii.co",
      "url": "https://andrii.co"
    },
    "license": {
      "name": "Proprietary — public endpoints documented for interoperability"
    }
  },
  "servers": [
    {
      "url": "https://andrii.net",
      "description": "Production API"
    }
  ],
  "externalDocs": {
    "description": "MCP booking server",
    "url": "https://andrii.net/mcp"
  },
  "paths": {
    "/healthz": {
      "get": {
        "summary": "Health probe",
        "description": "Deep health check (database + uploads writable). 200 when healthy.",
        "operationId": "getHealthz",
        "responses": {
          "200": {
            "description": "Service healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "commit": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "A dependency is unavailable"
          }
        }
      }
    },
    "/public-config": {
      "get": {
        "summary": "Non-secret runtime config",
        "description": "Public, non-secret configuration the static portal pages read (browser Maps key, which payment rails are enabled, the operator's Interac address). No secrets are ever returned here.",
        "operationId": "getPublicConfig",
        "responses": {
          "200": {
            "description": "Config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConfig"
                }
              }
            }
          }
        }
      }
    },
    "/availability": {
      "post": {
        "summary": "Open appointment slots",
        "description": "Return open days and slots for a requested service + meeting location over the booking horizon. Each slot carries a `startUnix` (the exact time a client books with) alongside `HH:MM` start/end, a fit band, and merged windows; the response also carries a `recommended` ribbon. Responses are cacheable (ETag + Cache-Control) and non-personalized. This computes availability; it does not create or hold a booking.",
        "operationId": "postAvailability",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AvailabilityRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Open days + slots (shape varies by service/mode; see the MCP `search_availability` tool for the typed contract).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "304": {
            "description": "Not modified (honors If-None-Match)"
          },
          "400": {
            "description": "Invalid request"
          },
          "426": {
            "description": "Stale client build (only when the admin web-build gate is armed)"
          },
          "429": {
            "description": "Rate limited (per-IP availability budget)"
          }
        }
      }
    },
    "/availability/autocomplete": {
      "post": {
        "summary": "Washington address autocomplete",
        "description": "Address suggestions for the booking widget, clamped to Washington State. Always returns 200 with a (possibly empty) `predictions` array; degrades to empty when the upstream is unavailable.",
        "operationId": "postAutocomplete",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutocompleteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suggestions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "predictions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/availability/reverse-geocode": {
      "post": {
        "summary": "Reverse-geocode a coordinate",
        "description": "Turn a visitor's shared GPS coordinate into an editable street address for the booking widget. Returns `{ address, lat, lng }`, or `{ address: null }` when nothing is found.",
        "operationId": "postReverseGeocode",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReverseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resolved address",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "address": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "lat": {
                      "type": "number"
                    },
                    "lng": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/pricing/quote": {
      "post": {
        "summary": "Server-authoritative USD price quote",
        "description": "A pre-shipping USD estimate mirroring the published rate card: notarization, mobile travel, after-hours, and apostille tiers. Returns line items + whether prepayment is required. USD only. This is an estimate, not a charge — no payment is initiated.",
        "operationId": "postPricingQuote",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (e.g. unsupported currency)"
          }
        }
      }
    },
    "/shipping/quote": {
      "get": {
        "summary": "International shipping estimates for a document envelope",
        "description": "Static, dated retail zone-rate estimates (DHL Express Worldwide; FedEx International Priority/Economy; USPS PMEI/PMI flat-rate envelope + First-Class Package International) for a document envelope shipped from the Bothell, WA office. Estimates only — carrier fuel/demand surcharges and duties/taxes excluded; the notary confirms the exact cost with the order. Unknown destinations answer 200 with empty quotes plus an advisory (fail-soft).",
        "operationId": "getShippingQuote",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "Destination country — ISO 3166-1 alpha-2 code or an English name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "postal",
            "in": "query",
            "required": false,
            "description": "Destination postal code (accepted for forward-compatibility; v1 rates are country/zone level).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "docs",
            "in": "query",
            "required": false,
            "description": "Document count 1–50 — drives the weight band only.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Quotes (possibly empty, with an advisory)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShippingQuoteResponse"
                }
              }
            }
          }
        }
      }
    },
    "/shipping/promise": {
      "get": {
        "summary": "Dated apostille-ready / carrier-drop promise (DHL and FedEx only)",
        "description": "Washington-business-day promise dates for an apostille order: the document is notarized on the appointment day, dropped at the WA Secretary of State the next business morning, processed per tier (expedited 1 / priority 3 / standard 7 business days), and handed to the carrier the same business day when ready before the 16:00 Pacific counter cut-off. Published for DHL and FedEx shipments only (400 otherwise).",
        "operationId": "getShippingPromise",
        "parameters": [
          {
            "name": "anchor",
            "in": "query",
            "required": false,
            "description": "Unix seconds of the booked appointment; omitted = the next WA business day.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "tier",
            "in": "query",
            "required": false,
            "description": "Apostille speed tier; omitted = standard.",
            "schema": {
              "type": "string",
              "enum": [
                "expedited",
                "priority",
                "standard"
              ]
            }
          },
          {
            "name": "carrier",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "dhl",
                "fedex"
              ]
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "Destination country for the delivery range (per-lane DHL transit when available).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Promise dates",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShippingPromiseResponse"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported carrier or unknown tier"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PublicConfig": {
        "type": "object",
        "properties": {
          "mapsBrowserKey": {
            "type": "string",
            "description": "HTTP-referrer-restricted browser Maps key (empty when unset)."
          },
          "stripeEnabled": {
            "type": "boolean"
          },
          "stripePublishableKey": {
            "type": "string"
          },
          "interacEmail": {
            "type": "string"
          },
          "paypalEnabled": {
            "type": "boolean"
          }
        }
      },
      "PlaceInput": {
        "type": "object",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "office",
              "peace_arch",
              "wa_sos",
              "customer"
            ],
            "description": "Preset location, or `customer` with a typed `address`."
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Required when kind = customer."
          }
        }
      },
      "AvailabilityRequest": {
        "type": "object",
        "required": [
          "service",
          "place"
        ],
        "properties": {
          "service": {
            "type": "string",
            "description": "e.g. office | mobile | ron | apostille_mail."
          },
          "place": {
            "$ref": "#/components/schemas/PlaceInput"
          },
          "places": {
            "type": "array",
            "maxItems": 3,
            "items": {
              "$ref": "#/components/schemas/PlaceInput"
            },
            "description": "Any-one-of candidate locations for one meeting (first = primary); supersedes `place` when non-empty."
          },
          "documentCount": {
            "type": "integer",
            "minimum": 1,
            "description": "Documents the customer will bring (drives slot duration). Default 1."
          },
          "batch": {
            "type": [
              "string",
              "null"
            ],
            "description": "Return a specific batch event's join grid instead of the normal horizon."
          }
        }
      },
      "AutocompleteRequest": {
        "type": "object",
        "required": [
          "query"
        ],
        "properties": {
          "query": {
            "type": "string",
            "minLength": 3
          },
          "originLat": {
            "type": [
              "number",
              "null"
            ]
          },
          "originLng": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "ReverseRequest": {
        "type": "object",
        "required": [
          "lat",
          "lng"
        ],
        "properties": {
          "lat": {
            "type": "number"
          },
          "lng": {
            "type": "number"
          }
        }
      },
      "QuoteRequest": {
        "type": "object",
        "properties": {
          "documentCount": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "description": "Default 1."
          },
          "mobile": {
            "type": "boolean",
            "description": "We travel to the customer (adds the flat travel fee; the first document's notarization is then bundled)."
          },
          "apostille": {
            "type": "boolean",
            "description": "Include the Secretary-of-State apostille."
          },
          "tier": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "expedited",
              "priority",
              "standard",
              "mailin",
              null
            ],
            "description": "Apostille speed tier (when apostille = true)."
          },
          "afterHours": {
            "type": "boolean",
            "description": "Mobile only: the chosen slot is after 6pm or on a weekend."
          },
          "needsNotarization": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Default true. Set false for a mail-in apostille of already-certified public records (birth/marriage/death certs) that need no notarial act."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Only \"USD\" is supported today."
          }
        }
      },
      "LineItem": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "amountCents": {
            "type": "integer"
          }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string"
          },
          "amountCents": {
            "type": "integer"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "prepaymentRequired": {
            "type": "boolean"
          }
        }
      },
      "ShippingQuoteResponse": {
        "type": "object",
        "properties": {
          "origin": {
            "type": "object",
            "properties": {
              "city": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "zip": {
                "type": "string"
              }
            }
          },
          "parcel": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "const": "document-envelope"
              },
              "weightOz": {
                "type": "integer"
              }
            }
          },
          "quotes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "carrier": {
                  "type": "string",
                  "enum": [
                    "dhl",
                    "fedex",
                    "usps"
                  ]
                },
                "service": {
                  "type": "string"
                },
                "amount": {
                  "type": "number"
                },
                "currency": {
                  "type": "string",
                  "const": "USD"
                },
                "transitDays": {
                  "type": "object",
                  "properties": {
                    "low": {
                      "type": "integer"
                    },
                    "high": {
                      "type": "integer"
                    }
                  },
                  "description": "Calendar-day range."
                }
              }
            }
          },
          "asOf": {
            "type": "string",
            "format": "date"
          },
          "disclaimer": {
            "type": "string"
          },
          "advisory": {
            "type": "string",
            "description": "Present when there is something to know about the lane (e.g. Ukraine ships best via Nova Poshta) or no quotes exist."
          }
        }
      },
      "ShippingPromiseResponse": {
        "type": "object",
        "properties": {
          "carrier": {
            "type": "string"
          },
          "tier": {
            "type": "string"
          },
          "anchorDate": {
            "type": "string",
            "format": "date"
          },
          "apostilleReadyBy": {
            "type": "string",
            "format": "date"
          },
          "carrierDropBy": {
            "type": "string",
            "format": "date"
          },
          "carrierDropCutoff": {
            "type": "string",
            "example": "16:00"
          },
          "deliveredBy": {
            "type": "object",
            "properties": {
              "low": {
                "type": "string",
                "format": "date"
              },
              "high": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "disclaimer": {
            "type": "string"
          }
        }
      }
    }
  }
}
