API Reference
Complete reference for every DealSurface REST API endpoint.
All endpoints require a Bearer token in the Authorization header. Rate-limit headers are included on every response. Filter vocabularies are closed: an unrecognized value in any enumerated input returns 400 naming the accepted set — it is never ignored, and never silently matches nothing.
/api/v2/productsscope: readFreeList active products for the authenticated team, including the target segments each product sells to. Disabled product metadata is hidden unless explicitly requested.
Query parameters
| Parameter | Type | Description |
|---|---|---|
include_disabled | boolean= false | Include disabled product metadata. Defaults to false. |
Response
{
"products": [
{
"product_id": 12,
"name": "Cyber Insurance Platform",
"description": "End-to-end cyber risk assessment...",
"created_at": "2026-01-15T10:30:00Z",
"is_active": true,
"keywords": [
{ "keyword_id": 1, "name": "cyber risk", "auto_extracted": false }
],
"sources": [
{ "source_id": 5, "url": "https://example.com", "title": "Product page", "source_type": "url" }
],
"industries": [
{ "industry_name": "Fire, Marine & Casualty Insurance", "industry_description": "...", "industry_code": "6331" }
],
"segment_targeting": "selected",
"target_segments": [
{
"insurance_entity_type": "carrier",
"company_ownership": "public",
"carrier_facts": {
"lines_of_business": [
{ "line_of_business": "commercial_auto", "premium_min": null, "premium_max": null, "unknown_premium": true },
{ "line_of_business": "general_liability", "premium_min": null, "premium_max": null, "unknown_premium": true }
],
"union_premium": {
"mode": "rank",
"rank_high": 1,
"rank_low": 250,
"premium_min": null,
"premium_max": null,
"include_unknown": false
},
"licensed_footprint": {
"min": null,
"max": null,
"bands": ["super_regional", "national"],
"states_any": ["CA", "TX"]
},
"include_unknown": true
}
}
]
}
]
}Target segments
| Parameter | Type | Description |
|---|---|---|
segment_targeting | string | "all" targets every company in the product's industry universe (target_segments is empty). "selected" narrows to the listed segments.allselected |
insurance_entity_type | string | The kind of company this segment targets.carrierreinsureragencymgabrokerageunclassified_intermediaryunknown |
company_ownership | string | "any" means no ownership constraint is set.publicprivateany |
carrier_facts | object | null | Present only for carrier segments. lines_of_business combine as OR (a carrier qualifies by writing any one of them). union_premium is a separate condition measured across the union of those lines: mode "rank" carries rank_high/rank_low (1 = largest writer across the selected lines, ranked by summed premium), mode "gross" carries premium_min/premium_max in USD; include_unknown says whether carriers with no premium in any selected line still qualify. licensed_footprint carries either an exact { min, max } licensed-state-count range or ANY-match size bands (single = 1 state, regional = 2–12, super_regional = 13–34, national = 35+), never both, plus states_any — the carrier must be licensed in at least one of those states — AND-ed with the size condition; null when no footprint condition is set. The per-line premium_min/premium_max/unknown_premium fields carry the legacy per-line contract and read null / true for segments saved with union_premium. |
Segments are read-only by design: they explain what a product targets so you can judge fit; changing them is a product-setup act that stays in the UI. Appointment-count constraints on agency/MGA segments exist in product setup but are not exposed in v2.0.
Examples
curl https://dealsurface.com/api/v2/products \
-H "Authorization: Bearer $DEALSURFACE_API_KEY"/api/v2/products/:productIdscope: readFreeGet detailed information about a specific active product, including its target segments. Disabled product metadata is hidden unless explicitly requested.
Path parameters
| Parameter | Type | Description |
|---|---|---|
productIdrequired | integer | The product ID (positive integer). |
Query parameters
| Parameter | Type | Description |
|---|---|---|
include_disabled | boolean= false | Allow returning disabled product metadata. Defaults to false. |
Response
Same shape as a single product from List Products, wrapped in { product: ... }.
Examples
curl https://dealsurface.com/api/v2/products/12 \
-H "Authorization: Bearer $DEALSURFACE_API_KEY"Returns 404 if the product doesn't exist or belongs to a different team, or if it is disabled andinclude_disabledis not true. Operational endpoints remain active-only.
/api/v2/prospectsscope: readFreeList prospects for a product, one row per corporate family, with premium and complaint data. Contains the full candidate universe: assessed and unassessed families alike, labeled by assessment_status.
Population and ordering
The list contains every company family in the product's universe, assessed or not, exactly like the web app. There is no population toggle; instead each row carries assessment_status, and unassessed rows have null scores. The default relevance ordering ranks assessed rows first (by internal absolute relevance score, then evidence strength and name, with a stable tiebreak), then unassessed rows — present and last, never hidden.
Filtering, sorting, ranking, and pagination are all applied before the response page is built, so rank and the totals always agree with the page you receive — a sorted first page really is the top of the whole filtered set, not the top of one page.
Query parameters
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product to get prospects for. |
min_deal_score | integer | Minimum deal score (1-5). Implies assessed-only: unscored rows can't match a score floor.12345 |
min_buying_signal | float | Minimum buying signal score (0.0-1.0). Same assessed-only implication as min_deal_score. |
assessment_status | string list | Optional narrowing to one or more assessment buckets (comma-separated).assessedpending_scoreunassessed |
entity_type | string list | Match families whose companies include any of these insurance entity types (comma-separated). "unknown" matches unclassified companies.carrierreinsureragencymgabrokerageunclassified_intermediaryunknown |
ownership | string= any | Filter on family ownership. Distinct from include_private, which is an entitlement gate.publicprivateany |
include_private | boolean= false | Include private and unknown-classification prospects when your team is entitled to private-company data. An entitlement gate, not an ownership filter. |
min_premium | number | Minimum annual premium in USD. Applies per line of business when lines_of_business is supplied, otherwise to total all-lines premium. |
max_premium | number | Maximum annual premium in USD. Same per-line versus total rule as min_premium. |
lines_of_business | string list | Comma-separated NAIC line-of-business keys. Matches families writing ANY of them. The full 30-key filterable vocabulary is printed below. |
min_licensed_states | integer | Minimum family-union licensed-state footprint (0-50). |
states_licensed_in | string list | Comma-separated two-letter state codes. Matches families licensed in ALL of them. The 50-state vocabulary is printed below. |
q | string | Name search over family name, ticker, aliases, and subsidiary names. q=Travelers Casualty returns the Travelers family row — useful when you were handed a name, not a ticker. |
include_children | boolean= false | Inline each family's subsidiaries with the list rows (same shape as hierarchy.children). Forces limit to 50 or fewer. |
sort_by | string= relevance | Sort key. relevance (default) is the fixed assessed-first ordering above. premium_line requires exactly one lines_of_business value (400 otherwise). Any non-relevance key sorts on that value with the relevance tuple as tiebreak, so paging is deterministic. Rows missing the sort value sort last, never as zero.relevancedeal_scorebuying_signalpremium_totalpremium_linecomplaint_indexlicensed_state_count |
sort_dir | string | Sort direction; defaults to the sensible direction per key (descending for scores, premium, complaint index, and footprint).ascdesc |
limit | integer= 100 | Max results to return (1-500). |
offset | integer= 0 | Pagination offset. |
Insurance filters are strict
A family with no premium data does not match a premium filter. On a product that targets more than one industry, applying an insurance filter therefore returns only the insurance prospects — that is the intended result, not data loss. excluded_no_data_groups counts the families dropped because they lacked the data a filter required, so thin data is visible rather than invisible.
Lines match any; states match all. “Writes commercial auto or homeowners” is the natural reading for lines. For states, “licensed in both TX and CA” is the question reps actually ask — nearly every carrier is licensed in at least one of any two states. (The web app uses any-match for states; the API deliberately diverges.)
Line-of-business vocabulary (43 filterable keys)
accident_healthaggregate_write_insaircraftallied_linesannuityboiler_machineryburglary_theftcommercial_autocommercial_multiple_perilcreditearthquakeexcess_workers_compensationfarmownersfederal_floodfidelityfinancial_guarantyfiregeneral_liabilitygroup_accident_healthgroup_annuitygroup_lifehomeownersindividual_accident_healthindividual_annuityindividual_lifeinland_marineinternationallifelong_term_caremedical_professional_liabilitymedicare_supplementmortgage_guarantymultiple_peril_cropocean_marinepet_insuranceprivate_cropprivate_floodprivate_passenger_autoproducts_liabilitysuretytitlewarrantyworkers_compensationNAIC statement lines plus CIS policy-type lines (individual / group life, individual / group annuity, individual / group accident & health, medicare_supplement, long_term_care). The life, annuity and accident_health keys are roll-ups of their policy-type lines and remain filterable. The eight policy-type keys are populated only after the NAIC CIS re-ingest has run for an environment and may return empty results until then. One additional key appears in premium output but is rejected as a filter, because it is a report total rather than something a carrier writes:
total_all_linesState vocabulary (the 50 US states)
AKALARAZCACOCTDEFLGAHIIAIDILINKSKYLAMAMDMEMIMNMOMSMTNCNDNENHNJNMNVNYOHOKORPARISCSDTNTXUTVAVTWAWIWVWYLicensed-state data is validated against the 50 US states, so DC and the territories cannot appear in the data and are not accepted as filters — accepting them would let a filter silently match nothing. This is also what bounds min_licensed_states at 0–50.
Response
{
"prospects": [
{
"ticker": "TRV",
"company_name": "Travelers Companies Inc",
"assessment_status": "assessed",
"deal_score": 5,
"buying_signal_score": 0.72,
"rank": 1,
"rank_total": 412,
"entity_count": 34,
"matched_entity_count": 6,
"insurance_entity_types": ["carrier"],
"ownership": "public",
"lines_of_business": ["commercial_auto", "homeowners"],
"licensed_states": ["CA", "NY", "TX"],
"licensed_state_count": 50,
"premium": {
"currency": "USD",
"report_years": [2023, 2024],
"total_all_lines_usd": 32100000000,
"by_line_of_business": { "commercial_auto": 4120000000 },
"contributing_entity_count": 6
},
"complaints": {
"report_years": [2024],
"total_complaints": 412,
"complaint_index": 0.87,
"complaint_index_status": "available"
}
}
],
"total_groups": 412,
"total_entities": 1130,
"excluded_no_data_groups": 27,
"limit": 100,
"offset": 0
}Response fields
| Parameter | Type | Description |
|---|---|---|
assessment_status | string | assessed: analysis and score exist. pending_score: content indexed, score not yet computed. unassessed: no analysis yet. deal_score and buying_signal_score are null unless assessed.assessedpending_scoreunassessed |
rank / rank_total | integer | Position within this filtered result (rank_total equals total_groups by construction), not market standing. Not comparable across calls with different filters. |
entity_count | integer | Companies in this corporate family. |
matched_entity_count | integer | How many of them satisfied your filters. The returned ticker is the family's representative and may not be one of them. |
insurance_entity_types | string[] | Key omitted entirely when the family is unclassified — absence means "unknown"; an empty array would falsely claim "checked and found none". |
ownership | string | Family ownership.publicprivate |
licensed_state_count | integer | Cardinality of the family-wide union of licensed states, so it can exceed any single company's footprint. |
premium | object | null | Family premium, deduplicated by NAIC company code. Null when none is reported. report_years lists every NAIC report year that contributed — more than one entry means the family sum blends years. See the warning below. |
complaints | object | null | Family complaint summary; null when none. complaint_index is the ratio of the family's summed complaint shares to its summed premium shares (deduplicated by company code) — 1.0 is par, above 1.0 means more complaints than the family's premium footprint predicts. total_complaints sums member counts, each at its own latest year; report_years lists contributors. |
complaint_index_status | string | available: an index was computed. no_premium_share: complaints were reported but no member has premium share, so no index exists — complaint_index is null and total_complaints is still meaningful. not_reported: no complaint data.availableno_premium_sharenot_reported |
children | object[] | Only when include_children=true; same shape as hierarchy.children on the detail endpoint. |
total_groups | integer | Corporate families in the filtered universe — one per returned row across all pages. |
total_entities | integer | Companies inside those families. |
excluded_no_data_groups | integer | Families excluded because an applied insurance filter had no data to evaluate. 0 when no insurance filter is applied. Never counts families that were evaluated and genuinely didn't match. |
Premium is not year-aligned, and the total does not reconcile with the breakdown
total_all_lines_usd comes from NAIC CIS all-lines data at each company's own latest report year, so a family total can blend years; report_years lists every year that contributed. by_line_of_business comes from a different place: NAIC CIS per-line figures falling back to national direct-written-premium data, each at its own vintage.
The two are not reconcilable: total_all_lines_usd is not the sum of by_line_of_business. Roll-up keys (life, annuity, accident_health) also overlap their CIS policy-type splits when both are present — both premium blocks list those in rollup_lines_of_business; never sum a roll-up with its splits. Use the total for scale and the breakdown for line mix, and do not expect them to add up — the difference is source and vintage, not missing data.
Small carriers can top a complaint_index sort
The family complaint index only claims “more (or fewer) complaints than premium share predicts”. A small carrier can top a sort_by=complaint_index ranking on very little evidence. Corroborate with the detail endpoint's full per-line, per-year complaint series before treating an index as authoritative.
Examples
curl "https://dealsurface.com/api/v2/prospects?product_id=12&lines_of_business=commercial_auto&min_premium=50000000&states_licensed_in=TX,CA&sort_by=complaint_index" \
-H "Authorization: Bearer $DEALSURFACE_API_KEY"Errors
| Parameter | Type | Description |
|---|---|---|
400 | Bad Request | Invalid product_id, filter value, or pagination range. An unrecognized lines_of_business, states_licensed_in, entity_type, or sort_by value is always a 400 naming the accepted set. premium_line without exactly one lines_of_business value is also a 400. |
404 | Not Found | Product not found, or not owned by your team. |
503 | Service Unavailable | The prospect data is rebuilding, or has not been rebuilt since the product's keywords changed. Body: { error: "projection_unavailable", reason: "rebuilding" | "stale_keywords", retry_after_seconds }. rebuilding is worth retrying in seconds; stale_keywords waits on a pipeline run, so do not poll tightly. In v1 these states returned an empty list. |
/api/v2/prospects/:tickerscope: readFreeFull analysis for one company, plus firmographics, premium by line of business, complaint history, and its corporate family.
Path parameters
| Parameter | Type | Description |
|---|---|---|
tickerrequired | string | Company serving alias (letters, numbers, dots, or hyphens; max 12 characters). Also accepts any hierarchy.children[].ticker from a previous response — family representatives and subsidiaries alike. |
Query parameters
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product context for this prospect analysis. |
include_private | boolean= false | Rank this prospect within the private-inclusive population when your team is entitled. The prospect's identity — ticker, name, score, cached analysis — never changes. |
report_year | integer | Narrow the premium and complaint series to a single report year. Omit it to receive every available year, which is the default — the trend is usually the actionable half: an index of 1.34 that was 2.10 two years ago is a different conversation. |
Response
{
"ticker": "TRV",
"company_name": "Travelers Companies Inc",
"assessment_status": "assessed",
"deal_score": 5,
"rank": 1,
"rank_total": 412,
"company_info": {
"public_or_private": "public",
"family_public_or_private": "public",
"sic_codes": ["6331"],
"insurance_entity_types": ["carrier"],
"primary_naic_cocode": "19046",
"all_naic_cocodes": ["19046", "25674"],
"licensed_states": ["CA", "NY", "TX"],
"insurance_types": ["Commercial Auto"],
"website": "https://travelers.com",
"primary_domain": "travelers.com"
},
"premium": {
"currency": "USD",
"report_years": [2024, 2023],
"total_all_lines_usd": 28400000000,
"by_line_of_business": { "commercial_auto": 4120000000 },
"observations": [
{ "report_year": 2024, "line_of_business": "commercial_auto",
"annual_premium_usd": 4120000000 },
{ "report_year": 2023, "line_of_business": "commercial_auto",
"annual_premium_usd": 3980000000 }
]
},
"complaints": {
"cocode": "19046",
"available_years": [2024, 2023, 2022],
"observations": [
{ "report_year": 2024, "line_of_business": "commercial_auto",
"total_complaints": 118, "complaint_index": 1.34,
"complaint_index_status": "available" },
{ "report_year": 2023, "line_of_business": "commercial_auto",
"total_complaints": 96, "complaint_index": 1.09,
"complaint_index_status": "available" }
]
},
"hierarchy": {
"prospect_kind": "public_company",
"child_count": 34,
"eligible_child_count": 8,
"coverage_ratio": 0.66,
"group": { "ticker": "TRV", "company_name": "Travelers Companies Inc", "is_self": true },
"children": [
{
"ticker": "TRVA1234",
"company_name": "Travelers Casualty",
"deal_score": 4,
"has_assessment": true
}
]
},
"narrative_scope": "company",
"findings_summary": "Travelers is actively investing in...",
"recommendation_summary": "Strong fit for cyber insurance...",
"key_opportunity": "CEO cited cyber as top growth area...",
"primary_challenge": "Legacy claims systems...",
"impact_score_distribution": { "5": 12, "4": 30 },
"findings": [
{
"finding_id": 1,
"text": "Travelers announced a $200M technology...",
"evidence_excerpt_ids": [101, 102]
}
],
"excerpt_count": 74,
"excerpts": [
{
"excerpt_id": 101,
"text": "We are investing significantly in...",
"impact_score": 5,
"source": { "name": "Q3 2025 Earnings Call", "date": "2025-10-28" },
"pain_points": ["technology modernization", "cyber exposure"],
"sales_summary": "CEO emphasizes cyber investment priority",
"buying_signal_present": true
}
]
}Response blocks
| Parameter | Type | Description |
|---|---|---|
company_info | object | Firmographics for this entity. insurance_entity_types is omitted entirely when unclassified — absence means "unknown", an empty array would falsely claim "checked and found none". primary_naic_cocode is this entity's own code when exactly one exists; all_naic_cocodes includes family-inherited codes and is not a superset duplicate. Like the premium and complaint rollups, it covers the whole corporate family — including members the hierarchy listing omits (hidden or entitlement-filtered); NAIC cocodes are public-registry identifiers. |
premium | object | null | This ENTITY only, not the family. observations carry the year × line grain (same shape as complaints). The same non-reconciliation warning applies: total_all_lines_usd is not the sum of by_line_of_business — they come from different sources at different vintages; use the total for scale and the breakdown for mix. |
complaints | object | null | Full NAIC complaint series by year AND line for this entity's cocode, complete by default (narrow with report_year). complaint_index_status per observation: available, no_premium_share (complaints reported but no premium share that year, so no index — total_complaints is still meaningful), or not_reported. |
hierarchy | object | null | The corporate family: prospect_kind, child_count, eligible_child_count, coverage_ratio, the group representative, and children with their own tickers. has_assessment is per-product; where it is false, fetching the ticker returns the facts-only shape (null scores, empty findings and excerpts) rather than 404. |
narrative_scope | string | Which grain the prose fields describe: "company" for this entity, "group" when the pipeline substitutes a parent-group narrative (as the UI does).companygroup |
excerpt_count | integer | How many excerpts exist in total; the embedded excerpts array is capped at 20. |
Analysing a subsidiary
Pass any hierarchy.children[].ticker straight back to this endpoint — it accepts those aliases, not only the tickers /prospects returned. A subsidiary reports its family's rank rather than one of its own, and its own premium and complaint record. Where has_assessment is false, the company exists in the data but has no analysis for this product yet: fetching it returns the facts-only shape — assessment_status "unassessed", null scores, empty findings and excerpts, with company_info, premium, complaints, and hierarchy still populated from the data that does exist.
Embedded excerpts are capped at 20
The excerpts array carries at most 20, highest impact score first, with excerpt_count reporting how many exist. Use GET /excerpts for the rest. The cap exists because well-covered companies carry hundreds of excerpts — enough to exhaust an AI agent's context window in a single call. The complaint series stays complete by default: its observations are short scalars.
Examples
curl "https://dealsurface.com/api/v2/prospects/TRV?product_id=12" \
-H "Authorization: Bearer $DEALSURFACE_API_KEY"
# Drill into a subsidiary using a hierarchy.children ticker
curl "https://dealsurface.com/api/v2/prospects/TRVA1234?product_id=12" \
-H "Authorization: Bearer $DEALSURFACE_API_KEY"Errors
| Parameter | Type | Description |
|---|---|---|
400 | Bad Request | Invalid ticker format or product_id. |
404 | Not Found | Unknown ticker for this product, or the alias is ambiguous. A known but unassessed company is NOT a 404 — it returns the facts-only shape. |
503 | Service Unavailable | projection_unavailable — same body and semantics as the list endpoint. |
/api/v2/excerptsscope: readFreeSearch and filter evidence excerpts from filings and earnings calls for a prospect and product.
Query parameters
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product the excerpts were assessed against. |
tickerrequired | string | Company serving alias (letters, numbers, dots, or hyphens; max 12 characters). Subsidiary aliases resolve like the detail endpoint. |
min_impact_score | integer | Minimum impact score (1-5).12345 |
keyword | string | Filter excerpts where pain_points contain this keyword (case-insensitive, max 200 chars). |
limit | integer= 50 | Max results (1-200). |
offset | integer= 0 | Pagination offset. |
framework_category is removed in v2
framework_category is gone as both a query parameter and a response field — here and in the excerpts embedded in prospect detail. It stopped being a meaningful axis when relevance filtering stopped gating on it. Filter with min_impact_score and keyword instead.
Response
{
"excerpts": [
{
"excerpt_id": 101,
"text": "We are investing significantly in...",
"impact_score": 5,
"source": { "name": "Q3 2025 Earnings Call", "date": "2025-10-28" },
"pain_points": ["technology modernization"],
"sales_summary": "CEO emphasizes cyber investment priority",
"buying_signal_present": true
}
],
"total_count": 30,
"limit": 50,
"offset": 0
}Examples
curl "https://dealsurface.com/api/v2/excerpts?product_id=12&ticker=TRV&min_impact_score=4&keyword=cyber" \
-H "Authorization: Bearer $DEALSURFACE_API_KEY"Subsidiary tickers resolve to the family
DealMaps are held at corporate-family grain. Passing a hierarchy.children[].ticker to any DealMap or outreach endpoint resolves upward to the family's DealMap and says so in the response — a 404 would falsely read as “this company has no buying committee”. A generate that resolves to an already-cached family map is not charged.
/api/v2/dealmapscope: readFreeRead the current buying committee DealMap for a prospect company and product. Returns stakeholders with AI-assigned roles, org hierarchy edges, and a buying committee summary.
Query parameters
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product context. |
tickerrequired | string | Company serving alias. Subsidiary tickers resolve to the family's DealMap. |
Response
{
"status": "ready",
"chart": {
"id": "abc-123",
"generated_at": "2026-04-01T10:00:00Z",
"expires_at": "2026-05-01T10:00:00Z",
"company_name": "Travelers Companies Inc",
"node_count": 45,
"relevant_node_count": 12
},
"stakeholders": [
{
"node_id": "n-001",
"full_name": "Jane Smith",
"title": "Chief Information Officer",
"level": "c_suite",
"level_label": "C-Suite",
"linkedin_url": "https://linkedin.com/in/janesmith",
"work_email": null,
"manager_name": null,
"stakeholder_role": "Economic Buyer",
"stakeholder_role_source": "generated",
"annotation_score": 9,
"annotation_reason": "Oversees technology budget...",
"is_top_pick": true,
"top_pick_source": "generated"
}
],
"edges": [
{ "from_node_id": "n-001", "to_node_id": "n-002", "relationship": "manages" }
],
"buying_committee_summary": {
"economic_buyer": "Jane Smith",
"decision_maker": "Bob Johnson",
"champion": null,
"executive_sponsor": null,
"evaluator": "Alice Wong",
"blockers": []
}
}Returns "status": "empty" if no DealMap has been generated yet. Call POST /dealmap/generate first. "status": "stale" means a map exists but the annotation is past its TTL.
Examples
curl "https://dealsurface.com/api/v2/dealmap?product_id=12&ticker=TRV" \
-H "Authorization: Bearer $DEALSURFACE_API_KEY"/api/v2/dealmap/generatescope: readscope: outreach0-10 creditsGenerate or refresh the buying committee DealMap for a prospect. Maps the org structure and assigns stakeholder roles using AI analysis. Credits charged only when fresh org-chart data is fetched. Hard-capped at 5 requests per minute.
Request body
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product context. |
tickerrequired | string | Company serving alias. Subsidiary tickers resolve to the family; a generate that lands on a cached family map is not charged. |
force_refresh | boolean= false | Force fresh org chart fetch, bypassing cache. Consumes credits. |
force_refresh costs credits
Setting force_refresh: true always charges credits because it triggers a fresh org-chart fetch. Only use this if you need up-to-date organizational data and the cached results are known to be stale.
Response
{
"status": "ready",
"chart": {
"id": "abc-123",
"generated_at": "2026-04-01T10:00:00Z",
"expires_at": "2026-05-01T10:00:00Z",
"company_name": "Travelers Companies Inc",
"node_count": 45,
"relevant_node_count": 12
},
"stakeholders": [...],
"edges": [...],
"buying_committee_summary": {
"economic_buyer": "Jane Smith",
"decision_maker": "Bob Johnson",
"champion": null,
"executive_sponsor": null,
"evaluator": "Alice Wong",
"blockers": []
},
"generation_source": "org_fresh",
"credits_charged": true,
"credits_operation": "prospect_org_chart_annotation"
}Credit behavior
| Parameter | Type | Description |
|---|---|---|
org_fresh | 10 credits | Fresh org-chart data was fetched. Charged. |
org_cached | free | Cached org data was reused. Not charged. |
annotation_only | free | Only the AI annotation was refreshed. Not charged. |
Errors
| Parameter | Type | Description |
|---|---|---|
402 | Payment Required | Insufficient credits for a fresh generation. See Credits & Billing for the body shape. |
409 | Conflict | A generation for this prospect is already in flight. Wait, then read the result with GET /dealmap. |
429 | Too Many Requests | Hard cap of 5 requests per minute exceeded. |
Examples
curl -X POST https://dealsurface.com/api/v2/dealmap/generate \
-H "Authorization: Bearer $DEALSURFACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"product_id": 12, "ticker": "TRV"}'/api/v2/dealmap/node-rolescope: readFreeOverride the AI-assigned stakeholder role for a specific person on the buying committee DealMap. This is a team-wide override visible to all team members.
Request body
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product context. |
tickerrequired | string | Company serving alias. |
node_idrequired | string | The node_id from the DealMap stakeholders. |
stakeholder_rolerequired | string | The corrected stakeholder role.Economic BuyerDecision MakerChampionExecutive SponsorEvaluatorEnd UserProcurement / Legal ReviewerBlockerIrrelevant |
Response
{
"success": true,
"node_id": "n-001",
"stakeholder_role": "Decision Maker"
}Examples
curl -X PATCH https://dealsurface.com/api/v2/dealmap/node-role \
-H "Authorization: Bearer $DEALSURFACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"product_id": 12, "ticker": "TRV", "node_id": "n-001", "stakeholder_role": "Decision Maker"}'/api/v2/dealmap/node-top-pickscope: readFreeMark or unmark a stakeholder as the top pick for their role on the buying committee DealMap. Team-wide, like role overrides. Available since v1 but previously undocumented.
Request body
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product context. |
tickerrequired | string | Company serving alias. |
node_idrequired | string | The node_id from the DealMap stakeholders. |
is_top_pickrequired | boolean | true marks this stakeholder as the top pick for their role; false clears a manual top pick. |
Response
{
"success": true,
"node_id": "n-001",
"is_top_pick": true
}The top pick per role feeds buying_committee_summary and the { stakeholder_role } lead selector on POST /outreach.
Examples
curl -X PATCH https://dealsurface.com/api/v2/dealmap/node-top-pick \
-H "Authorization: Bearer $DEALSURFACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"product_id": 12, "ticker": "TRV", "node_id": "n-001", "is_top_pick": true}'/api/v2/outreachscope: readscope: outreach1 creditGenerate an evidence-backed outreach message for a prospect. Consumes 1 credit per successful request. Hard-capped at 10 requests per minute.
Request body
| Parameter | Type | Description |
|---|---|---|
product_idrequired | integer | Product context. |
tickerrequired | string | Company serving alias. Subsidiary tickers resolve to the family's DealMap for lead selection. |
platformrequired | string | Target platform for the message.emaillinkedin |
tonerequired | string | Desired tone.hard_sellhelpfulneutral |
length | string= typical | Desired length.shortertypicallonger |
selected_lead | SelectedLead | A full stakeholder object (from the DealMap response) to personalize the message for. Takes precedence over lead_selector. |
lead_selector | object | Selector to resolve a stakeholder from cached DealMap results. One of: { node_id: string }, { level: "manager"|"director"|"vp"|"c_suite" }, { full_name: string, level: string }, or { stakeholder_role: string } (picks the top-pick for that role). |
Lead targeting
You can pass either selected_lead (the full lead object) or lead_selector (to resolve a lead from cached results). If both are provided, selected_lead takes precedence. If neither is provided, outreach is generated without personalization for a specific contact.
Response
{
"subject": "Helping Travelers strengthen cyber risk assessment",
"body": "Hi Jane,\n\nI noticed Travelers has been investing...",
"platform": "email",
"tone": "helpful",
"length": "typical",
"used_excerpt_ids": [101, 102, 105],
"selected_lead": {
"full_name": "Jane Smith",
"title": "Chief Information Officer",
"level": "c_suite"
},
"selected_lead_note": null
}The used_excerpt_ids array indicates which evidence excerpts were used to ground the message. Cross-reference with the Excerpts endpoint to verify sources.
Examples
curl -X POST https://dealsurface.com/api/v2/outreach \
-H "Authorization: Bearer $DEALSURFACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_id": 12,
"ticker": "TRV",
"platform": "email",
"tone": "helpful",
"lead_selector": { "level": "c_suite" }
}'