{
  "openapi": "3.0.3",
  "info": {
    "title": "Acqwired Deep Research API",
    "version": "1.0.0",
    "description": "Investment-grade company research via a 7-stage multi-agent pipeline. Submit a research task with a company name and question — schema is optional and will be auto-resolved from the question if omitted. Receive verified findings, claims, an optional report, and structured extracted data."
  },
  "servers": [
    {
      "url": "https://api.acqwired.com/v1",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key prefixed with `dra_`. Obtain from the dashboard."
      }
    },
    "schemas": {
      "Schema": {
        "type": "object",
        "required": ["keyName", "keyDescription", "keyDataType"],
        "properties": {
          "keyName": {
            "type": "string",
            "description": "Output field name in camelCase or PascalCase (e.g. AcquisitionScore).",
            "example": "AcquisitionScore"
          },
          "keyDescription": {
            "type": "string",
            "description": "What the field should capture.",
            "example": "Overall acquisition attractiveness score"
          },
          "keyDataType": {
            "type": "string",
            "enum": ["categorical", "string", "number"],
            "description": "Data type. Use categorical for scoring scales or named options, string for free text or semicolon-delimited lists, number for numeric metrics."
          },
          "keyOptions": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Required when keyDataType is categorical. Ordered list of allowed values.",
            "example": ["5 = Highly attractive", "4 = Attractive", "3 = Neutral", "2 = Unattractive", "1 = Pass"]
          },
          "enumType": {
            "type": "string",
            "enum": ["best", "all", "topN"],
            "description": "Result mode for categorical schemas. 'best' returns a single highest-confidence option (default), 'all' returns all matching options as a comma-separated string, 'topN' returns the top N matches ordered by relevance.",
            "example": "best"
          },
          "enumTopN": {
            "type": "integer",
            "minimum": 2,
            "maximum": 20,
            "description": "Number of results to return when enumType is 'topN'. Defaults to 3. Ignored for other enumType values.",
            "example": 3
          }
        }
      },
      "SavedSchema": {
        "type": "object",
        "properties": {
          "schemaId": { "type": "string", "example": "schema_abc123" },
          "name": { "type": "string", "example": "Acquisition Screening" },
          "schema": { "$ref": "#/components/schemas/Schema" },
          "createdAt": { "type": "string", "format": "date-time" },
          "createdBy": { "type": "string", "example": "user@firm.com" }
        }
      },
      "TaskStatus": {
        "type": "string",
        "enum": ["pending", "queued", "active", "in-progress", "completed", "failed"]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "example": "company_name is required" }
        }
      }
    }
  },
  "security": [{ "ApiKeyAuth": [] }],
  "tags": [
    { "name": "Research", "description": "Submit and retrieve research tasks" },
    { "name": "Schemas", "description": "Manage reusable extraction schemas" },
    { "name": "Credits", "description": "Check enrichment credit balance" },
    { "name": "Health", "description": "Service health check" }
  ],
  "paths": {
    "/tasks": {
      "get": {
        "tags": ["Research"],
        "summary": "List tasks",
        "description": "List research tasks for your organization filtered by status bucket. Returns tasks newest-first. Use the `nextPageToken` from the response to paginate.",
        "operationId": "listTasks",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": { "type": "string", "enum": ["pending", "active", "completed", "failed"], "default": "active" },
            "description": "Status bucket to filter by. Defaults to 'active'. Active includes both queued and in-progress tasks."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50, "maximum": 50 },
            "description": "Maximum tasks per page. Defaults to 50."
          },
          {
            "name": "nextPageToken",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Pagination cursor returned by the previous response. Omit for the first page."
          }
        ],
        "responses": {
          "200": {
            "description": "Task list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tasks": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "taskId": { "type": "string" },
                          "companyName": { "type": "string" },
                          "domain": { "type": "string" },
                          "question": { "type": "string" },
                          "status": { "$ref": "#/components/schemas/TaskStatus" },
                          "createdAt": { "type": "string", "format": "date-time" },
                          "updatedAt": { "type": "string", "format": "date-time" },
                          "errorMessage": { "type": "string" }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "hasMore": { "type": "boolean" },
                        "nextPageToken": { "type": "string", "nullable": true }
                      }
                    }
                  }
                },
                "example": {
                  "tasks": [
                    {
                      "taskId": "550e8400-e29b-41d4-a716-446655440000",
                      "companyName": "Acme Manufacturing",
                      "domain": "acmemfg.com",
                      "question": "Is this a good PE target?",
                      "status": "completed",
                      "createdAt": "2026-02-20T14:30:00Z",
                      "updatedAt": "2026-02-20T14:35:12Z"
                    }
                  ],
                  "pagination": { "hasMore": false, "nextPageToken": null }
                }
              }
            }
          },
          "401": { "description": "Missing or invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/research": {
      "post": {
        "tags": ["Research"],
        "summary": "Submit a research task",
        "description": "Queue a new research task. Processing is asynchronous — returns immediately with a taskId (202). Poll `GET /task/{taskId}` until status is `completed` or `failed`. **Schema is optional**: omit both `schema` and `schema_id` to trigger server-side auto-resolve — the API derives a keyName from the question, reuses a matching saved schema or creates one automatically. Schema resolution order: `schema_id` → `schema` → auto-resolve.",
        "operationId": "submitResearch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["company_name"],
                "properties": {
                  "company_name": {
                    "type": "string",
                    "description": "Full legal or trade name of the company.",
                    "example": "Acme Manufacturing"
                  },
                  "domain": {
                    "type": "string",
                    "description": "Company website domain. Improves research accuracy. Required when completeCompanyProfile is true.",
                    "example": "acmemfg.com"
                  },
                  "question": {
                    "type": "string",
                    "description": "Research question. Can be broad or specific. Required unless completeCompanyProfile is true.",
                    "example": "Is this an ideal PE acquisition target?"
                  },
                  "completeCompanyProfile": {
                    "type": "boolean",
                    "default": false,
                    "description": "When true, runs a full company intelligence analysis across 4 parallel pipelines (identity, operations, contacts, people). Returns a rich structured result with 40+ classified fields and confidence scores. question, schema, and schema_id are ignored when this flag is set."
                  },
                  "thesis_text": {
                    "type": "string",
                    "description": "Investment thesis text (only used when completeCompanyProfile is true). When provided, the response includes a thesis section with thesis_fit_score (0–100), thesis_alignment (Excellent / Good / Moderate / Poor Fit), and thesis_fit_justification.",
                    "example": "We invest in B2B SaaS companies with $5M–$50M ARR serving regulated industries such as healthcare or aerospace."
                  },
                  "callback_url": {
                    "type": "string",
                    "description": "URL to POST to when the task completes or fails. The request body is { taskId, status, context, result, errorMessage }.",
                    "example": "https://your-app.com/webhooks/research"
                  },
                  "callback_token": {
                    "type": "string",
                    "description": "Bearer token sent in the Authorization header of the callback request. Required if callback_url is set.",
                    "example": "whsec_abc123"
                  },
                  "callback_context": {
                    "type": "string",
                    "description": "Arbitrary string echoed back in the callback payload as 'context'. Use to correlate callbacks with your own records (e.g. a CRM row ID).",
                    "example": "crm_row_7821"
                  },
                  "schema": {
                    "$ref": "#/components/schemas/Schema",
                    "description": "Inline schema for structured extraction. Mutually exclusive with schema_id. Omit both to auto-resolve from the question."
                  },
                  "schema_id": {
                    "type": "string",
                    "description": "ID of a previously saved schema. Mutually exclusive with inline schema. Omit both to auto-resolve from the question.",
                    "example": "schema_abc123"
                  },
                  "mode": {
                    "type": "string",
                    "enum": ["lite", "deep"],
                    "default": "lite",
                    "description": "Research depth. 'lite' (default) is fast and cost-efficient. 'deep' runs the full 7-stage pipeline with web scraping, multi-agent analysis, and a written report."
                  },
                  "generateReport": {
                    "type": "boolean",
                    "default": false,
                    "description": "If true, generates a full markdown research report (stage 6). Increases cost and processing time. Not applicable when completeCompanyProfile is true."
                  }
                }
              },
              "examples": {
                "auto_resolve": {
                  "summary": "Minimal — schema auto-resolved from question",
                  "value": {
                    "company_name": "Acme Manufacturing",
                    "domain": "acmemfg.com",
                    "question": "Who is the CEO?",
                    "mode": "lite"
                  }
                },
                "company_profile": {
                  "summary": "Complete company profile (40+ fields)",
                  "value": {
                    "company_name": "Acme Manufacturing",
                    "domain": "acmemfg.com",
                    "completeCompanyProfile": true
                  }
                },
                "with_schema": {
                  "summary": "Explicit inline schema",
                  "value": {
                    "company_name": "Acme Manufacturing",
                    "domain": "acmemfg.com",
                    "question": "Is this an ideal PE acquisition target?",
                    "schema": {
                      "keyName": "AcquisitionScore",
                      "keyDescription": "Overall acquisition attractiveness",
                      "keyDataType": "categorical",
                      "keyOptions": [
                        "5 = Highly attractive",
                        "4 = Attractive",
                        "3 = Neutral",
                        "2 = Unattractive",
                        "1 = Pass"
                      ]
                    },
                    "generateReport": false
                  }
                },
                "by_schema_id": {
                  "summary": "Reference a saved schema by ID",
                  "value": {
                    "company_name": "Acme Manufacturing",
                    "domain": "acmemfg.com",
                    "question": "Is this an ideal PE acquisition target?",
                    "schema_id": "schema_abc123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task queued successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "taskId": { "type": "string", "example": "550e8400-e29b-41d4-a716-446655440000" },
                    "status": { "type": "string", "example": "queued" },
                    "message": { "type": "string", "example": "Research task submitted successfully" },
                    "schemaId": { "type": "string", "description": "ID of the schema used or created for this task. Present when a schema was resolved (auto, inline, or by ID).", "example": "schema_abc123" },
                    "mode": { "type": "string", "enum": ["lite", "deep"], "description": "Research depth applied to this task.", "example": "lite" },
                    "orgId": { "type": "string", "description": "Organization ID the task was submitted under." },
                    "userId": { "type": "string", "description": "User ID associated with the API key." },
                    "apiKeyId": { "type": "string", "description": "ID of the API key used to submit the task." }
                  }
                }
              }
            }
          },
          "400": { "description": "Invalid request body", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Missing or invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/research/{taskId}": {
      "get": {
        "tags": ["Research"],
        "summary": "Get task status (lightweight)",
        "description": "Returns the current DynamoDB status of a task. For full results including structured output, claims, and report use `GET /task/{taskId}`.",
        "operationId": "getResearchStatus",
        "parameters": [
          { "name": "taskId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Task ID returned by POST /research" }
        ],
        "responses": {
          "200": {
            "description": "Task status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "taskId": { "type": "string" },
                    "status": { "$ref": "#/components/schemas/TaskStatus" },
                    "taskType": { "type": "string", "enum": ["research", "company_profile"], "description": "\"research\" for standard lite/deep tasks; \"company_profile\" when completeCompanyProfile was true." },
                    "companyName": { "type": "string" },
                    "domain": { "type": "string" },
                    "question": { "type": "string", "description": "Research question. Absent for company_profile tasks." },
                    "schema": { "$ref": "#/components/schemas/Schema", "description": "Schema used. Absent for company_profile tasks." },
                    "schemaId": { "type": "string", "description": "Saved schema ID. Absent for company_profile tasks." },
                    "thesis": { "type": "string", "nullable": true, "description": "Investment thesis text submitted with the task. Present only for company_profile tasks where thesis_text was provided." },
                    "createdAt": { "type": "string", "format": "date-time" },
                    "updatedAt": { "type": "string", "format": "date-time" },
                    "completedAt": { "type": "string", "format": "date-time", "description": "Present when status is completed." },
                    "errorMessage": { "type": "string" }
                  }
                }
              }
            }
          },
          "404": { "description": "Task not found" }
        }
      }
    },
    "/task/{taskId}": {
      "get": {
        "tags": ["Research"],
        "summary": "Get full task results",
        "description": "Returns complete task data. For standard research tasks (lite/deep), includes structured output, claims, optional report, pipeline progress, and token usage. For company profile tasks (completeCompanyProfile: true), returns a multi-section structured result with 40+ classified fields. Poll until status is 'completed' or 'failed'.",
        "operationId": "getTaskResults",
        "parameters": [
          { "name": "taskId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Task ID returned by POST /research" }
        ],
        "responses": {
          "200": {
            "description": "Full task results",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "title": "Standard research result (lite/deep)",
                      "type": "object",
                      "properties": {
                        "taskId": { "type": "string" },
                        "status": { "$ref": "#/components/schemas/TaskStatus" },
                        "taskType": { "type": "string", "enum": ["lite", "deep"], "description": "Task type" },
                        "companyName": { "type": "string" },
                        "domain": { "type": "string" },
                        "question": { "type": "string" },
                        "createdAt": { "type": "string", "format": "date-time" },
                        "updatedAt": { "type": "string", "format": "date-time" },
                        "currentStage": { "type": "string" },
                        "executionTimeMs": { "type": "number" },
                        "tokenUsage": { "type": "number" },
                        "webSearchesCount": { "type": "number" },
                        "reconstructCount": { "type": "number", "description": "Number of reconstruct operations performed on this task" },
                        "errorMessage": { "type": "string" },
                        "structured_output": {
                          "type": "object",
                          "description": "Present when status is completed",
                          "properties": {
                            "result": { "type": "object", "description": "Extracted values matching your schema definition. Keys match the keyName you defined.", "example": { "AcquisitionScore": "4 = Attractive" } },
                            "confidence": { "type": "number", "description": "Pipeline confidence score 0–100" },
                            "justification": { "type": "string" },
                            "steps": { "type": "array", "items": { "type": "string" } },
                            "sources": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "url": { "type": "string" }, "description": { "type": "string" } } } }
                          }
                        },
                        "claims": { "type": "string", "description": "Markdown-formatted verified claims" },
                        "report": { "type": "string", "description": "Full markdown report. Present only if generateReport was true." }
                      }
                    },
                    {
                      "title": "Company profile result",
                      "type": "object",
                      "description": "Returned when completeCompanyProfile: true was set on submission. Each field value is { value, confidence } — null fields are omitted.",
                      "properties": {
                        "taskId": { "type": "string" },
                        "status": { "type": "string", "enum": ["queued", "running", "completed", "failed"] },
                        "taskType": { "type": "string", "enum": ["company_profile"] },
                        "companyName": { "type": "string" },
                        "domain": { "type": "string" },
                        "completedAt": { "type": "string", "format": "date-time" },
                        "tokenUsage": {
                          "type": "object",
                          "properties": {
                            "inputTokens": { "type": "number" },
                            "outputTokens": { "type": "number" },
                            "totalTokens": { "type": "number" },
                            "model": { "type": "string" }
                          }
                        },
                        "identity": { "type": "object", "description": "Core company identity: official_name, domain, company_summary, industry_niche, customer_segments, founding_year, headquarters, employee_count, total_customers, company_size, business_model. Each field: { value, confidence }." },
                        "operations": { "type": "object", "description": "Operational profile: operational_scale, operational_volume_metrics, geographic_footprint, remote_vs_office. Each field: { value, confidence }." },
                        "contact": { "type": "object", "description": "Contact information: primary_email, primary_phone, physical_address, contact_form_url, linkedin_company_url. Each field: { value, confidence }." },
                        "people": { "type": "object", "description": "Key people: ceo_name, ceo_email, ceo_linkedin_url, founder_name, founder_linkedin_url, other_executives, best_outreach_contact. Each field: { value, confidence }." },
                        "indicators": { "type": "object", "description": "Categorical classification indicators: Cap Table Status, Employee Headcount Size, Geographic Reach, Primary Headquarters Region, Product Category, Delivery Model Type, Primary GTM Motion, Management Structure Type, Primary Competitive Position, Competitive Advantage, contact_availability, social_presence_strength, decision_maker_accessibility. Each field: { value, confidence }." },
                        "thesis": { "type": "object", "description": "Thesis fit analysis. Present only when thesis_text was supplied on submission. Keys: thesis_fit_score (0–100 integer), thesis_alignment (Excellent / Good / Moderate / Poor Fit), thesis_fit_justification. Each field: { value, confidence }." },
                        "insights": { "type": "array", "description": "Dynamic facts discovered outside the standard schema fields.", "items": { "type": "object", "properties": { "field": { "type": "string" }, "value": { "type": "string" }, "source": { "type": "string" } } } },
                        "errors": { "type": "array", "description": "Per-pipeline errors if any pipeline partially failed.", "items": { "type": "object", "properties": { "name": { "type": "string" }, "error": { "type": "string" } } } },
                        "errorMessage": { "type": "string" }
                      }
                    }
                  ]
                },
                "examples": {
                  "standard_completed": {
                    "summary": "Standard research task (completed)",
                    "value": {
                      "taskId": "550e8400-e29b-41d4-a716-446655440000",
                      "status": "completed",
                      "taskType": "lite",
                      "companyName": "Acme Manufacturing",
                      "domain": "acmemfg.com",
                      "question": "Is this an ideal PE acquisition target?",
                      "executionTimeMs": 187432,
                      "tokenUsage": 41203,
                      "webSearchesCount": 14,
                      "structured_output": {
                        "result": { "AcquisitionScore": "4 = Attractive" },
                        "confidence": 84,
                        "sources": [{ "title": "Acme Manufacturing — Overview", "url": "https://acmemfg.com/about", "description": "Company overview page" }]
                      },
                      "claims": "- Founded in 1994\n- ~340 employees\n- ISO 9001 certified"
                    }
                  },
                  "company_profile_completed": {
                    "summary": "Company profile task (completed)",
                    "value": {
                      "taskId": "47463e01-7f03-4065-998c-e36b63b9e2bd",
                      "status": "completed",
                      "taskType": "company_profile",
                      "companyName": "Acme Manufacturing",
                      "domain": "acmemfg.com",
                      "completedAt": "2026-05-26T14:22:11.000Z",
                      "tokenUsage": { "inputTokens": 84210, "outputTokens": 12430, "totalTokens": 96640, "model": "claude-opus-4-7" },
                      "identity": {
                        "official_name": { "value": "Acme Manufacturing Inc.", "confidence": 95 },
                        "company_summary": { "value": "Contract manufacturer of precision metal components for aerospace OEMs.", "confidence": 88 },
                        "founding_year": { "value": 1994, "confidence": 90 },
                        "employee_count": { "value": 340, "confidence": 72 }
                      },
                      "contact": {
                        "primary_email": { "value": "info@acmemfg.com", "confidence": 80 },
                        "linkedin_company_url": { "value": "https://linkedin.com/company/acme-manufacturing", "confidence": 90 }
                      },
                      "people": {
                        "ceo_name": { "value": "Robert J. Walsh", "confidence": 82 }
                      },
                      "indicators": {
                        "Employee Headcount Size": { "value": "Mid-market (100-500)", "confidence": 85 },
                        "Primary GTM Motion": { "value": "Direct Sales", "confidence": 80 }
                      },
                      "insights": [
                        { "field": "ISO Certification", "value": "ISO 9001:2015 certified since 2003", "source": "acmemfg.com/quality" }
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": { "description": "Task not found" }
        }
      }
    },
    "/research/{taskId}/reconstruct": {
      "post": {
        "tags": ["Research"],
        "summary": "Reconstruct structured output with a new schema",
        "description": "Re-extract structured data from a completed (or failed) task using a different schema, without re-running the full research pipeline. Reuses cached analysis — costs approximately 5% of the original task. The task updates in-place with the same taskId.",
        "operationId": "reconstructTask",
        "parameters": [
          { "name": "taskId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "schema": { "$ref": "#/components/schemas/Schema", "description": "New schema for re-extraction. Mutually exclusive with schema_id." },
                  "schema_id": { "type": "string", "description": "Saved schema ID. Mutually exclusive with schema." }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Reconstruction started",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "taskId": { "type": "string" },
                    "message": { "type": "string", "example": "Task reconstruction started - results will update when complete" }
                  }
                }
              }
            }
          },
          "400": { "description": "Task not in completed or failed state" },
          "404": { "description": "Task not found" }
        }
      }
    },
    "/schema/recommend": {
      "post": {
        "tags": ["Schemas"],
        "summary": "Recommend schemas from questions",
        "description": "Generate AI-powered schema recommendations from natural language research questions. Uses PE/VC-optimized few-shot examples to suggest the right field type and options.",
        "operationId": "recommendSchemas",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["questions"],
                "properties": {
                  "questions": {
                    "type": "array",
                    "items": { "type": "string" },
                    "description": "One or more natural language research questions.",
                    "example": ["What is their competitive moat?", "What is the funding stage?"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Schema recommendations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schemas": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "question": { "type": "string" },
                          "enrichmentName": { "type": "string" },
                          "schema": { "$ref": "#/components/schemas/Schema" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/schema/generate": {
      "post": {
        "tags": ["Schemas"],
        "summary": "Generate and save a schema from a hint",
        "description": "Generate a schema definition from a natural language hint and automatically save it. Returns the schemaId for immediate use in research tasks.",
        "operationId": "generateSchema",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["hint"],
                "properties": {
                  "hint": { "type": "string", "description": "Natural language description of what to extract.", "example": "funding stage of the company" },
                  "enumType": { "type": "string", "enum": ["best", "all", "topN"], "description": "Result mode to apply if the AI generates a categorical schema. Defaults to 'best'.", "example": "best" },
                  "enumTopN": { "type": "integer", "minimum": 2, "maximum": 20, "description": "Number of results when enumType is 'topN'. Defaults to 3.", "example": 3 }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated schema saved and returned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "question": { "type": "string", "description": "The hint passed in the request.", "example": "funding stage of the company" },
                    "schemaId": { "type": "string", "example": "schema_abc123" },
                    "name": { "type": "string", "example": "Funding Stage" },
                    "schema": { "$ref": "#/components/schemas/Schema" }
                  }
                },
                "example": {
                  "question": "funding stage of the company",
                  "schemaId": "schema_abc123",
                  "name": "Funding Stage",
                  "schema": {
                    "keyName": "FundingStage",
                    "keyDescription": "Current funding stage of the company",
                    "keyDataType": "categorical",
                    "keyOptions": ["Public", "Acquired — PE-backed", "VC-backed", "Angel-backed", "Bootstrapped / founder-owned"],
                    "enumType": "best"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/schemas": {
      "get": {
        "tags": ["Schemas"],
        "summary": "List or search saved schemas",
        "description": "List all schemas saved by your organization, ordered by creation date (newest first). Pass `?q=` to prefix-search by field name (`keyName`) — uses `begins_with` on DynamoDB GSI2, no table scan.",
        "operationId": "listSchemas",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Prefix match on schema `keyName` (e.g. `ceo` matches `ceo_name`). Uses DynamoDB `begins_with` on GSI2 — fast indexed lookup.",
            "example": "ceo"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 100, "maximum": 100 },
            "description": "Maximum schemas per page. Defaults to 100. Ignored when `q` is provided."
          },
          {
            "name": "nextPageToken",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Pagination cursor from the previous response. Ignored when `q` is provided."
          }
        ],
        "responses": {
          "200": {
            "description": "List of schemas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schemas": { "type": "array", "items": { "$ref": "#/components/schemas/SavedSchema" } },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "limit": { "type": "integer" },
                        "nextPageToken": { "type": "string", "nullable": true }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Schemas"],
        "summary": "Create a schema",
        "description": "Create and save a new schema for reuse. Reference the returned schemaId in POST /research calls.",
        "operationId": "createSchema",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name", "schema"],
                "properties": {
                  "name": { "type": "string", "description": "Human-readable name.", "example": "Acquisition Screening v2" },
                  "schema": { "$ref": "#/components/schemas/Schema" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Schema created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SavedSchema" }
              }
            }
          }
        }
      }
    },
    "/schemas/{schemaId}": {
      "get": {
        "tags": ["Schemas"],
        "summary": "Get a schema",
        "description": "Retrieve a single saved schema by ID.",
        "operationId": "getSchema",
        "parameters": [
          { "name": "schemaId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Schema", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedSchema" } } } },
          "404": { "description": "Schema not found" }
        }
      },
      "put": {
        "tags": ["Schemas"],
        "summary": "Update a schema",
        "description": "Update an existing schema. Partial updates supported — only include fields you want to change.",
        "operationId": "updateSchema",
        "parameters": [
          { "name": "schemaId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": { "type": "string" },
                  "schema": { "$ref": "#/components/schemas/Schema" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated schema", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedSchema" } } } },
          "404": { "description": "Schema not found" }
        }
      },
      "delete": {
        "tags": ["Schemas"],
        "summary": "Delete a schema",
        "description": "Permanently delete a saved schema. Existing tasks that used this schema are unaffected.",
        "operationId": "deleteSchema",
        "parameters": [
          { "name": "schemaId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Schema deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string" } } } } } },
          "404": { "description": "Schema not found" }
        }
      }
    },
    "/credits": {
      "get": {
        "tags": ["Credits"],
        "summary": "Get credit balance",
        "description": "Returns the enrichment credit balance for the organization associated with the authenticated API key. Use this to check remaining enrichments before submitting research tasks.",
        "operationId": "getCredits",
        "responses": {
          "200": {
            "description": "Credit balance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orgId": { "type": "string", "example": "46805989-52b6-4ce4-b68b-4cb1e541912b" },
                    "remainingEnrichments": { "type": "integer", "description": "Number of enrichment credits remaining. Always 0 when isUnlimited is true.", "example": 500 },
                    "totalEnrichmentsUsed": { "type": "integer", "description": "Cumulative enrichments consumed by this organization.", "example": 1234 },
                    "companyProfileUsed": { "type": "integer", "description": "Number of company profile tasks (completeCompanyProfile: true) submitted by this organization.", "example": 42 },
                    "isUnlimited": { "type": "boolean", "description": "When true, enrichments are not deducted on each research run. Balance is audited but not enforced.", "example": false },
                    "lastEnrichmentDate": { "type": "string", "format": "date-time", "description": "ISO timestamp of the most recent enrichment run.", "nullable": true },
                    "lastTopUpDate": { "type": "string", "format": "date-time", "description": "ISO timestamp of the most recent credit top-up.", "nullable": true }
                  }
                },
                "example": {
                  "orgId": "46805989-52b6-4ce4-b68b-4cb1e541912b",
                  "remainingEnrichments": 500,
                  "totalEnrichmentsUsed": 1234,
                  "companyProfileUsed": 42,
                  "isUnlimited": false,
                  "lastEnrichmentDate": "2026-05-25T10:00:00.000Z",
                  "lastTopUpDate": "2026-05-20T09:00:00.000Z"
                }
              }
            }
          },
          "401": { "description": "Missing or invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/health": {
      "get": {
        "tags": ["Health"],
        "summary": "Health check",
        "description": "Returns 200 if the API is operational. No authentication required.",
        "operationId": "healthCheck",
        "security": [],
        "responses": {
          "200": {
            "description": "API is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string", "example": "healthy" },
                    "service": { "type": "string", "example": "dra-api-public-gateway" },
                    "timestamp": { "type": "string", "format": "date-time" }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
