{
  "openapi": "3.1.0",
  "info": {
    "title": "Cape Partners — Sniffer Agent API",
    "version": "1.0.0",
    "description": "Machine-readable API backing the Cape Partners M&A deal-flow workspace (click, humans). Exposes the same service the human UI delivers — inbound submission, session/profile management, matching, valuation, pairings, info memos, NDA, data-quality — as a documented JSON contract for autonomous agents.\n\nFIT SCORE: canonical score = deterministic(0-100) x semantic(0-1), where deterministic = revenue_fit(30) + growth_fit(40) + ebitda_fit(30). Threshold 50.\nPAIR_ID format: pair-{buyer_id}-{seller_id}.\nDiscovery: see /llms.txt, /llms-full.txt, /sitemap.xml. Follow robots.txt content signals.\nSECURITY — every session-keyed read is protected by a layered defense stack:\n  * Session UUID is a capability token in the URL PATH (a well-formed UUID-v4 only).\n  * First-party cross-origin gate: Origin/Referer must be absent or a Cape first-party host; a leaked UUID replayed from a foreign origin is refused (403).\n  * Referrer-Policy: strict-origin-when-cross-origin on all responses (no UUID in Referer).\n  * Per-IP rate limiting on confidential read endpoints (429 on burst).\n  * NDA-gated resources (/api/matched-names, /api/infomemo/*) require a server-side signature recorded in nda_signatures via POST /api/nda/sign (helper nda_signed).\n  * The API host sits behind an email-authentication access gate at the Cloudflare edge (unauthorized bots blocked before reaching the app).\nAUTH: session resources are keyed by the workspace UUID in the path; NDA-gated resources additionally require a recorded signature via POST /api/nda/sign.",
    "contact": {
      "name": "Cape Partners",
      "url": "https://www.capepartners.fr"
    }
  },
  "servers": [
    {
      "url": "https://www.capepartners.fr",
      "description": "Production (www) via Cloudflare"
    },
    {
      "url": "https://sniffer.capepartners.fr",
      "description": "Workspace host"
    },
    {
      "url": "http://localhost:3000",
      "description": "Local dev"
    }
  ],
  "tags": [
    {
      "name": "Discovery"
    },
    {
      "name": "Registration"
    },
    {
      "name": "Identity"
    },
    {
      "name": "Matching"
    },
    {
      "name": "Valuation"
    },
    {
      "name": "Pairings"
    },
    {
      "name": "Search"
    },
    {
      "name": "Activity"
    },
    {
      "name": "InfoMemos"
    },
    {
      "name": "Confidentiality"
    },
    {
      "name": "DataQuality"
    }
  ],
  "paths": {
    "/api/submit": {
      "post": {
        "summary": "Register a seller (opportunity) or buyer (investor) inbound lead and get initial matches",
        "tags": [
          "Registration"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmitResponse"
                }
              }
            }
          },
          "400": {
            "description": "Empty/Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Empty/Invalid body",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Turnstile human-verification failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Turnstile human-verification failed",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitRequest"
              }
            }
          }
        },
        "security": []
      }
    },
    "/api": {
      "get": {
        "summary": "Agent capability index: service/version/base_url, auth model, discovery links (openapi, llms, ai-plugin), and the key endpoint list. The single discoverable entry point for an autonomous agent.",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "service": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "base_url": {
                      "type": "string"
                    },
                    "auth": {
                      "type": "object"
                    },
                    "discovery": {
                      "type": "object"
                    },
                    "capabilities": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "example": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/.well-known/llms.txt": {
      "get": {
        "summary": "Serve the LLM discovery index (same content as /llms.txt) at the conventional well-known path.",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "llms.txt content"
          }
        },
        "security": []
      }
    },
    "/.well-known/ai-plugin.json": {
      "get": {
        "summary": "AI-plugin / ARD discovery manifest (points agents to the OpenAPI contract + llms + registration policy).",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "ai-plugin.json manifest"
          }
        },
        "security": []
      }
    },
    "/api/workspace/{session_id}": {
      "get": {
        "summary": "Check whether a workspace UUID already holds visitor data. The presented UUID is the capability token (ownership = the UUID you present). Guarded by cross-origin + rate-limit so a foreign host cannot probe/enumerate the identity oracle.",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceInfo"
                }
              }
            }
          },
          "403": {
            "description": "Cross-origin or guard rejection",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Guard rejection",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Workspace not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Workspace not found",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ]
      }
    },
    "/api/workspace/join": {
      "post": {
        "summary": "Create/update a workspace with visitor identity; returns the session UUID to enter the workspace",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceJoinResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing required fields",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Missing required fields",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkspaceJoinRequest"
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/session/{session_id}": {
      "get": {
        "summary": "Load full session: identity, buyer/seller profile, data-quality, and valuation",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionIdentity"
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Session not found",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Session authorization failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Guard rejection",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ]
      },
      "post": {
        "summary": "Update session identity and buyer/seller profile (COALESCE upsert keeps existing values)",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SessionIdentity"
              }
            }
          }
        }
      }
    },
    "/api/matches/{session_id}": {
      "get": {
        "summary": "Find suggested matches for the session (buyer→sellers or seller→buyers). Canonical fit score = deterministic x semantic, threshold 50. Communication-eligibility and ticket-fit gates applied.",
        "tags": [
          "Matching"
        ],
        "responses": {
          "200": {
            "description": "Ranked match list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Match"
                  },
                  "description": "Ranked matches"
                }
              }
            }
          },
          "404": {
            "description": "No buyer or seller for this session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "No buyer or seller",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Session authorization failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Guard rejection",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ]
      }
    },
    "/api/matched-names/{session_id}": {
      "get": {
        "summary": "Reveal non-redacted matched company names for a session. NDA-signature-gated.",
        "tags": [
          "Matching"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchedNames"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ],
        "security": [
          {
            "SessionToken": [],
            "NdaSigned": []
          }
        ]
      }
    },
    "/api/seller-name/{session_id}": {
      "get": {
        "summary": "Resolve an entity name by ID, but ONLY for entities within the requesting session's scope (its own entity or a pairing counterparty). NDA-gated; forbids arbitrary ID enumeration.",
        "tags": [
          "Matching"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntityName"
                }
              }
            }
          },
          "403": {
            "description": "NDA not signed or entity not scoped to this session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Not scoped / NDA required",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Entity not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Entity not found",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Entity ID to resolve (must be within this session's scope)"
          }
        ],
        "security": [
          {
            "SessionToken": [],
            "NdaSigned": []
          }
        ]
      }
    },
    "/api/valuation/{session_id}": {
      "get": {
        "summary": "Return the seller's valuation for the session. If not yet computed and revenue exists, triggers Arnold's valuation script.",
        "tags": [
          "Valuation"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Valuation"
                }
              }
            }
          },
          "404": {
            "description": "No seller for this session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "No seller found",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Session authorization failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Guard rejection",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ]
      }
    },
    "/api/pairings/{session_id}": {
      "get": {
        "summary": "Return pairings for the session (as buyer or seller), with match score, phase, valuation, and entity details.",
        "tags": [
          "Pairings"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PairingsResponse"
                }
              }
            }
          },
          "404": {
            "description": "No buyer or seller for this session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "No buyer or seller",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Session authorization failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Guard rejection",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ]
      }
    },
    "/api/pairings/create": {
      "post": {
        "summary": "Create a new pairing (pair-{buyer_id}-{seller_id}) between a buyer and seller.",
        "tags": [
          "Pairings"
        ],
        "responses": {
          "201": {
            "description": "Pairing created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PairingCreateResponse"
                }
              }
            }
          },
          "409": {
            "description": "Pairing already exists",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "pair_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "buyer_id and seller_id required",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Missing ids",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PairingCreateRequest"
              }
            }
          }
        }
      }
    },
    "/api/pairings/update_phase": {
      "post": {
        "summary": "Bulk-update deal phase for one or more pairings. Valid phases: 0_prospecting, 1_contact, 1_initial_contact, 2_nda, 2_valuation, 4_ioi, 4_loi, 5_dd, 6_negotiation, 7_closing, 8_closed, 9_abandoned.",
        "tags": [
          "Pairings"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhaseUpdateResponse"
                }
              }
            }
          },
          "400": {
            "description": "No pair_ids provided or invalid phase",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Invalid phase",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "pair_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "phase": {
                    "type": "string"
                  }
                },
                "required": [
                  "pair_ids",
                  "phase"
                ]
              }
            }
          }
        }
      }
    },
    "/api/search/{session_id}": {
      "get": {
        "summary": "Manual search of sellers and buyers by name/sector/product. \"paired\" is relative to the requesting session.",
        "tags": [
          "Search"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID"
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Search term"
          }
        ]
      }
    },
    "/api/activity/{session_id}": {
      "get": {
        "summary": "Return recent agent_event_log activity for the session.",
        "tags": [
          "Activity"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActivityResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ]
      }
    },
    "/api/infomemo/{session_id}": {
      "get": {
        "summary": "Generate/retrieve the InfoMemo markdown for the session. NDA-gated.",
        "tags": [
          "InfoMemos"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InfoMemo"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ],
        "security": [
          {
            "SessionToken": [],
            "NdaSigned": []
          }
        ]
      }
    },
    "/api/infomemo/{session_id}/download": {
      "get": {
        "summary": "Download the InfoMemo markdown with appended matched names and NDA block.",
        "tags": [
          "InfoMemos"
        ],
        "responses": {
          "200": {
            "description": "InfoMemo markdown download",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "InfoMemo not found"
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          }
        ],
        "security": [
          {
            "SessionToken": [],
            "NdaSigned": []
          }
        ]
      }
    },
    "/api/light-infomemo/{session_id}/{pair_id}": {
      "get": {
        "summary": "Generate and stream a light (Cape-branded teaser) InfoMemo PDF for a pairing scoped to the requesting session (the pairing must involve the session's buyer or seller).",
        "tags": [
          "InfoMemos"
        ],
        "responses": {
          "200": {
            "description": "PDF download",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Pairing not scoped to this session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Not scoped",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          },
          {
            "name": "pair_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/product-analysis/{session_id}/{pairing_id}": {
      "get": {
        "summary": "Return seller product analysis for a pairing scoped to the requesting session (the pairing must involve the session's buyer or seller).",
        "tags": [
          "InfoMemos"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductAnalysis"
                }
              }
            }
          },
          "403": {
            "description": "Pairing not scoped to this session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Not scoped",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No product analysis for this pairing",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "No product analysis",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace session UUID (acts as the scoped credential)"
          },
          {
            "name": "pairing_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ]
      }
    },
    "/api/nda/sign": {
      "post": {
        "summary": "Record a server-side NDA signature for a session. Unlocks NDA-gated endpoints (/matched-names, /infomemo).",
        "tags": [
          "Confidentiality"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NdaSignResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid session or missing signer_name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Invalid session identifier",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NdaSignRequest"
              }
            }
          }
        }
      },
      "security": [
        {
          "SessionToken": []
        }
      ]
    },
    "/api/dq/enqueue": {
      "post": {
        "summary": "Enqueue high-priority data-quality improvement steps for a seller or buyer for the enrichment worker.",
        "tags": [
          "DataQuality"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DqEnqueueResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DqEnqueueRequest"
              }
            }
          }
        }
      },
      "security": [
        {
          "SessionToken": []
        }
      ]
    }
  },
  "components": {
    "securitySchemes": {
      "SessionToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Session-Id",
        "description": "The workspace session UUID is a capability token carried in the URL PATH (not this header — shown here only because OpenAPI securitySchemes cannot model a path parameter as a credential). A valid request must present a well-formed UUID-v4 in the path segment {session_id} AND a first-party Origin/Referer (or none). Requests carrying a known-foreign Origin/Referer are refused 403. Per-IP rate limiting applies. All responses carry Referrer-Policy: strict-origin-when-cross-origin."
      },
      "NdaSigned": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Nda-Signed",
        "description": "Precondition (not a literal header): a server-side NDA signature for {session_id} must be recorded in the nda_signatures table via POST /api/nda/sign before NDA-gated resources (/api/matched-names, /api/infomemo/*) will serve data. Recorded signatures are enforced server-side (helper `nda_signed`), not by trusting a client header."
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "PairIds": {
        "type": "object",
        "properties": {
          "pair_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Array of pairing IDs to update"
          },
          "phase": {
            "type": "string",
            "description": "Deal phase from the phase taxonomy: 0_prospecting, 1_contact/1_initial_contact, 2_nda, 2_valuation, 4_ioi, 4_loi, 5_dd, 6_negotiation, 7_closing, 8_closed, 9_abandoned"
          },
          "buyer_id": {
            "type": "integer"
          },
          "seller_id": {
            "type": "integer"
          },
          "match_score": {
            "type": "number",
            "format": "float",
            "default": 0,
            "description": "Canonical fit score = deterministic(0-100) x semantic(0-1), threshold 50"
          }
        }
      },
      "Identity": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Contact first/last name"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "company": {
            "type": "string"
          },
          "role": {
            "type": "string"
          }
        }
      },
      "SessionIdentity": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "identity": {
            "$ref": "#/components/schemas/Identity"
          },
          "user_type": {
            "type": "string",
            "enum": [
              "investor",
              "buyer",
              "opportunity",
              "seller"
            ]
          },
          "buyer": {
            "$ref": "#/components/schemas/BuyerProfile"
          },
          "seller": {
            "$ref": "#/components/schemas/SellerProfile"
          },
          "valuation": {
            "$ref": "#/components/schemas/Valuation"
          }
        }
      },
      "BuyerProfile": {
        "type": "object",
        "properties": {
          "sector": {
            "type": "string"
          },
          "check_size_min": {
            "type": "number",
            "description": "Ticket (deal size) min, in EUR millions"
          },
          "check_size_max": {
            "type": "number",
            "description": "Ticket (deal size) max, in EUR millions"
          },
          "geography": {
            "type": "string"
          },
          "growth_target": {
            "type": "number",
            "description": "Target revenue growth, percent"
          },
          "ebitda_target": {
            "type": "number",
            "description": "Target EBITDA margin, percent"
          },
          "solution_1": {
            "type": "string"
          },
          "solution_2": {
            "type": "string"
          },
          "solution_3": {
            "type": "string"
          },
          "data_quality": {
            "type": "integer",
            "description": "Data-quality score /10"
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SellerProfile": {
        "type": "object",
        "properties": {
          "revenue": {
            "type": "number",
            "description": "Revenue in EUR millions"
          },
          "growth": {
            "type": "number",
            "description": "Revenue growth, percent"
          },
          "ebitda_margin": {
            "type": "number",
            "description": "EBITDA margin, percent"
          },
          "product": {
            "type": "string",
            "description": "Product/solution description"
          },
          "sector": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "data_quality": {
            "type": "integer",
            "description": "Data-quality score /10"
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Valuation": {
        "type": "object",
        "properties": {
          "valuation_revenue": {
            "type": "number",
            "format": "float"
          },
          "valuation_ebitda": {
            "type": "number",
            "format": "float"
          },
          "valuation_dcf": {
            "type": "number",
            "format": "float"
          },
          "valuation_conservative": {
            "type": "number",
            "format": "float"
          },
          "valuation_triangulated": {
            "type": "number",
            "format": "float"
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "no_data"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Match": {
        "type": "object",
        "description": "A ranked counterparty match. Redacted names (Company A...) until NDA reveal.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "description": "Redacted name unless NDA-gated reveal"
          },
          "fit": {
            "type": "number",
            "format": "float",
            "description": "Overall fit score"
          },
          "score": {
            "type": "number",
            "format": "float"
          },
          "revenue": {
            "type": "number"
          },
          "growth": {
            "type": "number"
          },
          "sector": {
            "type": "string"
          }
        }
      },
      "Pairing": {
        "type": "object",
        "properties": {
          "pair_id": {
            "type": "string",
            "example": "pair-12-345"
          },
          "buyer_id": {
            "type": "integer"
          },
          "seller_id": {
            "type": "integer"
          },
          "match_score": {
            "type": "number",
            "format": "float"
          },
          "phase": {
            "type": "string"
          },
          "valuation": {
            "type": "number",
            "format": "float"
          },
          "valuation_triangulated": {
            "type": "number",
            "format": "float"
          },
          "updated_at": {
            "type": "string"
          },
          "buyer_name": {
            "type": "string"
          },
          "seller_name": {
            "type": "string"
          },
          "buyer_sector": {
            "type": "string"
          },
          "seller_sector": {
            "type": "string"
          },
          "seller_revenue": {
            "type": "number"
          },
          "seller_growth": {
            "type": "number"
          },
          "seller_ebitda": {
            "type": "number"
          },
          "seller_product": {
            "type": "string"
          },
          "seller_contact_name": {
            "type": "string"
          },
          "seller_contact_email": {
            "type": "string"
          },
          "seller_data_quality": {
            "type": "integer"
          },
          "buyer_ticket_min": {
            "type": "number"
          },
          "buyer_ticket_max": {
            "type": "number"
          },
          "pairing_dbid": {
            "type": "integer"
          }
        }
      },
      "PairingsResponse": {
        "type": "object",
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "buyer",
              "seller"
            ]
          },
          "buyer_id": {
            "type": "integer"
          },
          "seller_id": {
            "type": "integer"
          },
          "buyer_name": {
            "type": "string"
          },
          "seller_name": {
            "type": "string"
          },
          "pairings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Pairing"
            }
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "SearchSeller": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "sector": {
            "type": "string"
          },
          "revenue": {
            "type": "number"
          },
          "ebitda": {
            "type": "number"
          },
          "product": {
            "type": "string"
          },
          "contact_name": {
            "type": "string"
          },
          "contact_email": {
            "type": "string"
          },
          "paired": {
            "type": "boolean",
            "description": "Pairing already exists relative to this requesting session"
          }
        }
      },
      "SearchBuyer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "sector": {
            "type": "string"
          },
          "ticket_min": {
            "type": "number"
          },
          "ticket_max": {
            "type": "number"
          },
          "contact": {
            "type": "string"
          },
          "contact_email": {
            "type": "string"
          },
          "paired": {
            "type": "boolean"
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "sellers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchSeller"
            }
          },
          "buyers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchBuyer"
            }
          },
          "total": {
            "type": "integer"
          },
          "query": {
            "type": "string"
          }
        }
      },
      "ActivityEvent": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "time": {
            "type": "string"
          }
        }
      },
      "ActivityResponse": {
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActivityEvent"
            }
          }
        }
      },
      "ProductAnalysis": {
        "type": "object",
        "properties": {
          "pairing_id": {
            "type": "string"
          },
          "website_url": {
            "type": "string"
          },
          "product_summary": {
            "type": "string"
          },
          "tech_stack": {
            "type": "string"
          },
          "quality_score": {
            "type": "number"
          },
          "buyer_fit_notes": {
            "type": "string"
          },
          "consolidation_potential": {
            "type": "string"
          },
          "last_analyzed_at": {
            "type": "string"
          },
          "buyer_solution_scope_1": {
            "type": "string"
          },
          "buyer_solution_scope_2": {
            "type": "string"
          },
          "buyer_solution_scope_3": {
            "type": "string"
          },
          "tech_stack_flags": {
            "type": "string"
          },
          "fit_score": {
            "type": "number"
          },
          "data_quality_flags": {
            "type": "string"
          }
        }
      },
      "NdaSignRequest": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "signer_name": {
            "type": "string"
          },
          "signerName": {
            "type": "string"
          },
          "company": {
            "type": "string"
          },
          "signedAt": {
            "type": "string"
          },
          "signed_at": {
            "type": "string"
          }
        },
        "required": [
          "session_id",
          "signer_name"
        ]
      },
      "NdaSignResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "session_id": {
            "type": "string"
          },
          "nda_signed": {
            "type": "boolean"
          }
        }
      },
      "PairingCreateRequest": {
        "type": "object",
        "properties": {
          "buyer_id": {
            "type": "integer"
          },
          "seller_id": {
            "type": "integer"
          },
          "match_score": {
            "type": "number",
            "default": 0
          },
          "phase": {
            "type": "string",
            "default": "0_prospecting"
          }
        },
        "required": [
          "buyer_id",
          "seller_id"
        ]
      },
      "PairingCreateResponse": {
        "type": "object",
        "properties": {
          "pair_id": {
            "type": "string"
          },
          "buyer_id": {
            "type": "integer"
          },
          "seller_id": {
            "type": "integer"
          },
          "match_score": {
            "type": "number"
          },
          "phase": {
            "type": "string"
          }
        }
      },
      "PhaseUpdateResponse": {
        "type": "object",
        "properties": {
          "updated": {
            "type": "integer"
          },
          "pair_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "phase": {
            "type": "string"
          }
        }
      },
      "DqEnqueueRequest": {
        "type": "object",
        "properties": {
          "entity_type": {
            "type": "string",
            "enum": [
              "seller",
              "buyer"
            ]
          },
          "entity_id": {
            "type": "integer"
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Default seller steps: [siren, financials, president, advisor, contacts, product]; buyer: [discovery, profile]"
          }
        },
        "required": [
          "entity_type",
          "entity_id"
        ]
      },
      "DqEnqueueResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "enqueued": {
            "type": "integer"
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "entity_type": {
            "type": "string"
          },
          "entity_id": {
            "type": "integer"
          }
        }
      },
      "SubmitRequest": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string"
          },
          "user_type": {
            "type": "string",
            "enum": [
              "investor",
              "opportunity",
              "seller"
            ]
          },
          "identity": {
            "$ref": "#/components/schemas/Identity"
          },
          "buyer": {
            "$ref": "#/components/schemas/BuyerProfile"
          },
          "seller": {
            "$ref": "#/components/schemas/SellerProfile"
          },
          "turnstileToken": {
            "type": "string",
            "description": "Cloudflare Turnstile verification token (required)"
          }
        },
        "required": [
          "user_type",
          "turnstileToken"
        ]
      },
      "SubmitResponse": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string"
          },
          "matches": {
            "type": "integer"
          },
          "match_details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Match"
            }
          },
          "summary": {
            "type": "string"
          }
        }
      },
      "WorkspaceJoinRequest": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "company": {
            "type": "string"
          }
        },
        "required": [
          "uuid",
          "name",
          "email",
          "company"
        ]
      },
      "WorkspaceJoinResponse": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string"
          },
          "uuid": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "company": {
            "type": "string"
          }
        }
      },
      "WorkspaceInfo": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "company": {
            "type": "string"
          },
          "user_type": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          }
        }
      },
      "MatchedNames": {
        "type": "object",
        "properties": {
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Non-redacted counterparty names (NDA-gated)"
          }
        }
      },
      "EntityName": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          }
        }
      },
      "InfoMemo": {
        "type": "object",
        "properties": {
          "markdown": {
            "type": "string"
          }
        }
      },
      "LightInfoMemo": {
        "type": "string",
        "format": "binary",
        "description": "Pretty/light InfoMemo PDF (Content-Type application/pdf)"
      }
    }
  }
}
