{
  "openapi": "3.0.3",
  "info": {
    "title": "Verified.Tools API",
    "version": "1.0.0",
    "contact": {
      "name": "Verified.Tools Support",
      "url": "https://verified.tools/support",
      "email": "support@verified.tools"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://verified.tools/terms"
    },
    "description": "## Overview\n\nThe Verified.Tools REST API provides programmatic access to document certification, verification, and audit features.\n\nBase URL: `https://api.verified.tools/api/v1`\n\n## eIDAS Disclosure\n\nThis API constitutes a Simple Electronic Signature (SES) platform under eIDAS Regulation (EU) 910/2014. All verification outputs are labelled \"Platform-Verified Integrity Check\" and do not constitute a Qualified Electronic Signature (QES) or Advanced Electronic Signature (AdES) as defined under eIDAS. QTSP/AdES integration is planned for a future release. Until that integration is live, relying parties should treat verification results as integrity evidence rather than legally binding electronic signatures under eIDAS Article 25.\n\n## Authentication\n\nThe API supports two authentication methods:\n\n1. **Bearer JWT** — Short-lived access token (1-hour TTL) issued at login. Pass in `Authorization: Bearer <token>` header.\n2. **API Key** — Long-lived key with the `vt_` prefix. Manage keys via the `/api-keys` endpoints. Pass in `Authorization: Bearer vt_<key>` header.\n\nCertain endpoints require **Step-up MFA** — the JWT must include a fresh `mfa_verified_at` claim (within 5 minutes). Obtain a step-up token via `POST /auth/step-up`.\n\n## Rate Limiting\n\nAll endpoints are rate-limited at the Cloudflare WAF layer. Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. A `429 Too Many Requests` response is returned when the limit is exceeded.\n\n## Errors\n\nAll errors follow the envelope:\n\n```json\n{\n  \"error\": {\n    \"code\": \"ERROR_CODE\",\n    \"message\": \"Human-readable description\",\n    \"details\": {}\n  }\n}\n```\n\n## Pagination\n\nList endpoints use cursor-based pagination. The response includes a `next_cursor` field. Pass `?cursor=<value>` to retrieve the next page.\n\n## Idempotency\n\nDocument upload supports the `Idempotency-Key` header for safe retries. Pass a UUID v4 in the header; duplicate requests with the same key return the cached response."
  },
  "servers": [
    {
      "url": "https://api.verified.tools/api/v1",
      "description": "Production"
    },
    {
      "url": "https://api-staging.verified.tools/api/v1",
      "description": "Staging"
    }
  ],
  "security": [
    { "BearerJWT": [] },
    { "ApiKey": [] }
  ],
  "components": {
    "securitySchemes": {
      "BearerJWT": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Short-lived JWT access token (1-hour TTL). Obtain via POST /auth/login or POST /auth/register."
      },
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Long-lived API key with `vt_` prefix. Create and manage via the /api-keys endpoints. Requires step-up MFA to create."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code.",
                "example": "VALIDATION_ERROR"
              },
              "message": {
                "type": "string",
                "description": "Human-readable description of the error.",
                "example": "Request validation failed"
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Optional structured details (e.g. per-field validation errors)."
              }
            }
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "required": [
          "name", "slug", "domain", "country_code",
          "business_id_type", "business_id_value",
          "admin_email", "admin_password"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 200,
            "example": "Acme Engineering Pty Ltd"
          },
          "slug": {
            "type": "string",
            "minLength": 2,
            "maxLength": 63,
            "pattern": "^[a-z0-9-]+$",
            "example": "acme-engineering"
          },
          "domain": {
            "type": "string",
            "minLength": 4,
            "maxLength": 253,
            "example": "acme.com.au"
          },
          "country_code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code.",
            "example": "AU"
          },
          "business_id_type": {
            "type": "string",
            "minLength": 2,
            "maxLength": 20,
            "example": "ABN"
          },
          "business_id_value": {
            "type": "string",
            "minLength": 4,
            "maxLength": 50,
            "example": "51824753556"
          },
          "admin_email": {
            "type": "string",
            "format": "email",
            "example": "admin@acme.com.au"
          },
          "admin_password": {
            "type": "string",
            "minLength": 12,
            "format": "password",
            "description": "Minimum 12 characters with at least one uppercase letter, one digit, and one special character."
          }
        }
      },
      "RegisterResponse": {
        "type": "object",
        "properties": {
          "company_id": { "type": "string", "format": "uuid" },
          "user_id": { "type": "string", "format": "uuid" },
          "status": { "type": "string", "example": "PendingDnsVerification" },
          "dns_txt_record": {
            "type": "object",
            "properties": {
              "host": { "type": "string", "example": "_verified-tools" },
              "value": { "type": "string", "example": "verified-tools-verify=abc123..." },
              "ttl": { "type": "integer", "example": 300 }
            }
          },
          "jwt": { "type": "string", "description": "Initial access JWT." },
          "expires_at": { "type": "integer", "description": "Unix epoch seconds when JWT expires." },
          "message": { "type": "string" }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": ["email", "password"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string", "format": "password" }
        }
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "access_token": { "type": "string" },
          "refresh_token": { "type": "string" },
          "expires_in": { "type": "integer", "example": 3600 },
          "mfa_required": { "type": "boolean" },
          "token_type": { "type": "string", "example": "Bearer" }
        }
      },
      "RefreshRequest": {
        "type": "object",
        "required": ["refresh_token"],
        "properties": {
          "refresh_token": { "type": "string" }
        }
      },
      "RefreshResponse": {
        "type": "object",
        "properties": {
          "access_token": { "type": "string" },
          "refresh_token": { "type": "string" },
          "expires_in": { "type": "integer", "example": 3600 }
        }
      },
      "StepUpRequest": {
        "type": "object",
        "required": ["code"],
        "properties": {
          "code": {
            "type": "string",
            "description": "6-digit TOTP code from authenticator app.",
            "example": "123456",
            "minLength": 6,
            "maxLength": 6
          }
        }
      },
      "StepUpResponse": {
        "type": "object",
        "properties": {
          "access_token": { "type": "string", "description": "New JWT with mfa_verified_at claim." },
          "expires_in": { "type": "integer", "example": 3600 }
        }
      },
      "VerifyDnsResponse": {
        "type": "object",
        "properties": {
          "verified": { "type": "boolean" },
          "message": { "type": "string" }
        }
      },
      "DocumentSummary": {
        "type": "object",
        "properties": {
          "document_id": { "type": "string", "format": "uuid" },
          "status": {
            "type": "string",
            "enum": ["Processing", "Active", "Revoked", "Superseded", "Failed"]
          },
          "original_filename": { "type": "string" },
          "content_type": { "type": "string" },
          "file_size_bytes": { "type": "integer" },
          "sha256_hash": { "type": "string" },
          "verification_url": { "type": "string", "format": "uri" },
          "hmac_verification_id": { "type": "string" },
          "verifier_name": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "DocumentDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/DocumentSummary" },
          {
            "type": "object",
            "properties": {
              "download_url": {
                "type": "string",
                "format": "uri",
                "description": "Time-limited signed R2 URL (1-hour TTL). Only present for Active documents."
              },
              "download_url_expires_at": {
                "type": "string",
                "format": "date-time"
              },
              "timestamp_token": {
                "type": "object",
                "properties": {
                  "tsa": { "type": "string" },
                  "serial": { "type": "string" },
                  "timestamp": { "type": "string", "format": "date-time" }
                }
              }
            }
          }
        ]
      },
      "DocumentUploadResponse": {
        "type": "object",
        "properties": {
          "document_id": { "type": "string", "format": "uuid" },
          "hmac_verification_id": { "type": "string" },
          "status": { "type": "string", "example": "Processing" },
          "verification_url": { "type": "string", "format": "uri" },
          "message": { "type": "string" }
        }
      },
      "DocumentListResponse": {
        "type": "object",
        "properties": {
          "documents": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/DocumentSummary" }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque cursor for the next page. null if no more pages."
          },
          "total_count": {
            "type": "integer",
            "description": "Approximate total count."
          }
        }
      },
      "VerificationUrlResponse": {
        "type": "object",
        "properties": {
          "verification_url": { "type": "string", "format": "uri" },
          "hmac_verification_id": { "type": "string" }
        }
      },
      "RevokeRequest": {
        "type": "object",
        "required": ["reason"],
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Human-readable justification for revocation. Stored in audit log.",
            "example": "Document contained an error"
          }
        }
      },
      "RevokeResponse": {
        "type": "object",
        "properties": {
          "document_id": { "type": "string", "format": "uuid" },
          "status": { "type": "string", "example": "Revoked" },
          "relationship_id": { "type": "string", "format": "uuid" },
          "revoked_at": { "type": "string", "format": "date-time" }
        }
      },
      "SupersedeRequest": {
        "type": "object",
        "required": ["new_document_id"],
        "properties": {
          "new_document_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the new (superseding) document. Must be Active and in the same company."
          }
        }
      },
      "SupersedeResponse": {
        "type": "object",
        "properties": {
          "old_document_id": { "type": "string", "format": "uuid" },
          "new_document_id": { "type": "string", "format": "uuid" },
          "old_status": { "type": "string", "example": "Superseded" },
          "relationship_id": { "type": "string", "format": "uuid" }
        }
      },
      "AmendRequest": {
        "type": "object",
        "required": ["new_document_id"],
        "properties": {
          "new_document_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the new document that amends the old one."
          }
        }
      },
      "AmendResponse": {
        "type": "object",
        "properties": {
          "old_document_id": { "type": "string", "format": "uuid" },
          "new_document_id": { "type": "string", "format": "uuid" },
          "relationship_id": { "type": "string", "format": "uuid" },
          "relationship_type": { "type": "string", "example": "Amends" }
        }
      },
      "ReferRequest": {
        "type": "object",
        "required": ["target_document_id"],
        "properties": {
          "target_document_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the document being referred to."
          }
        }
      },
      "ReferResponse": {
        "type": "object",
        "properties": {
          "source_document_id": { "type": "string", "format": "uuid" },
          "target_document_id": { "type": "string", "format": "uuid" },
          "relationship_id": { "type": "string", "format": "uuid" },
          "relationship_type": { "type": "string", "example": "Refers" }
        }
      },
      "RelationshipsResponse": {
        "type": "object",
        "properties": {
          "relationships": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "relationship_id": { "type": "string", "format": "uuid" },
                "relationship_type": {
                  "type": "string",
                  "enum": ["Supersedes", "Amends", "Refers", "Revokes"]
                },
                "source_document_id": { "type": "string", "format": "uuid" },
                "target_document_id": { "type": "string", "format": "uuid" },
                "created_at": { "type": "string", "format": "date-time" }
              }
            }
          }
        }
      },
      "AuditEvent": {
        "type": "object",
        "properties": {
          "event_id": { "type": "string", "format": "uuid" },
          "company_id": { "type": "string", "format": "uuid" },
          "actor_pseudonym": {
            "type": "string",
            "description": "HMAC pseudonym of the actor (GDPR pseudonymisation). Never a raw email address."
          },
          "action": {
            "type": "string",
            "example": "document.upload",
            "description": "Event type / action code."
          },
          "resource_type": { "type": "string", "example": "Document" },
          "resource_id": { "type": "string", "format": "uuid" },
          "prev_hash": {
            "type": "string",
            "nullable": true,
            "description": "SHA-256 of previous event hash for chain verification."
          },
          "event_hash": {
            "type": "string",
            "description": "SHA-256 tamper-evident seal of this event."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true
          },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "AuditListResponse": {
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/AuditEvent" }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "PublicVerificationResult": {
        "type": "object",
        "description": "Result of a public document verification lookup.",
        "properties": {
          "verified": { "type": "boolean" },
          "company_name": { "type": "string" },
          "company_domain": { "type": "string" },
          "document_filename": { "type": "string" },
          "verification_level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "verified_at": { "type": "string", "format": "date-time" },
          "timestamp_token": {
            "type": "object",
            "properties": {
              "tsa": { "type": "string" },
              "serial": { "type": "string" },
              "timestamp": { "type": "string", "format": "date-time" }
            }
          },
          "eidas_disclosure": {
            "type": "string",
            "example": "Platform-Verified Integrity Check — not a Qualified Electronic Signature under eIDAS Regulation (EU) 910/2014."
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "endpoint_id": { "type": "string", "format": "uuid" },
          "url": { "type": "string", "format": "uri" },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "document.processed",
                "document.failed",
                "document.revoked",
                "document.superseded",
                "document.amended",
                "relationship.created",
                "verification.viewed",
                "billing.subscription_updated",
                "billing.payment_failed"
              ]
            }
          },
          "is_active": { "type": "boolean" },
          "description": { "type": "string", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookRegisterRequest": {
        "type": "object",
        "required": ["url", "events"],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS endpoint URL to receive webhook deliveries. HTTP is rejected."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "document.processed",
                "document.failed",
                "document.revoked",
                "document.superseded",
                "document.amended",
                "relationship.created",
                "verification.viewed",
                "billing.subscription_updated",
                "billing.payment_failed"
              ]
            }
          },
          "description": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          }
        }
      },
      "WebhookRegisterResponse": {
        "type": "object",
        "properties": {
          "endpoint_id": { "type": "string", "format": "uuid" },
          "secret": {
            "type": "string",
            "description": "Base64url signing secret. Shown ONCE — store securely. Used to verify X-Verified-Tools-Signature header on deliveries."
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "delivery_id": { "type": "string", "format": "uuid" },
          "endpoint_id": { "type": "string", "format": "uuid" },
          "event_type": { "type": "string" },
          "status": {
            "type": "string",
            "enum": ["Pending", "Delivered", "Failed", "Exhausted"]
          },
          "attempt_count": { "type": "integer" },
          "last_response_status": { "type": "integer", "nullable": true },
          "next_retry_at": { "type": "string", "format": "date-time", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "BillingPlan": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "price_starter" },
          "name": { "type": "string", "example": "Starter" },
          "price_aud_cents": { "type": "integer", "example": 4900 },
          "document_limit": { "type": "integer", "example": 50 },
          "features": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "BillingSetupRequest": {
        "type": "object",
        "required": ["price_id"],
        "properties": {
          "price_id": {
            "type": "string",
            "description": "Stripe Price ID of the selected plan.",
            "example": "price_starter"
          }
        }
      },
      "BillingSetupResponse": {
        "type": "object",
        "properties": {
          "checkout_url": {
            "type": "string",
            "format": "uri",
            "description": "Stripe-hosted checkout URL. Redirect the user to this URL."
          }
        }
      },
      "BillingPortalResponse": {
        "type": "object",
        "properties": {
          "portal_url": {
            "type": "string",
            "format": "uri",
            "description": "Stripe Customer Portal URL. Short-lived, single-use."
          }
        }
      },
      "ApiKeySummary": {
        "type": "object",
        "properties": {
          "key_id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "key_prefix": { "type": "string", "example": "vt_abc123" },
          "scopes": {
            "type": "array",
            "items": { "type": "string" }
          },
          "key_type": {
            "type": "string",
            "enum": ["Standard", "MCP"]
          },
          "last_used_at": { "type": "string", "format": "date-time", "nullable": true },
          "revoked_at": { "type": "string", "format": "date-time", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "ApiKeyCreateRequest": {
        "type": "object",
        "required": ["name", "scopes"],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "example": "CI/CD Pipeline"
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "documents:read",
                "documents:write",
                "audit:read",
                "webhooks:read",
                "webhooks:write",
                "verify:read"
              ]
            }
          },
          "key_type": {
            "type": "string",
            "enum": ["Standard", "MCP"],
            "default": "Standard"
          }
        }
      },
      "ApiKeyCreateResponse": {
        "type": "object",
        "properties": {
          "key_id": { "type": "string", "format": "uuid" },
          "api_key": {
            "type": "string",
            "description": "Full plaintext API key (shown ONCE). Store securely."
          },
          "key_prefix": { "type": "string" },
          "name": { "type": "string" },
          "scopes": {
            "type": "array",
            "items": { "type": "string" }
          },
          "key_type": { "type": "string" }
        }
      },
      "AddressPrediction": {
        "type": "object",
        "properties": {
          "place_id": { "type": "string" },
          "description": { "type": "string" },
          "main_text": { "type": "string" },
          "secondary_text": { "type": "string" }
        }
      },
      "AddressAutocompleteResponse": {
        "type": "object",
        "properties": {
          "predictions": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/AddressPrediction" }
          }
        }
      },
      "AddressValidateRequest": {
        "type": "object",
        "required": ["address"],
        "properties": {
          "address": {
            "type": "object",
            "description": "Address lines for validation via Google Address Validation API.",
            "properties": {
              "regionCode": { "type": "string" },
              "addressLines": {
                "type": "array",
                "items": { "type": "string" }
              }
            }
          }
        }
      },
      "AddressValidateResponse": {
        "type": "object",
        "properties": {
          "result": {
            "type": "object",
            "properties": {
              "verdict": {
                "type": "object",
                "properties": {
                  "inputGranularity": { "type": "string" },
                  "validationGranularity": { "type": "string" },
                  "geocodeGranularity": { "type": "string" },
                  "addressComplete": { "type": "boolean" }
                }
              },
              "address": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "AddressConfirmRequest": {
        "type": "object",
        "required": ["confirmed_address"],
        "properties": {
          "confirmed_address": {
            "type": "string",
            "description": "The user-confirmed formatted address string."
          }
        }
      },
      "AddressConfirmResponse": {
        "type": "object",
        "properties": {
          "verification_level": { "type": "integer", "example": 3 },
          "confirmed_at": { "type": "string", "format": "date-time" }
        }
      },
      "PhoneSendRequest": {
        "type": "object",
        "required": ["phone"],
        "properties": {
          "phone": {
            "type": "string",
            "description": "E.164 format phone number.",
            "example": "+61412345678"
          }
        }
      },
      "PhoneSendResponse": {
        "type": "object",
        "properties": {
          "message": { "type": "string", "example": "OTP sent" },
          "expires_in": { "type": "integer", "example": 300 }
        }
      },
      "PhoneConfirmRequest": {
        "type": "object",
        "required": ["phone", "code"],
        "properties": {
          "phone": {
            "type": "string",
            "description": "E.164 format phone number.",
            "example": "+61412345678"
          },
          "code": {
            "type": "string",
            "description": "6-digit OTP.",
            "example": "123456",
            "minLength": 6,
            "maxLength": 6
          }
        }
      },
      "PhoneConfirmResponse": {
        "type": "object",
        "properties": {
          "verification_level": { "type": "integer", "example": 2 },
          "verified_at": { "type": "string", "format": "date-time" }
        }
      },
      "BankStartResponse": {
        "type": "object",
        "properties": {
          "client_secret": {
            "type": "string",
            "description": "Stripe Financial Connections client_secret for Stripe.js. Short-lived (15 min)."
          }
        }
      },
      "BankConfirmRequest": {
        "type": "object",
        "required": ["financial_connections_account_id"],
        "properties": {
          "financial_connections_account_id": {
            "type": "string",
            "description": "Stripe Financial Connections account ID returned by Stripe.js after user links account.",
            "example": "fca_abc123"
          }
        }
      },
      "BankConfirmResponse": {
        "type": "object",
        "properties": {
          "verification_level": { "type": "integer", "example": 4 },
          "verified_at": { "type": "string", "format": "date-time" }
        }
      },
      "TeamInvitation": {
        "type": "object",
        "properties": {
          "invitation_id": { "type": "string", "format": "uuid" },
          "email": { "type": "string", "format": "email" },
          "role_type": {
            "type": "string",
            "enum": ["Admin", "Certifier"]
          },
          "expires_at": { "type": "string", "format": "date-time" },
          "used_at": { "type": "string", "format": "date-time", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "TeamInviteRequest": {
        "type": "object",
        "required": ["email", "role_type"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "role_type": {
            "type": "string",
            "enum": ["Admin", "Certifier"]
          }
        }
      },
      "TeamInviteResponse": {
        "type": "object",
        "properties": {
          "invitation_id": { "type": "string", "format": "uuid" },
          "email": { "type": "string", "format": "email" },
          "expires_at": { "type": "string", "format": "date-time" }
        }
      },
      "TeamMember": {
        "type": "object",
        "properties": {
          "user_id": { "type": "string", "format": "uuid" },
          "email": { "type": "string", "format": "email" },
          "role_type": {
            "type": "string",
            "enum": ["Admin", "Certifier"]
          },
          "mfa_enabled": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "TeamMembersResponse": {
        "type": "object",
        "properties": {
          "members": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TeamMember" }
          }
        }
      }
    },
    "parameters": {
      "DocumentId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" },
        "description": "Document UUID."
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": { "type": "string", "format": "uuid" },
        "description": "Optional UUID v4 for idempotent retries. Duplicate requests with the same key return the cached 202 response."
      },
      "CursorParam": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "schema": { "type": "string" },
        "description": "Opaque pagination cursor from a previous response's next_cursor field."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Authentication required or token invalid/expired.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "UNAUTHORIZED", "message": "Missing or invalid authentication token" }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Insufficient permissions or step-up MFA required.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "FORBIDDEN", "message": "Step-up MFA required for this operation" }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found or not accessible to this account.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "NOT_FOUND", "message": "Resource not found" }
            }
          }
        }
      },
      "Conflict": {
        "description": "Resource conflict (e.g. duplicate, invalid state transition).",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded.",
        "headers": {
          "Retry-After": {
            "schema": { "type": "integer" },
            "description": "Seconds to wait before retrying."
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "RATE_LIMITED", "message": "Too many requests. Please retry after the specified interval." }
            }
          }
        }
      },
      "InternalError": {
        "description": "Internal server error.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "INTERNAL_ERROR", "message": "An unexpected error occurred" }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request validation failed.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": {
                "code": "VALIDATION_ERROR",
                "message": "Request validation failed",
                "details": { "email": ["Invalid email address"] }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    { "name": "Auth", "description": "Authentication, registration, and MFA operations." },
    { "name": "Team", "description": "Team invitation and member management." },
    { "name": "Documents", "description": "Document upload, retrieval, and lifecycle management." },
    { "name": "Relationships", "description": "Document relationship operations: revoke, supersede, amend, refer." },
    { "name": "Audit", "description": "Tamper-evident audit log query and export." },
    { "name": "Verification", "description": "Public document verification endpoint." },
    { "name": "Webhooks", "description": "Webhook endpoint registration, testing, and delivery history." },
    { "name": "Billing", "description": "Subscription plan listing and Stripe checkout/portal." },
    { "name": "API Keys", "description": "Programmatic API key management." },
    { "name": "Address", "description": "Address autocomplete and validation (Google Maps Platform)." },
    { "name": "Phone Verify", "description": "Level 2 phone verification via SMS OTP." },
    { "name": "Bank Verify", "description": "Level 4 bank account verification via Stripe Financial Connections." }
  ],
  "paths": {
    "/auth/register": {
      "post": {
        "tags": ["Auth"],
        "summary": "Register a new company",
        "description": "Creates a company and admin user, starts a 14-day trial, and returns DNS TXT verification instructions. The company enters `PendingDnsVerification` status until DNS is confirmed.",
        "operationId": "registerCompany",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RegisterRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Company and admin user created successfully.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RegisterResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "409": {
            "description": "Email, slug, or domain already exists.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "examples": {
                  "email_exists": {
                    "value": { "error": { "code": "EMAIL_EXISTS", "message": "An account with this email already exists" } }
                  },
                  "slug_exists": {
                    "value": { "error": { "code": "SLUG_EXISTS", "message": "This company slug is already taken" } }
                  },
                  "domain_exists": {
                    "value": { "error": { "code": "DOMAIN_EXISTS", "message": "This domain is already registered by an active company" } }
                  }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/auth/login": {
      "post": {
        "tags": ["Auth"],
        "summary": "Authenticate with email and password",
        "description": "Validates credentials and returns a short-lived access JWT (1 hour) and a long-lived refresh token (30 days). If MFA is required, `mfa_required: true` is returned and the caller must POST to `/auth/step-up` with a TOTP code.",
        "operationId": "loginUser",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LoginRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authentication successful.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LoginResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": {
            "description": "Invalid credentials.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": { "code": "INVALID_CREDENTIALS", "message": "Invalid email or password" } }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/auth/refresh": {
      "post": {
        "tags": ["Auth"],
        "summary": "Refresh access token",
        "description": "Exchanges a valid refresh token for a new access token and refresh token. The old refresh token is immediately invalidated (rotation).",
        "operationId": "refreshToken",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RefreshRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New tokens issued.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RefreshResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": {
            "description": "Refresh token invalid, expired, or already rotated.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/auth/verify-dns": {
      "post": {
        "tags": ["Auth"],
        "summary": "Trigger DNS TXT verification check",
        "description": "Manually triggers a DNS-over-HTTPS lookup to verify the company's domain TXT record. Rate limited to 5 calls/hour per company. On success, advances the company to `verification_level >= 1`.",
        "operationId": "verifyDns",
        "security": [{ "BearerJWT": [] }],
        "responses": {
          "200": {
            "description": "DNS check completed (verified or not).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/VerifyDnsResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/auth/step-up": {
      "post": {
        "tags": ["Auth"],
        "summary": "Verify TOTP for step-up MFA",
        "description": "Accepts a 6-digit TOTP code from the user's authenticator app. Returns a new JWT with a fresh `mfa_verified_at` claim valid for 5 minutes. Required before sensitive operations (document upload, revocation, supersede, API key creation, billing changes).",
        "operationId": "stepUpMfa",
        "security": [{ "BearerJWT": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/StepUpRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "TOTP verified. New JWT with mfa_verified_at claim issued.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StepUpResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": {
            "description": "TOTP code invalid or expired.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": { "code": "INVALID_TOTP", "message": "Invalid or expired TOTP code" } }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/team/invitations": {
      "post": {
        "tags": ["Team"],
        "summary": "Invite a team member",
        "description": "Sends an invitation email with a 32-byte crypto-random token (48-hour expiry, single-use) to the specified email address. Admin role required.",
        "operationId": "inviteTeamMember",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TeamInviteRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invitation created and email queued.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TeamInviteResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": {
            "description": "A pending invitation for this email already exists.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/team/members": {
      "get": {
        "tags": ["Team"],
        "summary": "List team members",
        "description": "Returns all active users in the authenticated company. Admin role required.",
        "operationId": "listTeamMembers",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "responses": {
          "200": {
            "description": "Team members listed.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TeamMembersResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents": {
      "get": {
        "tags": ["Documents"],
        "summary": "List documents",
        "description": "Returns a cursor-paginated list of documents for the authenticated company. Page size is 50.",
        "operationId": "listDocuments",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["Processing", "Active", "Revoked", "Superseded", "Failed"]
            },
            "description": "Filter by document status."
          },
          {
            "name": "uploaded_after",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" },
            "description": "ISO 8601 date-time; return documents uploaded after this time."
          },
          { "$ref": "#/components/parameters/CursorParam" }
        ],
        "responses": {
          "200": {
            "description": "Documents listed.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DocumentListResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "post": {
        "tags": ["Documents"],
        "summary": "Upload a document",
        "description": "Uploads a document for verification processing. Accepts `multipart/form-data` with a `file` field. Supports idempotent retries via `Idempotency-Key` header. Returns `202 Accepted` immediately; processing is asynchronous.",
        "operationId": "uploadDocument",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Document file. Max 50 MB. Allowed types: PDF, PNG, JPEG, DOC, DOCX."
                  },
                  "filename": {
                    "type": "string",
                    "description": "Optional override filename. Falls back to Content-Disposition."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Upload accepted and queued for processing.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DocumentUploadResponse" }
              }
            }
          },
          "400": {
            "description": "Invalid file, MIME type not allowed, or Gateway scan failed.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": {
            "description": "Trial limit reached (document count or date).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": { "code": "TRIAL_LIMIT_REACHED", "message": "Trial document limit exceeded" } }
              }
            }
          },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": {
            "description": "Duplicate file (SHA-256 already uploaded by this company).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": { "code": "DUPLICATE_FILE", "message": "A document with this file hash already exists" } }
              }
            }
          },
          "422": {
            "description": "File size exceeds 50 MB or MIME type not allowed.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents/{id}": {
      "get": {
        "tags": ["Documents"],
        "summary": "Get document details",
        "description": "Returns full document metadata. For Active documents, includes a time-limited (1-hour) signed R2 download URL.",
        "operationId": "getDocument",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "responses": {
          "200": {
            "description": "Document details.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DocumentDetail" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "delete": {
        "tags": ["Documents"],
        "summary": "Delete a document",
        "description": "Permanently deletes a document record and its R2 object. Only Processing or Failed documents can be deleted; Active/Revoked/Superseded documents must be revoked first. Requires step-up MFA.",
        "operationId": "deleteDocument",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "responses": {
          "200": {
            "description": "Document deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": { "type": "boolean", "example": true }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "Document cannot be deleted in its current status.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents/{id}/verification-url": {
      "get": {
        "tags": ["Documents"],
        "summary": "Get verification URL",
        "description": "Returns the public verification URL and HMAC verification ID for the document. The URL format is `https://verified.tools/verify/{hmacId}`.",
        "operationId": "getVerificationUrl",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "responses": {
          "200": {
            "description": "Verification URL.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/VerificationUrlResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents/{id}/revoke": {
      "post": {
        "tags": ["Relationships"],
        "summary": "Revoke a document",
        "description": "Permanently revokes an Active document (terminal state). Creates an audit-trail `Revokes` relationship record. Requires step-up MFA. KV verification cache is invalidated immediately.",
        "operationId": "revokeDocument",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RevokeRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Document revoked.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RevokeResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "Document is not in Active status.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents/{id}/supersede": {
      "post": {
        "tags": ["Relationships"],
        "summary": "Supersede a document",
        "description": "Records that a new document supersedes this document. The old document transitions from `Active` to `Superseded`. Both documents must be Active and belong to the same company. Requires step-up MFA.",
        "operationId": "supersedeDocument",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SupersedeRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Supersession recorded.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SupersedeResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "One or both documents are not Active.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents/{id}/amend": {
      "post": {
        "tags": ["Relationships"],
        "summary": "Amend a document",
        "description": "Records that a new document amends this document. Unlike supersession, amendment does not change either document's status — both remain Active. Requires step-up MFA.",
        "operationId": "amendDocument",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AmendRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Amendment relationship recorded.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AmendResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "One or both documents are not Active.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents/{id}/refer": {
      "post": {
        "tags": ["Relationships"],
        "summary": "Create a reference between documents",
        "description": "Records that this document refers to a target document. Both documents remain Active. Requires step-up MFA.",
        "operationId": "referDocument",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ReferRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reference relationship recorded.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ReferResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "One or both documents are not Active.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/documents/{id}/relationships": {
      "get": {
        "tags": ["Relationships"],
        "summary": "List document relationships",
        "description": "Returns all relationship records (as source or target) for the specified document.",
        "operationId": "listDocumentRelationships",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DocumentId" }
        ],
        "responses": {
          "200": {
            "description": "Relationships listed.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RelationshipsResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/audit": {
      "get": {
        "tags": ["Audit"],
        "summary": "Query audit log",
        "description": "Returns a paginated, filtered view of AuditEvent records for the authenticated company. Includes `prev_hash` and `event_hash` for client-side chain verification.",
        "operationId": "queryAuditLog",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "date_from",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" },
            "description": "ISO 8601 — return events at or after this timestamp."
          },
          {
            "name": "date_to",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" },
            "description": "ISO 8601 — return events strictly before this timestamp."
          },
          {
            "name": "event_type",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Filter by action/event type (e.g. `document.upload`)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          },
          { "$ref": "#/components/parameters/CursorParam" }
        ],
        "responses": {
          "200": {
            "description": "Audit events.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AuditListResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/audit/export.json": {
      "get": {
        "tags": ["Audit"],
        "summary": "Export audit log as JSONL",
        "description": "Streams a JSONL (newline-delimited JSON) export of AuditEvent records. The hash chain is verified before returning; the final line includes `{\"_chain_verification\": {\"valid\": true|false, \"brokenAt\": N|null}}`.",
        "operationId": "exportAuditJson",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" },
            "description": "Inclusive lower bound (created_ts)."
          },
          {
            "name": "end",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" },
            "description": "Exclusive upper bound (created_ts)."
          },
          {
            "name": "action",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Exact-match filter on action/event type."
          },
          {
            "name": "resource_type",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Exact-match filter on resource_type."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 1000 }
          }
        ],
        "responses": {
          "200": {
            "description": "JSONL stream of audit events.",
            "headers": {
              "Content-Disposition": {
                "schema": { "type": "string" },
                "example": "attachment; filename=\"audit-{company_id}-2025-01-01.jsonl\""
              }
            },
            "content": {
              "application/x-ndjson": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/audit/export.pdf": {
      "get": {
        "tags": ["Audit"],
        "summary": "Export audit log as PDF",
        "description": "Returns a formatted PDF report of audit events for the authenticated company.",
        "operationId": "exportAuditPdf",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "end",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF audit report.",
            "content": {
              "application/pdf": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/verify/{hmacId}": {
      "get": {
        "tags": ["Verification"],
        "summary": "Verify a document (public)",
        "description": "Public endpoint — no authentication required. Verifies that a document was certified by the named company. Returns HTML by default; pass `Accept: application/json` for JSON. Rate limited to 100 requests/min per IP.\n\n**eIDAS Disclosure:** Results are labelled \"Platform-Verified Integrity Check\" and do not constitute a Qualified Electronic Signature under eIDAS Regulation (EU) 910/2014.",
        "operationId": "verifyDocument",
        "security": [],
        "parameters": [
          {
            "name": "hmacId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            },
            "description": "HMAC-SHA256 verification ID (43 base64url chars)."
          }
        ],
        "responses": {
          "200": {
            "description": "Document found and verified.",
            "content": {
              "text/html": {
                "schema": { "type": "string" }
              },
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PublicVerificationResult" }
              }
            }
          },
          "404": {
            "description": "Document not found, revoked, or HMAC ID invalid.",
            "content": {
              "text/html": { "schema": { "type": "string" } },
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List webhook endpoints",
        "description": "Returns all webhook endpoints (active and inactive) for the authenticated company. The signing secret is never included in list responses.",
        "operationId": "listWebhooks",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "responses": {
          "200": {
            "description": "Webhook endpoints listed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoints": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/WebhookEndpoint" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Register a webhook endpoint",
        "description": "Creates a new webhook endpoint. The signing secret is returned ONCE in the response — store it securely. Use the secret to verify the `X-Verified-Tools-Signature: sha256=<HMAC-SHA256(body, secret)>` header on incoming deliveries.",
        "operationId": "registerWebhook",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/WebhookRegisterRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook endpoint created. Secret returned once.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/WebhookRegisterResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "tags": ["Webhooks"],
        "summary": "Delete (deactivate) a webhook endpoint",
        "description": "Soft-deletes the webhook endpoint by setting `is_active = false`. Historical delivery records are retained for audit purposes.",
        "operationId": "deleteWebhook",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "description": "Webhook endpoint UUID."
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint deactivated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deactivated": { "type": "boolean", "example": true }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "Endpoint already inactive.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Send a test delivery",
        "description": "Sends a test webhook delivery to the endpoint with a synthetic event payload. Useful for verifying endpoint connectivity and signature validation.",
        "operationId": "testWebhook",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Test delivery attempted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery_id": { "type": "string", "format": "uuid" },
                    "status": { "type": "string", "enum": ["Delivered", "Failed"] },
                    "response_status": { "type": "integer", "nullable": true }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List delivery history",
        "description": "Returns recent delivery attempts for the specified webhook endpoint, including status and retry schedule.",
        "operationId": "listWebhookDeliveries",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
          },
          { "$ref": "#/components/parameters/CursorParam" }
        ],
        "responses": {
          "200": {
            "description": "Delivery history.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/WebhookDelivery" }
                    },
                    "next_cursor": { "type": "string", "nullable": true }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/billing/plans": {
      "get": {
        "tags": ["Billing"],
        "summary": "List available subscription plans",
        "description": "Returns available subscription plans. No authentication required — plan information is public.",
        "operationId": "listBillingPlans",
        "security": [],
        "responses": {
          "200": {
            "description": "Plans listed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plans": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/BillingPlan" }
                    }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/billing/setup": {
      "post": {
        "tags": ["Billing"],
        "summary": "Create Stripe Checkout Session",
        "description": "Creates a Stripe Checkout Session for subscription setup and returns the checkout URL. Requires step-up MFA. Admin role required.",
        "operationId": "billingSetup",
        "security": [{ "BearerJWT": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BillingSetupRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout session created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BillingSetupResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/billing/portal": {
      "get": {
        "tags": ["Billing"],
        "summary": "Create Stripe Customer Portal Session",
        "description": "Returns a Stripe Customer Portal URL for self-serve subscription management. Requires step-up MFA. Admin role required.",
        "operationId": "billingPortal",
        "security": [{ "BearerJWT": [] }],
        "responses": {
          "200": {
            "description": "Portal session created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BillingPortalResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": {
            "description": "No Stripe customer ID — billing not yet set up.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/api-keys": {
      "get": {
        "tags": ["API Keys"],
        "summary": "List API keys",
        "description": "Returns all API key summaries for the company. Never returns key hashes or plaintext keys — only prefix, scopes, and metadata.",
        "operationId": "listApiKeys",
        "security": [{ "BearerJWT": [] }],
        "parameters": [
          {
            "name": "include_revoked",
            "in": "query",
            "schema": { "type": "boolean", "default": true }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": { "type": "integer", "minimum": 0, "default": 0 }
          }
        ],
        "responses": {
          "200": {
            "description": "API keys listed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ApiKeySummary" }
                    },
                    "total": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "post": {
        "tags": ["API Keys"],
        "summary": "Create an API key",
        "description": "Creates a new API key. The full key is returned ONCE in the response — store it securely. Requires step-up MFA.",
        "operationId": "createApiKey",
        "security": [{ "BearerJWT": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ApiKeyCreateRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created. Full key returned once.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiKeyCreateResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/api-keys/{id}": {
      "delete": {
        "tags": ["API Keys"],
        "summary": "Revoke an API key",
        "description": "Permanently revokes the API key. Cannot be undone. Requires step-up MFA.",
        "operationId": "revokeApiKey",
        "security": [{ "BearerJWT": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "description": "API key UUID."
          }
        ],
        "responses": {
          "200": {
            "description": "API key revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": { "type": "boolean", "example": true }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "API key already revoked.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/api-keys/{id}/rotate": {
      "post": {
        "tags": ["API Keys"],
        "summary": "Rotate an API key",
        "description": "Atomically creates a new key (inheriting name, scopes, and key_type) and revokes the old key. New plaintext key returned once. Requires step-up MFA.",
        "operationId": "rotateApiKey",
        "security": [{ "BearerJWT": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "201": {
            "description": "New key created and old key revoked. New full key returned once.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiKeyCreateResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "API key already revoked (cannot rotate).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/address/autocomplete": {
      "get": {
        "tags": ["Address"],
        "summary": "Address autocomplete",
        "description": "Proxies partial address strings to Google Places Autocomplete and returns filtered predictions. Pass a stable `session_token` UUID per autocomplete session for optimal Google billing.",
        "operationId": "addressAutocomplete",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "parameters": [
          {
            "name": "input",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "minLength": 3, "maxLength": 500 },
            "description": "Partial address string (3–500 chars)."
          },
          {
            "name": "session_token",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "description": "UUID v4 session token for Google Places billing grouping. Generate once per session."
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 5 address predictions.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AddressAutocompleteResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/address/validate": {
      "post": {
        "tags": ["Address"],
        "summary": "Validate an address",
        "description": "Validates an address via Google Address Validation API and returns geocoding and address component verdicts.",
        "operationId": "validateAddress",
        "security": [{ "BearerJWT": [] }, { "ApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AddressValidateRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Address validation result.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AddressValidateResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/verify/address/confirm": {
      "post": {
        "tags": ["Address"],
        "summary": "Confirm address for Level 3 verification",
        "description": "Records the user-confirmed postal address and advances the company to `verification_level = 3` (postal address verified).",
        "operationId": "confirmAddress",
        "security": [{ "BearerJWT": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AddressConfirmRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Address confirmed; verification level updated.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AddressConfirmResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/verify/phone/send": {
      "post": {
        "tags": ["Phone Verify"],
        "summary": "Send phone verification OTP",
        "description": "Sends a 6-digit OTP to the provided E.164 phone number via SMS. Rate limited to 3 sends per phone number per 24-hour window. OTP expires in 5 minutes.",
        "operationId": "sendPhoneOtp",
        "security": [{ "BearerJWT": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PhoneSendRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OTP sent.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PhoneSendResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/verify/phone/confirm": {
      "post": {
        "tags": ["Phone Verify"],
        "summary": "Confirm phone OTP",
        "description": "Validates the 6-digit OTP and advances the company to `verification_level = 2` (phone verified).",
        "operationId": "confirmPhoneOtp",
        "security": [{ "BearerJWT": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PhoneConfirmRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Phone verified; verification level updated.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PhoneConfirmResponse" }
              }
            }
          },
          "400": {
            "description": "Invalid or expired OTP.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": { "code": "INVALID_OTP", "message": "OTP is invalid or expired" } }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/verify/bank/start": {
      "post": {
        "tags": ["Bank Verify"],
        "summary": "Start bank account verification",
        "description": "Creates a Stripe Financial Connections session and returns the `client_secret` for Stripe.js. The session is anchored to the company's Stripe customer. Session expires in 15 minutes.",
        "operationId": "startBankVerify",
        "security": [{ "BearerJWT": [] }],
        "responses": {
          "200": {
            "description": "Stripe Financial Connections client_secret returned.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BankStartResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": {
            "description": "Company has no Stripe customer ID — billing setup required first.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/verify/bank/confirm": {
      "post": {
        "tags": ["Bank Verify"],
        "summary": "Confirm bank account verification",
        "description": "Validates the Stripe Financial Connections account ID obtained from Stripe.js and advances the company to `verification_level = 4` (bank account verified).",
        "operationId": "confirmBankVerify",
        "security": [{ "BearerJWT": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BankConfirmRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bank account verified; verification level updated.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BankConfirmResponse" }
              }
            }
          },
          "400": {
            "description": "Financial Connections account ID invalid or session expired.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    }
  }
}
