{
  "openapi": "3.0.0",
  "info": {
    "title": "Quick Enrich API",
    "description": "API for employee enrichment, contact finder, company finder, and lookup helpers.\n\n**Authentication:** Pass your API key as a Bearer token:\n`Authorization: Bearer qe_...`\n\nAPI keys are managed in the QuickEnrich dashboard under Settings → API Key.\n\n**Response envelope:** All JSON responses use `{ success, message, code, data?, meta?, errors? }`.\n\n**Credits:** Enrichment and finder endpoints consume credits when data is returned (see each endpoint). Meta may include `credits_used` and `remaining_credits`. GTM Unlimited subscribers do not receive `credits_used` or `remaining_credits` in API responses.",
    "contact": {
      "email": "admin@quickenrich.io"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://quickenrich.io",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Employees",
      "description": "Person enrichment and dataset search"
    },
    {
      "name": "Contact Finder",
      "description": "Filter-based contact search and reveal"
    },
    {
      "name": "Company Finder",
      "description": "Filter-based company search"
    },
    {
      "name": "Lookups",
      "description": "Allowed filter values (no API key required)"
    }
  ],
  "paths": {
    "/api/employees/search": {
      "get": {
        "tags": ["Employees"],
        "summary": "Enrich employee (email)",
        "description": "Search by LinkedIn URL and/or company + name. Returns one employee record. Consumes 1 credit when a real email is returned.\n\nGTM Unlimited only: `reverify=1` reverifies a stored email with Bounceban when `email_verification_date` is missing or older than 7 days. This can add about 30 seconds. Deliverable emails are returned and the verification date is updated. Undeliverable emails are removed and are not charged. Risky and unknown emails stay stored and are omitted unless `include_risky=1`. Either flag without GTM Unlimited returns 403.",
        "operationId": "employeesSearch",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "linkedin_url",
            "in": "query",
            "required": false,
            "description": "LinkedIn profile URL. Provide this OR (company_url + first_name + last_name).",
            "schema": {"type": "string", "format": "uri", "example": "https://www.linkedin.com/in/johndoe"}
          },
          {
            "name": "company_url",
            "in": "query",
            "required": false,
            "description": "Company website URL or domain (normalized server-side).",
            "schema": {"type": "string", "example": "https://example.com"}
          },
          {
            "name": "first_name",
            "in": "query",
            "required": false,
            "schema": {"type": "string", "maxLength": 255, "example": "John"}
          },
          {
            "name": "last_name",
            "in": "query",
            "required": false,
            "schema": {"type": "string", "maxLength": 255, "example": "Doe"}
          },
          {
            "name": "reverify",
            "in": "query",
            "required": false,
            "description": "GTM Unlimited only. Reverify the stored email with Bounceban when it has not been verified in the past 7 days.",
            "schema": {"type": "boolean", "default": false}
          },
          {
            "name": "include_risky",
            "in": "query",
            "required": false,
            "description": "GTM Unlimited only. With reverify, also return emails Bounceban marked risky or unknown.",
            "schema": {"type": "boolean", "default": false}
          }
        ],
        "responses": {
          "200": {
            "description": "Employee found (data is an object) or not found (data is an empty array with meta.reason EMPLOYEE_NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "allOf": [
                        {"$ref": "#/components/schemas/ApiSuccess"},
                        {
                          "type": "object",
                          "properties": {
                            "data": {"$ref": "#/components/schemas/EnrichmentEmployee"},
                            "meta": {"$ref": "#/components/schemas/CreditsMeta"}
                          }
                        }
                      ]
                    },
                    {
                      "allOf": [
                        {"$ref": "#/components/schemas/ApiSuccess"},
                        {
                          "type": "object",
                          "properties": {
                            "data": {"type": "array", "maxItems": 0, "items": {}},
                            "meta": {"$ref": "#/components/schemas/NotFoundMeta"}
                          }
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "402": {"$ref": "#/components/responses/InsufficientCredits"},
          "403": {
            "description": "Email verification on the fly is only available via GTM Unlimited.",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/ApiError"}
              }
            }
          },
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/employees/phone-search": {
      "get": {
        "tags": ["Employees"],
        "summary": "Enrich employee (phone)",
        "description": "Same lookup rules as /search. Returns phone fields (and limited identity/company fields). Consumes 1 credit when a phone is returned.",
        "operationId": "employeesPhoneSearch",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "linkedin_url",
            "in": "query",
            "required": false,
            "schema": {"type": "string", "format": "uri"}
          },
          {
            "name": "company_url",
            "in": "query",
            "required": false,
            "schema": {"type": "string"}
          },
          {
            "name": "first_name",
            "in": "query",
            "required": false,
            "schema": {"type": "string", "maxLength": 255}
          },
          {
            "name": "last_name",
            "in": "query",
            "required": false,
            "schema": {"type": "string", "maxLength": 255}
          }
        ],
        "responses": {
          "200": {
            "description": "Phone found or not found (meta.reason PHONE_NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "oneOf": [
                            {"$ref": "#/components/schemas/PhoneEnrichmentEmployee"},
                            {"type": "array", "maxItems": 0, "items": {}}
                          ]
                        },
                        "meta": {"$ref": "#/components/schemas/CreditsMeta"}
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "402": {"$ref": "#/components/responses/InsufficientCredits"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/employees/email-search": {
      "get": {
        "tags": ["Employees"],
        "summary": "Reverse email lookup",
        "description": "Lookup one employee by exact email. Consumes 1 credit when a real email is present on the returned record.",
        "operationId": "employeesEmailSearch",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": true,
            "schema": {"type": "string", "format": "email", "maxLength": 255, "example": "john.doe@example.com"}
          }
        ],
        "responses": {
          "200": {
            "description": "Employee found or not found (meta.reason EMAIL_NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "oneOf": [
                            {"$ref": "#/components/schemas/EnrichmentEmployee"},
                            {"type": "array", "maxItems": 0, "items": {}}
                          ]
                        },
                        "meta": {"$ref": "#/components/schemas/CreditsMeta"}
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "402": {"$ref": "#/components/responses/InsufficientCredits"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/employees/dataset-search": {
      "get": {
        "tags": ["Employees"],
        "summary": "Dataset search by company URL",
        "description": "Search by normalized company URL. Returns up to 20 contacts per page.\n\n**Charging:** With `title`, 1 credit per contact on the current page that has email or phone. Without `title`, flat 1 credit when the page has results.",
        "operationId": "employeesDatasetSearch",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "company_url",
            "in": "query",
            "required": true,
            "schema": {"type": "string", "example": "https://www.example.com"}
          },
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "Single title or comma-separated titles (e.g. \"CEO, CFO\").",
            "schema": {"type": "string", "maxLength": 255, "example": "CEO, CFO"}
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {"type": "integer", "minimum": 1, "default": 1}
          },
          {
            "name": "has_email",
            "in": "query",
            "required": false,
            "description": "When true, only contacts with a real email in the dataset.",
            "schema": {"type": "boolean"}
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated employees (may be empty with meta.reason EMPLOYEE_NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"$ref": "#/components/schemas/EnrichmentEmployee"}
                        },
                        "meta": {"$ref": "#/components/schemas/PaginationCreditsMeta"}
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "402": {"$ref": "#/components/responses/InsufficientCredits"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/employees/contact-finder": {
      "post": {
        "tags": ["Contact Finder"],
        "summary": "Search contacts by filters",
        "description": "Filter-based contact search. Email and phone values are masked (`has_email` / `has_phone`); use `/reveal` to unlock contact details.\n\nAt least one filter is required: a non-empty include/exclude dimension, `has_email`, or `has_phone`.\n\nConstrained dimensions (`country_code`, `industry_linkedin`, `number_of_employees`, `revenue`, `services`) must use exact values from the Lookups endpoints. Max 100 values per include/exclude list.\n\nOffset `page` pagination is limited to the first 10,000 results. To continue, pass `meta.next_cursor` as `cursor` (page is ignored when cursor is set).",
        "operationId": "contactFinderSearch",
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/ContactFinderSearchRequest"},
              "example": {
                "page": 1,
                "per_page": 10,
                "has_email": true,
                "title": {"include": ["CEO", "Chief Executive Officer"]},
                "country_code": {"include": ["US"]},
                "industry_linkedin": {"include": ["Software Development"]},
                "company_url": {"exclude": ["example.com"]}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contacts fetched (credits_used is 0; search does not charge).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"$ref": "#/components/schemas/ContactFinderEmployee"}
                        },
                        "meta": {"$ref": "#/components/schemas/PaginationCreditsMeta"}
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/employees/contact-finder/reveal": {
      "post": {
        "tags": ["Contact Finder"],
        "summary": "Reveal contact email and phone",
        "description": "Unlock email/phone for a contact from a prior search. Provide one of: `document_id`, `emp_id`, `employee_linkedin`, or (`first_name` + `last_name` + `company_url`). Consumes credits when reveal succeeds.",
        "operationId": "contactFinderReveal",
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/ContactFinderRevealRequest"},
              "examples": {
                "byDocumentId": {
                  "summary": "By document_id",
                  "value": {"document_id": "123456"}
                },
                "byLinkedIn": {
                  "summary": "By LinkedIn",
                  "value": {"employee_linkedin": "https://www.linkedin.com/in/johndoe"}
                },
                "byNameAndCompany": {
                  "summary": "By name + company",
                  "value": {
                    "first_name": "John",
                    "last_name": "Doe",
                    "company_url": "example.com"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact revealed with email and phone.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {"$ref": "#/components/schemas/ContactFinderEmployeeRevealed"},
                        "meta": {"$ref": "#/components/schemas/CreditsMeta"}
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "402": {"$ref": "#/components/responses/InsufficientCredits"},
          "404": {
            "description": "Contact not found",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/ApiError"}
              }
            }
          },
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/companies/company-finder": {
      "post": {
        "tags": ["Company Finder"],
        "summary": "Search companies by filters",
        "description": "Filter-based company search. At least one filter is required. `company_url` supports include only (exclude is rejected).\n\nConstrained dimensions (`country_code`, `number_of_employees`, `revenue`, `services`, `industry`) must use exact lookup values. Charges 1 credit per company returned on the page (unless the account has an unlimited Contact Finder subscription).\n\nOffset `page` pagination is limited to the first 10,000 results. To continue, pass `meta.next_cursor` as `cursor` (page is ignored when cursor is set). Free plans are limited to the first 5 pages and cannot use `cursor`.",
        "operationId": "companyFinderSearch",
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/CompanyFinderSearchRequest"},
              "example": {
                "page": 1,
                "per_page": 10,
                "include_full_text": false,
                "country_code": {"include": ["US"]},
                "services": {"include": ["Software Development"]},
                "company_name": {"include": ["Acme"]}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Companies fetched (may be empty with meta.reason COMPANY_NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"$ref": "#/components/schemas/CompanyFinderCompany"}
                        },
                        "meta": {"$ref": "#/components/schemas/PaginationCreditsMeta"}
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "402": {"$ref": "#/components/responses/InsufficientCredits"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/lookups/country-codes": {
      "get": {
        "tags": ["Lookups"],
        "summary": "List country codes",
        "description": "ISO 2-letter country codes for Contact/Company Finder filters. No API key required.",
        "operationId": "lookupsCountryCodes",
        "responses": {
          "200": {
            "description": "Array of country code strings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"type": "string", "example": "US"}
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/lookups/industries": {
      "get": {
        "tags": ["Lookups"],
        "summary": "List LinkedIn industries",
        "description": "Allowed values for Contact Finder `industry_linkedin`. No API key required.",
        "operationId": "lookupsIndustries",
        "responses": {
          "200": {
            "description": "Array of industry strings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"type": "string"}
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/lookups/employee-ranges": {
      "get": {
        "tags": ["Lookups"],
        "summary": "List employee count ranges",
        "description": "Allowed values for `number_of_employees`. Alias: `/api/lookups/employee-bands`. No API key required.",
        "operationId": "lookupsEmployeeRanges",
        "responses": {
          "200": {
            "description": "Array of employee range strings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"type": "string", "example": "11-50"}
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/lookups/employee-bands": {
      "get": {
        "tags": ["Lookups"],
        "summary": "List employee count ranges (alias)",
        "description": "Alias of `/api/lookups/employee-ranges`.",
        "operationId": "lookupsEmployeeBands",
        "responses": {
          "200": {
            "description": "Array of employee range strings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"type": "string"}
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/lookups/revenue-ranges": {
      "get": {
        "tags": ["Lookups"],
        "summary": "List revenue ranges",
        "description": "Allowed values for `revenue`. No API key required.",
        "operationId": "lookupsRevenueRanges",
        "responses": {
          "200": {
            "description": "Array of revenue range strings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"type": "string"}
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    },
    "/api/lookups/company-services": {
      "get": {
        "tags": ["Lookups"],
        "summary": "List or search company services",
        "description": "Without `q`, returns top services by popularity (`value` + `count`). With `q`, autocomplete search. No API key required.",
        "operationId": "lookupsCompanyServices",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Optional search query for service autocomplete.",
            "schema": {"type": "string", "example": "software"}
          }
        ],
        "responses": {
          "200": {
            "description": "Array of service objects",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ApiSuccess"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {"$ref": "#/components/schemas/CompanyServiceLookup"}
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {"$ref": "#/components/responses/RateLimited"},
          "500": {"$ref": "#/components/responses/ServerError"}
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Dashboard API key (e.g. `qe_...`). Send as `Authorization: Bearer <api_key>`."
      }
    },
    "schemas": {
      "ApiSuccess": {
        "type": "object",
        "required": ["success", "message", "code"],
        "properties": {
          "success": {"type": "boolean", "example": true},
          "message": {"type": "string", "example": "OK"},
          "code": {"type": "integer", "example": 200},
          "data": {},
          "meta": {"type": "object", "additionalProperties": true}
        }
      },
      "ApiError": {
        "type": "object",
        "required": ["success", "message"],
        "properties": {
          "success": {"type": "boolean", "example": false},
          "message": {"type": "string", "example": "Validation failed"},
          "code": {"type": "integer", "example": 422},
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {"type": "string"}
            }
          },
          "data": {},
          "meta": {"type": "object", "additionalProperties": true}
        }
      },
      "CreditsMeta": {
        "type": "object",
        "properties": {
          "credits_used": {"type": "integer", "example": 1},
          "remaining_credits": {"type": "integer", "example": 99},
          "reason": {
            "type": "string",
            "description": "Present on empty/not-found success responses.",
            "enum": ["EMPLOYEE_NOT_FOUND", "EMAIL_NOT_FOUND", "PHONE_NOT_FOUND", "COMPANY_NOT_FOUND"]
          }
        }
      },
      "NotFoundMeta": {
        "allOf": [
          {"$ref": "#/components/schemas/CreditsMeta"},
          {
            "type": "object",
            "properties": {
              "credits_used": {"type": "integer", "example": 0},
              "reason": {"type": "string", "example": "EMPLOYEE_NOT_FOUND"}
            }
          }
        ]
      },
      "PaginationCreditsMeta": {
        "type": "object",
        "properties": {
          "page": {"type": "integer", "example": 1},
          "per_page": {"type": "integer", "example": 20},
          "total": {"type": "integer", "example": 100},
          "last_page": {"type": "integer", "example": 5},
          "credits_used": {"type": "integer", "example": 1},
          "remaining_credits": {"type": "integer", "example": 99},
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque cursor for the next page. Null when there are no more results. Pass this value as `cursor` to continue past the first 10,000 rows."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results can be fetched via `cursor`."
          },
          "max_pages": {
            "type": "integer",
            "description": "Present on Free plans. Contact Finder and Company Finder search is limited to this many pages (5). Cursor pagination is not available."
          },
          "reason": {"type": "string"}
        }
      },
      "IncludeExcludeFilter": {
        "type": "object",
        "properties": {
          "include": {
            "type": "array",
            "maxItems": 100,
            "items": {"type": "string"}
          },
          "exclude": {
            "type": "array",
            "maxItems": 100,
            "items": {"type": "string"}
          }
        }
      },
      "EnrichmentEmployee": {
        "type": "object",
        "properties": {
          "first_name": {"type": "string", "example": "John"},
          "last_name": {"type": "string", "example": "Doe"},
          "title": {"type": "string", "example": "Software Engineer"},
          "email": {"type": "string", "example": "john.doe@example.com"},
          "employee_phone": {"type": "string", "example": "+1234567890"},
          "employee_phone_type": {"type": "string", "example": "direct"},
          "employee_linkedin": {"type": "string", "example": "https://linkedin.com/in/johndoe"},
          "email_verification_date": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "example": "2024-01-15"
          },
          "company_url": {"type": "string", "example": "example.com"},
          "company_name": {"type": "string", "example": "Example Corp"},
          "email_domain": {"type": "string", "example": "example.com"},
          "company_linked": {"type": "string"},
          "company_phone": {"type": "string"},
          "industry": {"type": "string"},
          "revenue": {"type": "string"},
          "employee_count": {"type": "string"},
          "address": {"type": "string"},
          "city": {"type": "string"},
          "region_code": {"type": "string"},
          "zip": {"type": "string"},
          "country_code": {"type": "string", "example": "US"}
        }
      },
      "PhoneEnrichmentEmployee": {
        "type": "object",
        "properties": {
          "first_name": {"type": "string", "nullable": true},
          "last_name": {"type": "string", "nullable": true},
          "company_name": {"type": "string", "nullable": true},
          "company_url": {"type": "string", "nullable": true},
          "employee_phone": {"type": "string", "nullable": true, "example": "+1234567890"},
          "employee_phone_type": {"type": "string", "nullable": true, "example": "direct"},
          "employee_count": {"type": "string"},
          "address": {"type": "string"},
          "city": {"type": "string"},
          "region_code": {"type": "string"},
          "zip": {"type": "string"},
          "country_code": {"type": "string"}
        }
      },
      "ContactFinderEmployee": {
        "type": "object",
        "description": "Masked contact (no email/phone values). Use reveal to unlock.",
        "properties": {
          "emp_id": {"type": "integer", "nullable": true},
          "first_name": {"type": "string"},
          "last_name": {"type": "string"},
          "title": {"type": "string"},
          "employee_linkedin": {"type": "string"},
          "has_email": {"type": "boolean"},
          "has_phone": {"type": "boolean"},
          "has_linkedin": {"type": "boolean"},
          "company_url": {"type": "string"},
          "company_name": {"type": "string"},
          "email_domain": {"type": "string"},
          "home_page_email": {"type": "string"},
          "company_linked": {"type": "string"},
          "company_phone": {"type": "string"},
          "industry": {"type": "string"},
          "revenue": {"type": "string"},
          "employee_count": {"type": "string"},
          "address1": {"type": "string"},
          "address2": {"type": "string"},
          "city": {"type": "string"},
          "region_code": {"type": "string"},
          "zip": {"type": "string"},
          "locality": {"type": "string"},
          "country_code": {"type": "string"}
        }
      },
      "ContactFinderEmployeeRevealed": {
        "allOf": [
          {"$ref": "#/components/schemas/ContactFinderEmployee"},
          {
            "type": "object",
            "properties": {
              "email": {"type": "string", "example": "john.doe@example.com"},
              "employee_phone": {"type": "string", "example": "+1234567890"},
              "employee_phone_type": {"type": "string", "example": "direct"},
              "email_verification_date": {"type": "string", "nullable": true}
            }
          }
        ]
      },
      "CompanyFinderCompany": {
        "type": "object",
        "properties": {
          "company_name": {"type": "string", "nullable": true},
          "url": {"type": "string", "nullable": true},
          "email_domain": {"type": "string", "nullable": true},
          "final_email_domain": {"type": "string", "nullable": true},
          "home_page_email": {"type": "string", "nullable": true},
          "phone": {"type": "string", "nullable": true},
          "linkedin_url": {"type": "string", "nullable": true},
          "city": {"type": "string", "nullable": true},
          "region_code": {"type": "string", "nullable": true},
          "country_code": {"type": "string", "nullable": true},
          "industry": {"type": "string", "nullable": true},
          "employee_count": {"type": "string", "nullable": true},
          "revenue": {"type": "string", "nullable": true},
          "services": {
            "type": "array",
            "items": {"type": "string"}
          },
          "service_count": {"type": "integer"},
          "home_page_text_snippet": {"type": "string", "nullable": true},
          "bio_li_snippet": {"type": "string", "nullable": true},
          "home_page_text": {
            "type": "string",
            "nullable": true,
            "description": "Only present when include_full_text=true"
          }
        }
      },
      "CompanyServiceLookup": {
        "type": "object",
        "properties": {
          "value": {"type": "string", "example": "Software Development"},
          "count": {"type": "integer", "example": 1200}
        }
      },
      "ContactFinderSearchRequest": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "Offset page. Limited to the first 10,000 results. Ignored when `cursor` is set."
          },
          "per_page": {"type": "integer", "minimum": 1, "maximum": 100, "default": 10},
          "cursor": {
            "type": "string",
            "maxLength": 2048,
            "description": "Opaque cursor from `meta.next_cursor`. Use to fetch past the first 10,000 rows. Echo unchanged."
          },
          "has_email": {"type": "boolean"},
          "has_phone": {"type": "boolean"},
          "company_name": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "company_url": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "city": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "country_code": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Exact values from GET /api/lookups/country-codes"}
            ]
          },
          "industry_linkedin": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Exact values from GET /api/lookups/industries"}
            ]
          },
          "number_of_employees": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Exact values from GET /api/lookups/employee-ranges"}
            ]
          },
          "revenue": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Exact values from GET /api/lookups/revenue-ranges"}
            ]
          },
          "title": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "locality": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "bio_li": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Full-text search against LinkedIn bio (max 500 chars per value)"}
            ]
          },
          "services": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Exact values from GET /api/lookups/company-services"}
            ]
          }
        }
      },
      "ContactFinderRevealRequest": {
        "type": "object",
        "properties": {
          "document_id": {"type": "string", "maxLength": 512},
          "emp_id": {"type": "integer", "minimum": 1},
          "employee_linkedin": {"type": "string", "maxLength": 2048},
          "first_name": {"type": "string", "maxLength": 255},
          "last_name": {"type": "string", "maxLength": 255},
          "company_url": {"type": "string", "maxLength": 2048}
        }
      },
      "CompanyFinderSearchRequest": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "Offset page. Limited to the first 10,000 results. Ignored when `cursor` is set."
          },
          "per_page": {"type": "integer", "minimum": 1, "maximum": 100, "default": 10},
          "cursor": {
            "type": "string",
            "maxLength": 2048,
            "description": "Opaque cursor from `meta.next_cursor`. Use to fetch past the first 10,000 rows. Echo unchanged."
          },
          "include_full_text": {
            "type": "boolean",
            "description": "When true, include full home_page_text on each company."
          },
          "company_name": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "company_url": {
            "type": "object",
            "description": "Include only; exclude is not supported.",
            "properties": {
              "include": {
                "type": "array",
                "maxItems": 100,
                "items": {"type": "string"}
              }
            }
          },
          "city": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "country_code": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "number_of_employees": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "revenue": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "industry": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "services": {"$ref": "#/components/schemas/IncludeExcludeFilter"},
          "home_page_text": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Full-text search against homepage content"}
            ]
          },
          "bio_li": {
            "allOf": [
              {"$ref": "#/components/schemas/IncludeExcludeFilter"},
              {"description": "Full-text search against LinkedIn bio"}
            ]
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request — invalid parameter combination",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/ApiError"},
            "example": {
              "success": false,
              "message": "Invalid search parameters. Provide either linkedin_url OR (company_url, first_name, last_name)",
              "code": 400
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid, or inactive API key",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/ApiError"},
            "examples": {
              "missing": {
                "value": {"success": false, "message": "API key is required"}
              },
              "invalid": {
                "value": {"success": false, "message": "Invalid or inactive API key"}
              }
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "Insufficient credits",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/ApiError"},
            "example": {
              "success": false,
              "message": "Insufficient credits",
              "code": 402,
              "meta": {"remaining_credits": 0}
            }
          }
        }
      },
      "ValidationError": {
        "description": "Validation failed",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/ApiError"},
            "example": {
              "success": false,
              "message": "Validation failed",
              "code": 422,
              "errors": {
                "email": ["The email field is required."]
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/ApiError"},
            "example": {
              "success": false,
              "message": "Rate limit exceeded. Try again later.",
              "code": 429
            }
          }
        }
      },
      "ServerError": {
        "description": "Upstream/search service error",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/ApiError"},
            "example": {
              "success": false,
              "message": "Search service temporarily unavailable",
              "code": 500
            }
          }
        }
      }
    }
  }
}
