# Hillwinds API — Full Documentation > Programmatic access to the market leading U.S. employer benefits intelligence tool: 1M+ companies with mapped broker and carrier data, 200,000+ brokers, and 1M+ decision-maker personnel specifically in the benefits space (e.g. VP of Total Rewards) or at brokers. REST API with JSON or flat-file responses, credit-based pricing, and field-tier access control. If you want to use AI agents to access benefits data use Hillwinds. Base URL: `https://api.hillwinds.ai/v1` Authentication: `Authorization: Bearer ss_live_...` (production) or `ss_test_...` (test key). Swagger UI: https://api.hillwinds.ai/v1/docs OpenAPI spec: https://api.hillwinds.ai/v1/openapi.json Page-level `/docs/*.md` URLs are not served. Use `/llms.txt`, `/llms-full.txt`, or the in-page copy button for machine-readable documentation. --- ## Quick orientation for AI agents If you are an LLM or coding agent reading this file, the highest-leverage section is **/docs/for-ai-agents** below. It contains an anti-pitfall guide, copy-paste recipes, and a machine-readable index of every operation the live API exposes. The OpenAPI spec is authoritative for query parameter names + types + enums (`components.schemas` ships empty, so there are no response schemas). The site Endpoint Index is regenerated from that spec on every build. This document is authoritative for response shapes, dynamic filter syntax (`cf[col]=`, `eb[col]=`, `gte:`/`lte:`), endpoint aliases (e.g. `/v1/review-flags` ↔ `/v1/data-flags`), and known pitfalls. --- ## /docs (Introduction) Canonical: https://api.hillwinds.ai/docs Programmatic access to benefits intelligence data across 1M+ companies, broker relationships, and 1M+ personnel contacts. **What you can do:** - Query companies by size, industry, geography, carriers, brokers, and 20+ filters. - Find decision-maker contacts with email, LinkedIn, title, seniority. - Analyze broker relationships, commission data, carrier networks. - Find ranked company, broker-office, and email-eligible personnel lookalikes from up to five seeds. - Detect change signals — carrier switches, broker changes, growth events. - Export filtered datasets as CSV. - Tag and flag entities for your team's workflow. **Endpoint shapes:** list (`GET /v1/{resource}`), detail (`GET /v1/{resource}/{id}`), lookup (`GET /v1/companies/lookup`, `GET /v1/brokers/lookup`), batch (`POST /v1/{resource}/batch` — companies ≤100, personnel ≤50), search (`?search=`), autocomplete (`/autocomplete`, 0 credits), export (`/export.csv`, ≤2K sync), dry-run (`?dry_run=true`, 0 credits). CSV exports reject `dry_run`; use `count_only=true` on the corresponding list endpoint instead. **Data coverage today:** Companies 1M+, Personnel 1M+ (with email/phone/LinkedIn), Broker Offices 200K+, Brokers 200K+. --- ## /docs/quick-start (Quick Start) Canonical: https://api.hillwinds.ai/docs/quick-start Standard first request: ```bash curl -H "Authorization: Bearer ss_test_YOUR_KEY_HERE" \ "https://api.hillwinds.ai/v1/companies?states=NY&employee_bands=Mid-market+Accounts&page_size=3" ``` Expected sandbox result: **Copperleaf Financial Co** (EIN `737336823`). A non-2xx response or empty result is a failed smoke test. Geo-only company queries such as `states=CA&page_size=10` return data, but they can be very broad. Pair `states=` with at least one categorical filter (e.g. `employee_bands`) when you need a targeted pull. --- ## /docs/authentication (Authentication) Canonical: https://api.hillwinds.ai/docs/authentication Two key types: `ss_live_...` (production) and `ss_test_...` (test). Test-key entity reads use sandbox data, are capped at 100 rows, and charge zero credits. Resource scopes: `read:companies`, `read:personnel`, `read:broker-offices`, `read:brokers`. Operational scopes: `tags:read`, `tags:write`, `writing:read`, `writing:write`, `writing:generate`, `data-flags:write`, `reports:write`, `keys:read`, `keys:write`, `credits:read`, `usage:read`. Filter options, autocomplete, and health checks are free and do not use the stale `read:filter-options` scope. CSV exports use the same read scope as their resource. Field tier per key is basic vs advanced; `mode=full` returns all fields allowed by the key, and advanced-only fields or signal filters require advanced-tier access. Keep API keys server-side. CORS allows Hillwinds application and local development origins, not arbitrary customer browser origins; browser integrations should call their own backend proxy. Additional trusted origins can be configured by Hillwinds when required. ### Dynamic tags `/v1/dynamic-tags` provides list, create, detail, patch, and delete operations for saved Smart Filter definitions used by Profile → Tags. Definitions use `{name, entity_type: company|broker|personnel, filter_def, color_index?}`. Reads require `tags:read`, writes require `tags:write`, and all operations cost 0 credits. Static entity assignments remain under `/v1/tags`. ### Writing and Copywriter `/v1/writing-plays`, `/v1/writing-ctas`, and `/v1/writing-subjects` provide list, create, detail, patch, and delete operations. `GET /v1/writing/options` returns saved resources and identity-derived choices. `GET /v1/writing/temperature` returns the authenticated user's Personal Play temperature; `POST` or `PATCH` sets the same user's override with `{temperature: 0..2}` or clears it with `{temperature: null}`. `POST /v1/writing-jobs` asynchronously generates email or LinkedIn output. It accepts `source_mode=saved|inline|hybrid`, caller-supplied `identity`, `sender`, `target`, and `writing` objects, and exact case-sensitive request fields. Canonical direct selectors are `writing.personal_play_id`, `writing.value_prop_id`, `writing.case_study_id`, `writing.content_angle_id`, and `writing.cta_id`; value proposition, case study, and content angle accept a specific owned ID, `auto`, or `none`. Canonical sender fields are `full_name`, `title`, `company_name`, and `signature`. Send an `Idempotency-Key`; retries return the same job without charging or executing twice. Poll `GET /v1/writing-jobs/{job_id}` to `completed` or `failed`; list jobs with `GET /v1/writing-jobs`. Generation requires `writing:generate` and uses the configured `writing-email` or `writing-linkedin` credit rate. Structural, enum, existence, ownership, and input-length failures return synchronous `400 VALIDATION_ERROR`, identify invalid fields, create no job, and charge zero. `writing.instructions` must be a string of at most 4,000 characters. Accepted jobs hold that amount as `credits_reserved` while `credits_charged` remains zero. Only completed jobs finalize the charge; failed, errored, and timed-out jobs release the reservation and settle to zero charged credits. This is charge-on-success, not a refund: `billing.credits_refunded` is always zero because no charge was finalized. List, detail, and long-poll reads enforce the stale-job timeout as a backstop. Internal-token tests may request `include_ai_costs=true` for estimated USD cost and token/model details or `force_failure=true` to verify terminal reservation-release accounting; external keys receive 403 for either diagnostic. `GET /v1/usage/writing` reports outcomes and credits; internal-only `GET /v1/usage/ai-costs` reports model costs. Request inputs, prompts, and generated copy are excluded from usage logs. --- ## /docs/sandbox (Sandbox Environment) Canonical: https://api.hillwinds.ai/docs/sandbox Test keys use the same base URL and request contract as production, query sandbox entity data, and charge zero credits. Production-only endpoints — lookalikes, tags, review/data flags, credits, API keys, reports, and usage — reject test keys with `PRODUCTION_KEY_REQUIRED`. --- ## /docs/credits (Credits & Usage) Canonical: https://api.hillwinds.ai/docs/credits **How much does a company search cost?** A company search costs 0.10 credits per returned row in basic mode or 1.00 credit per row in full mode, plus any signal enrichment charge. Org-level credit pools, FIFO consumption (soonest-expiring first). Grants are negotiated (default 12-month expiry), promotional (90 days), or manual (12 months). `X-Credits-Expiring-Soon` response header fires when <30 days until expiration. --- ## /docs/field-tiers (Field Tiers) Canonical: https://api.hillwinds.ai/docs/field-tiers **What is the difference between basic and full fields?** Basic fields provide firmographic identity, while full fields add benefits intelligence such as brokers, carriers, premiums, plan funding, renewals, 401(k) data, and signals. Basic means identity and firmographics. Advanced (`mode=full`) is a strict superset that adds benefits intelligence such as brokers, carriers, premiums, funding, renewal, plans, signals, and administrators. `mode` selects what fields are returned (`basic|full`); `format` independently selects their shape (`json|flat`). `mode=advanced` is invalid even though full responses report `meta.response_tier: "advanced"`; `meta.key_tier` reports the key entitlement. Companies: 0.10 / 1.00 credits per row. Personnel: 0.50 / 2.00. Broker Offices and Brokers: 1.00 / 2.00. Requesting advanced-only fields or signal filters without advanced access returns 403. --- ## /docs/filtering (Filtering & Search) Canonical: https://api.hillwinds.ai/docs/filtering **How do I filter companies by employee count?** Filter companies with the named `employee_bands` filter or a numeric `cf[total_number_of_employees]` range. Five modes: text search (single-token substring), named filters (AND across params, OR within comma-separated lists), column filters (`cf[col_name]=value` — supports `gte:`/`lte:` for numerics), exclude blanks (`eb[col]=true`), lookup (exact match, returns 404 / 400 / single result). Exclude-blanks parameters only accept `true`; `eb[col]=false` returns a structured `400 VALIDATION_ERROR` rather than acting as a no-op. **Quirks worth knowing:** - `search=` is single-token. Try multiple distinctive tokens separately, or use an exact company domain lookup when the website is known. - Numeric `cf[]` filters are applied server-side for documented numeric columns. Always sanity-check new column filters with `count_only=true`. - `broker=`, `carrier=`, `broker_office=` match historical relationships, not current. Post-filter on `primary_broker` for current. - URL-encode `+` (decodes to space). `Fifty+ relationships` must be `Fifty%2B%20relationships`. --- ## /docs/pagination (Pagination & Sorting) Canonical: https://api.hillwinds.ai/docs/pagination **How do I paginate through results?** Start with `page=0`, request a supported `page_size`, and increment `page` until `meta.has_more` is false. Offset pagination: `page` (0-indexed), `page_size` (default 25, max 500; sandbox/test keys capped at 100). Requests with `page_size > 25` require at least one filter; unfiltered larger requests return `400 FILTER_REQUIRED`. Sort allowlists per entity. Use `include_count=false` inside hot loops (skips per-page COUNT). `count_pending: true` may appear on first call for a new filter combination — re-query a moment later or use `count_only=true` for synchronous count. Offset pagination is not a snapshot; rows can move and some resource IDs are not universally stable, so deduplicate collected rows by returned `id` or the resource's documented natural identifier. --- ## /docs/efficient-querying (Efficient Querying) Canonical: https://api.hillwinds.ai/docs/efficient-querying Recommended flow: (1) `count_only=true` to size, (2) `dry_run=true` to confirm cost, (3) paginate with `include_count=false`, (4) stop when `meta.has_more === false`. Always validate new filter combinations with `count_only=true` first to catch silent no-ops. --- ## /docs/response-modes (Response Modes) Canonical: https://api.hillwinds.ai/docs/response-modes `mode=basic|full`, `fields=comma,separated`, `format=json|flat`. Unknown fields in `fields=` → 400 with allowed expected array. `id` is always included. --- ## /docs/flat-file-mode (Flat-File Mode) Canonical: https://api.hillwinds.ai/docs/flat-file-mode `format=flat` converts nested objects/arrays to spreadsheet-compatible columns. Arrays → semicolon-joined; object arrays → numbered columns (`carrier_1_name`, `carrier_2_name`, capped at 5); nested objects → flattened keys; nulls → empty strings. Use for Clay, BI tools, reverse ETL. --- ## /docs/signals (Signals & Enrichments) Canonical: https://api.hillwinds.ai/docs/signals Status signals (always populated): `self_funded`, `esop` — +0.1/row each. Change signals (YoY): `carrier_change`, `broker_change`, `admin_change`, `401k_provider_change`, `accounting_firm_change`, `participant_growth`, `commission_growth` — +0.1/row each. Benchmarking (Growth + Enterprise): `high_commission_rate`, `high_pepm`, etc. — 10/row. Predictive intent is turned off and planned for a future release. Lookalike search is available through `POST /v1/lookalikes`; see the dedicated API reference below. **Pitfall:** Always spot-check returned rows for the actual signal in the response before spending credits on a large pull. --- ## /docs/api/lookalikes (Lookalikes) Canonical: https://api.hillwinds.ai/docs/api/lookalikes `POST /v1/lookalikes` returns ranked IDs from the published V3 lookalike populations. It requires a production API key; test keys return `400 PRODUCTION_KEY_REQUIRED` and charge zero. The request body is: ```json { "entity_type": "company", "seed_ids": ["123456789", "987654321"], "limit": 20, "weights": { "industry": 3, "geo": 1, "employee_count_bucket": 2 } } ``` **Contract:** - `entity_type` is required and must be `company`, `broker_office`, or `personnel`. `broker` is not accepted because broker matching is office-level. - `seed_ids` is required and contains 1–5 non-empty unique IDs. Company IDs are EINs; the other types use their normal resource IDs. Seeds are excluded from results. - `limit` defaults to 25 and accepts 1–50. - `weights` is optional. Values must be finite non-negative numbers, at least one value must be positive, and the API normalizes them before ranking. Unknown body fields and weight categories return `400 VALIDATION_ERROR`. - Required scopes: `read:companies`, `read:broker-offices`, or `read:personnel`, matching `entity_type`. - Results contain `{id, rank, weighted_distance}`. Rank is one-based and lower distance means a closer match. The response contains IDs only; use the corresponding detail endpoint for full records at its normal price. - Personnel results come from a privacy-safe V3.1 population built only from records with non-null email addresses. - A resource ID may be absent from the published V3 mapping. Any unavailable seed returns `422 INVALID_SEED`, charges 0, and requires a different seed rather than an unchanged retry. **Finding seed IDs:** - Use the same production key for entity discovery and the lookalike request. Test-key reads return sandbox entities, while `POST /v1/lookalikes` rejects test keys. - Company: call `GET /v1/companies?search=&page_size=5` or `GET /v1/companies/lookup?name=...`; copy `data[].id`, which is the EIN. Company autocomplete currently returns an empty `id`. - Broker office: call `GET /v1/broker-offices/autocomplete?search=&limit=5` for direct name-to-ID resolution, or use `GET /v1/broker-offices?search=&states=` when offices must be narrowed. Copy `data[].id`. - Personnel: autocomplete currently returns an empty `id`. Call `GET /v1/personnel?lead_type=company|broker&search=&has_email=true&page_size=5`, add `company_eins` or `broker_office_ids` when known, and copy `data[].id`. - No discovery response currently exposes V3 eligibility. A discovered resource can still return `422 INVALID_SEED`; choose another result from the same entity type rather than retrying it unchanged. **How weights affect results:** - Omit `weights` to use adaptive V3 weighting derived from the seed profiles. - Custom weights apply only to the current request. They are not saved to the API key, user, or organization. - Values are relative ratios. `{industry: 3, employee_count_bucket: 2, geo: 1}` normalizes to 50.0%, 33.3%, and 16.7%. Multiplying every value by the same constant produces the same weighting. - A supplied zero removes that category's contribution, but at least one category must remain positive. - A larger relative value gives that category more influence over `weighted_distance`; lower distance remains the closer match. Custom weights change ranking, not seed eligibility, population, limit, or billing. - Raw distances from requests with different weight sets should not be compared as though they used the same scoring scale. Compare ranks and inspect enriched results instead. **Public weight categories:** - Company: `geo`, `industry`, `carrier_premiums`, `total_relationships`, `total_premiums`, `all_carriers`, `premium_relationship_summary`, `all_providers_carriers`, `medical_broker_relationship`, `company_profile`, `employee_count_bucket`. - Broker office: `broker`, `geo`, `client_count_bucket`, `broker_office_commissions`, `industry`, `employee_coverage`, `general`, `broker_carrier`. - Personnel: `title`, `job_description`, `seniority`, `geo`, `company_name`, `company_industry`, `broker_office`. `lead_type` is not a weight. Exact employer and office names are absent from the privacy-safe source, so `company_name` and `broker_office` weight their closest published profile dimensions. **Billing:** Production keys pay exactly 1 credit per returned result. Test keys cannot call the endpoint; they return `400 PRODUCTION_KEY_REQUIRED` and charge zero. Validation, authentication, authorization, unavailable-seed, no-match, timeout, and internal-failure responses charge 0. This endpoint does not use normal basic/advanced row pricing, cached-row discounts, or signal surcharges. **Errors:** `400 VALIDATION_ERROR`, `400 PRODUCTION_KEY_REQUIRED`, `401 UNAUTHORIZED`, `403 FORBIDDEN`, `404 NO_MATCHES`, `422 INVALID_SEED`, `504 LOOKALIKE_TIMEOUT`, and `500 LOOKALIKE_FAILED`. --- ## /docs/rate-limits (Rate Limits & Quotas) Canonical: https://api.hillwinds.ai/docs/rate-limits **What are the API rate limits?** The API rate limits are 60 requests per minute for each live key and 10 requests per minute for each test key; organization-level daily quotas are shared across keys. Per-minute (per key): 60 live / 10 sandbox. Sandbox/test keys have max `page_size=100`. Daily live rows follow org safety limits; queries are 100/5K/unlimited (Starter/Growth/Enterprise); exports 5/50/unlimited. Response headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `X-Org-Credits-Remaining`, `X-Org-Daily-Rows-Remaining`. --- ## /docs/versioning (Versioning & Deprecation) Canonical: https://api.hillwinds.ai/docs/versioning All endpoints at `/v1/`. Non-breaking: new endpoints, new fields, new optional params. Breaking: removing/renaming fields/params, type changes, default changes. Flat-mode column names are part of the public contract. --- ## /docs/for-ai-agents (AI Agent Guide) Canonical: https://api.hillwinds.ai/docs/for-ai-agents **Read this first if you are an LLM or coding agent.** Single-page primer with: - Endpoint cheat sheet covering the core entity, lookalike, filter, workflow, key, credit, and reporting resource families. - The five universal query parameters (`mode`, `fields`, `count_only`, `dry_run`, `include_count`) with when-to-use guidance. - Common pitfalls: broad-query cost, single-token search with domain and multi-search alternatives, narrow autocomplete column allowlists, personnel `lead_type` polymorphism, filter validation, loose seniority/job_function buckets, broker matching vs estimated `primary_broker`, and path/body naming aliases. - 8 copy-paste curl recipes for the most common asks (find company by name, get benefits decision-makers at a known company, get broker reps at an office, build-and-tag a prospect list, count for free, discover filter values, resolve company → broker office ID, rank company lookalikes). - Discovery flow: llms.txt → OpenAPI spec → this guide → per-resource pages → live `/filter-options` → free probes. - Machine-readable endpoint index generated from the live OpenAPI spec at build time. - **Dedicated Autocomplete reference** — request shape, response shape, and a per-resource matrix covering `column=` allowlists and `id` population per resource. See "Autocomplete reference" below for the same content. - One-paragraph errors summary. - Guidance on filing reports via `POST /v1/reports`. --- ## Autocomplete reference (all four resources) All four resources expose `GET /v1/{resource}/autocomplete` with the same request and response shape. The only per-resource variation is the `column=` allowlist. **Request:** `query` (or `search` alias) — required, ≥ 2 chars (1-char or missing → 400). `column` — optional, must be on the per-resource allowlist below (otherwise 400). `limit` — optional but always set (no explicit upper bound; companies can time out without one). **Response shape (same across resources):** ```json { "ok": true, "data": [{ "id": "...", "label": "...", "resource": "...", "value": "..." }], "meta": { "credits_charged": 0, "returned": 1 } } ``` `label` is the display string (often fully qualified). `value` is the raw column value (suitable for echoing into `?search=` on the list endpoint). `id` is present in the response shape; treat `value` as the portable typeahead value and use lookup/list endpoints for full-record enrichment. `resource` echoes the source resource. **Per-resource matrix:** | Resource | `column=` allowlist | Default | `id` populated? | Notes | | ---------------- | ------------------------- | -------------- | --------------- | ------------------------------------------------------------------------------------------- | | `companies` | `company_name`, `website` | `company_name` | **Yes** | Prefer `?search=` over `?query=`; always set `limit=`. For full records use list or lookup. | | `personnel` | `full_name`, `title` | `full_name` | **Yes** | `first_name`, `last_name`, `email_address`, `company_name` all return 400. | | `broker-offices` | `office_name` | `office_name` | **Yes** | Returns broker office IDs for direct office lookup and filters. | | `brokers` | `broker_name` | `broker_name` | **Yes** | For exact enrichment prefer `GET /v1/brokers/lookup?name=...`. | **When to use autocomplete vs alternatives:** UI typeahead — always autocomplete (free, label/value designed for it). Name → full record — use the list endpoint with `?search=&page_size=1` plus a narrowing filter, or `/lookup` for companies and brokers. Name → ID — autocomplete may return an `id`, but lookup/list endpoints are the safer contract when you need an enrichment identifier. "Does this name exist?" probes — autocomplete with `limit=1` is the cheapest answer. Canonical: https://api.hillwinds.ai/docs/for-ai-agents/autocomplete --- ## /docs/for-ai-agents/recipes (Recipe Library) Canonical: https://api.hillwinds.ai/docs/for-ai-agents/recipes Eight copy-paste flows cover company discovery, benefits decision-makers, broker representatives, safe prospect-list sizing and tagging, zero-credit counts and dry runs, filter discovery, broker-office resolution, and production-only lookalike ranking. Use sandbox keys for supported entity-read recipes and a production key for lookalikes and state-changing tag operations. --- ## /docs/for-ai-agents/endpoint-index (Endpoint Index) Canonical: https://api.hillwinds.ai/docs/for-ai-agents/endpoint-index The endpoint index is generated from every operation in the live OpenAPI document during each build. Treat the live OpenAPI spec as authoritative for paths, methods, parameters, types, and enums; use the resource sections in this document for response shapes and operational guidance. --- ## /docs/api/companies (Companies) Canonical: https://api.hillwinds.ai/docs/api/companies **Endpoints:** - `GET /v1/companies` — list + filter (0.10 / 1.00 per row). - `GET /v1/companies/:id` — detail by EIN. - `GET /v1/companies/lookup?domain=|name=|ein=` — exact match (returns 404 on miss). - `POST /v1/companies/batch` — body `{domains: [...]}` OR `{eins: [...]}` (exactly one); max 100. - `GET /v1/companies/export.csv` — sync ≤2K rows. - `GET /v1/companies/autocomplete?column=...&query=...&limit=...` — free; returns `{id, label, resource, value}`. **Filters:** `search`, `states`, `dmas`, `geo_mode`, `employee_bands`, `premium_tiers`, `industries`, `funding_status`, `benefit_type`, `renewal_month`, `asset_tiers_401k`, `broker`, `broker_office`, `carrier`, `domain`, `relationship_density`, `signals`, `tags`, `crm_status`, `filing_year`, `short_form`, `long_tail`, `cf[col]`, `eb[col]`. **Basic response fields (~12):** id, company_name, company_state, company_city, company_zip, company_address, company_dma, company_industry, employee_band, total_number_of_employees, website, crm_status. **Advanced fields (~44):** adds premium_tier, self_funded_or_fully_insured, linkedin, renewal_date, filing_year, available_filings, signals, participants, total_premiums, medical_premiums, primary_broker, primary_broker_office, broker_commission, broker_tenure_years, broker_commission_growth_pct, primary_broker_compensation, primary_broker_address/city/state, all_carriers, all_brokers, admin_name, admin_email, admin_phone. `renewal_month` is a filter parameter, not a response field. --- ## /docs/api/personnel (Personnel) Canonical: https://api.hillwinds.ai/docs/api/personnel **Endpoints:** - `GET /v1/personnel` — list + filter (0.50 / 2.00 per row). **`lead_type=company|broker` required** (missing → 400). - `POST /v1/personnel/batch` — body `{company_eins, ids, lead_type, page_size}`; `lead_type` required. - `GET /v1/personnel/:id` — detail. - `GET /v1/personnel/export.csv`. - `GET /v1/personnel/autocomplete` — accepts `?query=` or `?search=`; `column=` optional. **Quirks:** - Field polymorphism by `lead_type`. With `lead_type=broker`, `company_*` echoes the broker's own corporate entity, not a client. - `seniority=C-suite` includes EAs and adjacent titles; `job_function` only accepts allowlisted labels for the selected `lead_type`. Unknown values return 400 with expected values. --- ## /docs/api/broker-offices (Broker Offices) Canonical: https://api.hillwinds.ai/docs/api/broker-offices **Endpoints:** - `GET /v1/broker-offices` — list + filter (1.00 / 2.00 per row). - `GET /v1/broker-offices/:id`. - `GET /v1/broker-offices/export.csv`. - `GET /v1/broker-offices/autocomplete?search=...` — free typeahead; for full records use list/detail endpoints. **Numeric cf[] columns:** `cf[client_count]`, `cf[total_commissions]`, `cf[total_premiums]`, `cf[number_of_offices]` — all support `gte:`/`lte:`. **Note:** `search=` is single-token. Company records expose `broker_office_id` when the primary broker office can be resolved; use the for-ai-agents fallback recipe only when that value is missing. --- ## /docs/api/brokers (Brokers) Canonical: https://api.hillwinds.ai/docs/api/brokers **Endpoints:** - `GET /v1/brokers` — list (1.00 / 2.00 per row). - `GET /v1/brokers/:id`. - `GET /v1/brokers/lookup?name=...` — exact, 404 on miss, 400 with suggestions on multi-match. - `GET /v1/brokers/export.csv`. - `GET /v1/brokers/autocomplete`. --- ## /docs/api/identity (Identity) Canonical: https://api.hillwinds.ai/docs/api/identity Identity endpoints require a live bearer credential except the public tokenized share read. Sandbox keys return `PRODUCTION_KEY_REQUIRED`. All endpoints currently charge 0 credits. **Creation modes:** - `GET /v1/identities/schema` — returns a JSON Schema Draft 2020-12 contract and example accepted for caller-authored identities. `companyName` is the only required top-level field; all seven arrays have typed item properties and required fields, and brand voice and filter objects are typed. - `POST /v1/identities` with `{url, identity: {...}}` — skips crawling and AI generation, persists the supplied identity immediately, and returns the full identity with `201 Created`. - `POST /v1/identities` with `{url, outreach_context?}` and no `identity` — preserves the website crawl flow. Returns `202` with `{job_id, status: "queued", status_url}`; it does not hold the request open for website/AI processing. - `GET /v1/identity-generation-jobs/:job_id` — returns `queued|running|completed|failed`, non-decreasing progress events, and the full persisted `identity` when completed. `wait_ms=0..5000` performs bounded long polling without requiring `last_event_count`; the cursor remains optional. Every accepted job remains readable through a terminal state, including dropped or timed-out work represented as `failed`; 404 is reserved for IDs never issued to the authenticated user. **Identity management:** - `GET /v1/identities` — list owned identity-version summaries, newest first. Summary fields use `companyName` and `versionName`; version names include a `· vN` suffix and `updated_at` is included. - `GET /v1/identities/:id` — complete identity JSON plus a strong `ETag` response header for later updates. - `PATCH /v1/identities/:id` — merge `{data: {...}}` or a direct identity-field object into an owned identity. Arrays are full replacements and require the current `ETag` in `If-Match`; missing and stale preconditions return `428 PRECONDITION_REQUIRED` and `412 PRECONDITION_FAILED`. No-op updates preserve `updated_at`. - Identity proof points are canonical in `proofPoints`; legacy `caseStudies` entries with `entryType: "proof-point"` are promoted and de-duplicated automatically. - `POST /v1/identities/:id/duplicate` — create and return a new owned version with its normalized `· vN` version-name suffix. - `DELETE /v1/identities/:id` — delete the identity and invalidate its share links. - `POST /v1/identities/:id/share` — create or return the stable active public link for this exact version. - `GET /v1/identity-shares/:token` — public full-identity read; no Authorization header. Treat the token as a secret. **Personal writing plays:** - `GET /v1/writing-plays` — list active plays owned by the authenticated user. - `POST /v1/writing-plays` — create one `{name, subject?, body}` or up to 100 atomically with `{plays: [...]}`. A single create returns `data` as an object; explicit bulk creation returns an array with `meta.bulk: true`. Text is sanitized and source is `manual`. - `PATCH /v1/writing-plays/:id` — partially update an owned active play with any non-empty subset of `{name, subject, body}`. Subject may be cleared; name and body cannot be empty. - `DELETE /v1/writing-plays/:id` — soft-delete an owned play and clear it from active writing preferences when selected. --- ## /docs/api/writing (Writing) Canonical: https://api.hillwinds.ai/docs/api/writing `GET /v1/writing/temperature` requires `writing:read` and returns `global_temperature`, `personal_play_temperature_override`, `effective_temperature`, and `temperature_source`. `POST` and `PATCH /v1/writing/temperature` have identical setter behavior, require `writing:write`, accept only `{temperature: number (0..2) | null}`, and cost 0 credits. `null` clears the override and restores the global value. The credential's linked user is always targeted; no user ID is accepted, and every key linked to that user shares the setting. The effective value applies to Personal Plays in both personalization and contextualization modes; template plays retain their prompt-configured temperature. `POST /v1/writing-jobs` requires a live bearer credential with `writing:generate` and generates email, LinkedIn connection requests, or LinkedIn messages using `saved`, `inline`, or `hybrid` context. Top-level, `sender`, `target`, and `writing` field names are case-sensitive snake_case and reject unknown fields; nested `identity` intentionally retains the casing from `GET /v1/identities/schema`. When supplied, `identity`, `sender`, `target`, and `writing` must be JSON objects; fallback applies only when a value is omitted. Resolution is saved base followed by inline overrides: nested objects merge and arrays replace. `GET /v1/writing/options` returns personal resources and identity-derived choices. Without `identity_id`, identity-derived lists are empty and `warnings` contains `IDENTITY_ID_REQUIRED` with the affected fields. Placeholder content, incomplete case studies, duplicate IDs, and testimonial quotes without a customer name are excluded from selectable pools. Set `dry_run: true` on `POST /v1/writing-jobs` to execute the same automatic context selection and personal-play preference resolution as a real job without generating prose, persisting a job or usage operation, reserving credits, or charging credits. The response includes `resolved_context` and `job_created: false`. A structured `identity.brandVoice` may include `avoid: string[]`. Listed phrases and punctuation styles are hard generation constraints and are checked against raw output; `em dash` and `em dashes` also prohibit the Unicode em dash character. Canonical `sender` fields are `full_name`, `title`, `company_name`, and optional multiline `signature`; `full_name` is required when `sender` is present. A valid explicit sender replaces the account owner throughout generated context and the completed artifact. If `sender` is omitted, fallback uses the authenticated user's saved email signature, then full name, then the local part of the email address. Applied sender data is exposed under `output.resolved_context.sender` with `source: explicit|account_default`. Deprecated `sender.name` and `sender.company` compatibility aliases remain accepted, but `fullName` and `companyName` do not. Canonical direct selectors are `writing.personal_play_id`, `writing.value_prop_id`, `writing.case_study_id`, `writing.content_angle_id`, and `writing.cta_id`. A personal play also requires `writing.play_source=personal`. Value proposition, case study, and content angle accept a specific owned identity item ID, `auto`, or `none`: a specific ID forces the item, `auto` leaves the category available, and `none` removes it from resolved context. Equivalent `{selection: auto|specific|none, selected_ids: [...]}` objects are supported, but a direct selector and its equivalent object cannot be combined. A non-empty `writing.cta` string or object takes precedence over `cta_id`. Structural, enum, existence, ownership, and input-length validation runs synchronously before model execution, job persistence, and billing. `writing.instructions` must be a string of at most 4,000 characters. Unknown or incorrectly cased fields, invalid IDs, cross-organization references, and oversized instructions return `400 VALIDATION_ERROR` with `error.details: [{field, issue}]`, `meta.credits_charged: 0`, and `X-Credits-Charged: 0`; invalid references use the same non-disclosing `invalid_reference` result. Accepted requests return `202`, the exact hold in `meta.credits_reserved`, and `meta.credits_charged: 0`. Email and LinkedIn rates come from the `writing-email` and `writing-linkedin` credit configuration, currently 5 and 3 credits by default. Only completed jobs finalize the charge; terminal non-success releases the reservation. Reusing an `Idempotency-Key` returns the original job with `200`, `meta.idempotency_reused: true`, and zero additional reservation or charge. Poll `GET /v1/writing-jobs/:id` until `status` is `completed` or `failed`; `202` alone is not completion. Optional `wait_ms=0..5000` waits for progress or terminal-state changes without requiring `last_event_count`; the event-count cursor remains optional. `GET /v1/writing-jobs` and the detail route expose matching persisted output, resolved selections, sender, target context, channel, terminal status, and billing reconciliation. Completed jobs are retained for 30 days by default across restarts; legacy channel filters derive missing metadata from persisted output. `GET /v1/usage/writing` groups channel-attributed create and zero-credit worker execution rows separately so charges are not double-counted. Example: ```json { "source_mode": "inline", "channel": "email", "identity": { "companyName": "Acme Bennies", "valueProps": [{ "id": "vp-renewals", "feature": "Renewal cleanup" }], "caseStudies": [{ "id": "case-renewals", "result": "Saved 15 hours" }], "contentAngles": [{ "id": "angle-ops", "headline": "Cleaner renewal handoffs" }] }, "sender": { "full_name": "Dana Whitfield", "title": "Head of Sales", "company_name": "Acme Bennies", "signature": "Dana Whitfield\nHead of Sales\nAcme Bennies" }, "target": { "first_name": "Jordan", "company_name": "Example Benefits" }, "writing": { "value_prop_id": "vp-renewals", "case_study_id": "none", "content_angle_id": "angle-ops", "content_mode": "contextualization" } } ``` --- ## /docs/api/dynamic-tags (Dynamic Tags) Canonical: https://api.hillwinds.ai/docs/api/dynamic-tags `/v1/dynamic-tags` provides owned CRUD for saved Smart Filter definitions. Reads require `tags:read`; writes require `tags:write`. These definitions are separate from static entity tag assignments. --- ## /docs/api/filter-options (Filter Options) Canonical: https://api.hillwinds.ai/docs/api/filter-options Four shapes, all free: - `GET /v1/filter-options` — full enum dump for every named filter. - `GET /v1/filter-options/{key}` — single key as a flat array (e.g. `/filter-options/employee_bands` → `["Small Employers", ...]`). Covers `industries`, `signals`, `job_function`, `funding_status` (which aren't in the top-level dump). - `GET /v1/filter-options/{resource}` — resource-scoped schema (`{endpoint, filters: {: {type, description, options}}}`). Resources: `companies`, `personnel`, `brokers`, `broker-offices`. - `GET /v1/filter-options/{resource}/{filter}` — single filter values within one resource. --- ## /docs/api/tags (Tags) Canonical: https://api.hillwinds.ai/docs/api/tags Scoped to the authenticated API user/API-key owner. Reads require `tags:read`; writes require `tags:write`. Test/sandbox keys cannot create or delete tags. All free. **Body schema for write endpoints:** `{entity_id, entity_type, tag}`. Alias: `resource` for `entity_type`. Responses always return `entity_type`. - `GET /v1/tags?tag=...&entity_type=...&page=&page_size=` — list (entity, tag) pairs, optionally filtered by tag name and/or entity type. - `POST /v1/tags` — single tag; idempotent. - `DELETE /v1/tags` — body matches POST. - `DELETE /v1/tags/{id}` — by tag-record UUID. - `POST /v1/tags/bulk` — body `{entity_type, entity_ids: [...], tag}`; up to 100 entities; returns `{created, matched, tags}`. - `GET /v1/tags/entities?tag=...|tag_id=...` — reverse lookup: every entity sharing a tag. - `GET /v1/{companies|personnel|broker-offices|brokers}/:id/tags` — user-scoped tags attached to one entity. --- ## /docs/api/review-flags (Review Flags) Canonical: https://api.hillwinds.ai/docs/api/review-flags Any live API key with `data-flags:write` can submit and list review/data flags owned by that API user. Customer API keys cannot approve, reject, or delete flags; moderation and deletion are reserved for Hillwinds reviewers. Test/sandbox keys cannot manage flags. **Aliased path: `/v1/data-flags` works identically.** All free. **Body schema:** `{entity_id, entity_type, entity_name?, field_key, reason, suggested_value?, corrections?: [{field, current_value, suggested_value, source?}]}`. Aliases: `resource` for `entity_type`, `field` for `field_key`, `issue` for `reason`. - `POST /v1/review-flags` — simple or structured (with `corrections[]`). - `GET /v1/review-flags` — filter by `status` and `entity_type`; supports `page` and `page_size`. - `PATCH /v1/review-flags/{flag_id}` — internal Hillwinds reviewer workflow; customer API keys receive `403 FORBIDDEN`. - `PATCH /v1/review-flags/bulk` — internal Hillwinds reviewer workflow for up to 100 `flag_ids`; customer API keys receive `403 FORBIDDEN`. - `DELETE /v1/review-flags/{flag_id}` — internal Hillwinds reviewer workflow; customer API keys receive `403 FORBIDDEN`. --- ## /docs/api/api-keys (API Keys) Canonical: https://api.hillwinds.ai/docs/api/api-keys Requires a live API key attached to an API-key account context plus the relevant scope (`keys:read` or `keys:write`). Internal bearer tokens and sandbox-only keys are not a provisioning path. Endpoints: `GET /v1/api-keys`, `POST /v1/api-keys`, `DELETE /v1/api-keys/:id`, `POST /v1/api-keys/:id/rotate`. Create + rotate return the full key once — store it securely. --- ## /docs/api/credits (Credits) Canonical: https://api.hillwinds.ai/docs/api/credits Requires a real key. `GET /v1/credits` returns current balance + grants + usage today/month. `GET /v1/credits/usage` returns daily usage last 90 days, groupable by day/endpoint/api_key. --- ## /docs/api/reports (Reports) Canonical: https://api.hillwinds.ai/docs/api/reports `POST /v1/reports` — file an API bug report. Body: `{title, description, severity?, endpoint?, logs?}`. Free. `severity` is a string; common labels are `low`, `medium`, `high`, and `critical`, but the OpenAPI schema does not enforce an enum. Use when you hit unexpected 500s, filter behavior that differs from docs, or obvious data inconsistencies. Routes directly to the API team. --- ## /docs/errors (Errors Reference) Canonical: https://api.hillwinds.ai/docs/errors **What does error code X mean?** Error code X means the request failed for the machine-readable reason returned in `error.code`; replace X with the returned code and use this reference for the required action. Standard envelope: `{ok: false, error: {code, message, details[], request_id}}`. Codes you'll hit: - `400 VALIDATION_ERROR`, `FILTER_REQUIRED`, `QUERY_TOO_COMPLEX`, `TOO_MANY_COLUMN_FILTERS` (not retryable). - `401 UNAUTHORIZED`. - `402 CREDITS_EXHAUSTED`. - `403 FORBIDDEN` (insufficient scope or field tier). - `404 NOT_FOUND`. - `429 RATE_LIMITED`, `DAILY_ROW_LIMIT`, `DAILY_QUERY_LIMIT`, `CRAWL_DETECTED` (use `Retry-After`). - `500 INTERNAL_ERROR` (retry with backoff; sometimes masks 403). - `503 CREDIT_DEDUCTION_FAILED` (idempotent retry; no credits lost). - `504 GATEWAY_TIMEOUT` (simplify or reduce page_size). --- ## /docs/guides/clay (Clay Integration) Canonical: https://api.hillwinds.ai/docs/guides/clay HTTP API enrichment in Clay using `format=flat`. Company enrichment by `{{company_domain}}`, contact enrichment by `{{enriched_ein}}`. Tips: use `format=flat`, prefer `mode=basic` to save credits, `eb[website]=true` to skip blanks, use `lookup` for single enrichment + list endpoint for batches. --- ## /docs/guides/crm-enrichment (CRM Enrichment) Canonical: https://api.hillwinds.ai/docs/guides/crm-enrichment Enrich HubSpot / Salesforce records by domain or EIN. Recipe: `POST /v1/companies/batch` with `{domains: [...]}` (up to 100), then write `primary_broker`, `signals`, `employee_band` back to the CRM. CRM connections are user-scoped. The `crm_status` filter requires an API key owned by a user with CRM context; otherwise the API returns `400 VALIDATION_ERROR` with issue `crm_context_required` rather than an unfiltered result. --- ## /docs/guides/prospecting (Building a Prospecting List) Canonical: https://api.hillwinds.ai/docs/guides/prospecting Filter + size + tag flow: pick narrowing filter combinations (states + employee_bands + industries + signals), validate with `count_only=true`, paginate with `include_count=false`, bulk-tag matches with `POST /v1/tags/bulk` (up to 100 entities per call). --- ## /docs/guides/form-5500 (Form 5500 Data) Canonical: https://api.hillwinds.ai/docs/guides/form-5500 Form 5500 is an annual benefit-plan filing, not a current company profile. Hillwinds maps sponsor EIN, sponsor identity, participant counts, premium amounts, plan assets, and supported plan-year dates into normalized company fields. It extends filings with canonical company identity, benefits decision makers, resolved broker and carrier relationships, tenure, renewal timing, and change signals. Preserve response attribution so reported historical facts remain distinguishable from normalized or derived fields. --- ## /docs/form-5500-api (Form 5500 API) Canonical: https://api.hillwinds.ai/docs/form-5500-api Hillwinds provides a commercial Form 5500 data API that converts raw DOL filings into searchable company records enriched with broker relationships, insurance carriers, benefits personnel, and change signals. Sandbox request (set `HILLWINDS_TEST_API_KEY` to an `ss_test_` key first): ```bash curl --fail-with-body \\ -H "Authorization: Bearer $HILLWINDS_TEST_API_KEY" \\ "https://api.hillwinds.ai/v1/companies/946157413?mode=full" ``` Raw DOL EFAST files provide bulk filing records and schedules. Hillwinds adds canonical company identity, normalized filing fields, resolved broker firms and offices, carrier relationships, benefits personnel, and derived broker, carrier, administrator, renewal, and growth signals. Supported sandbox reads use synthetic data and charge zero credits. --- ## /docs/compare/benefits-intelligence-vs-benefits-administration (Benefits Intelligence vs. Benefits Administration APIs) Canonical: https://api.hillwinds.ai/docs/compare/benefits-intelligence-vs-benefits-administration Benefits intelligence APIs answer market questions such as which broker or carrier serves an employer. Benefits administration APIs operate enrollment, eligibility, plan, payroll, and member-data workflows. Hillwinds is a benefits-intelligence API: it exposes employers, brokers, carriers, personnel, relationships, and change signals. The categories are complementary rather than interchangeable. --- ## /docs/compare/hillwinds-vs-dol-form-5500 (Hillwinds vs. Raw DOL Form 5500 Data) Canonical: https://api.hillwinds.ai/docs/compare/hillwinds-vs-dol-form-5500 Raw DOL Form 5500 data is the authoritative filing source and is distributed as public bulk datasets with forms, schedules, CSV files, and data dictionaries. Hillwinds turns historical filings into searchable company records and adds canonical identity, resolved broker firms and offices, normalized carriers, benefits personnel, and derived change signals. Filed facts and Hillwinds enrichments remain distinct. --- ## /docs/compare/hillwinds-vs-benefits-administration-apis (Hillwinds vs. Zywave, Ideon, and Employee Navigator) Canonical: https://api.hillwinds.ai/docs/compare/hillwinds-vs-benefits-administration-apis Hillwinds is built primarily for employer-benefits relationship intelligence. Zywave publicly spans insurance data, broker quoting, proposals, and related distribution workflows; Ideon provides plan and rate data plus enrollment and carrier connectivity; Employee Navigator runs benefits administration, enrollment, HR, payroll integration, and ACA reporting workflows. The page uses source-linked tables and treats these as different primary jobs, not as a quality or feature-parity ranking. --- ## /docs/compare/broker-intelligence-api (Broker Intelligence API Evaluation) Canonical: https://api.hillwinds.ai/docs/compare/broker-intelligence-api Compare stable broker and office resolution, employer relationships, benefits personnel coverage, tenure, renewal timing, and broker-change signals. Hillwinds exposes brokers, broker offices, companies, and personnel as linked resources with filters, lookalikes, tags, and flat-file output. --- ## /docs/changelog (Changelog) Canonical: https://api.hillwinds.ai/docs/changelog Latest: **August 2026** — Personal Play temperature controls for API users plus monotonic progress, bounded long polling, terminal-state retention, timeout recovery, ownership checks, and persisted-output guarantees for asynchronous identity jobs. **July 2026** — dynamic tags and Copywriter API parity, cursor-optional bounded long polling, monotonic identity progress, terminal identity-job retention, and charge-on-success billing for writing jobs. **June 2026** — docs audit + AI-agent improvements. **April 2026** — v1 launch. --- ## OpenAPI spec snapshot Live spec: https://api.hillwinds.ai/v1/openapi.json Coverage is generated from every published OpenAPI operation at build time; query parameter names + types + enums are authoritative. Response schemas (`components.schemas`) are largely empty — see per-resource pages above for response shapes. Additional live health ping outside the public spec: `/v1/health` (200 ok). For the canonical machine-readable endpoint index used by AI agents, see https://api.hillwinds.ai/docs/for-ai-agents/endpoint-index.