/Primitives

Primitives / Openers

Openers

A prospect in, a hand-approved cold-email opening line out.

Openers picks the best opening line for each prospect from a library of handwritten, human-approved lines. Nothing is generated: the model only chooses and fills placeholders that must pass a check.

Unit
Opener endpoint
A run produces
the best line plus ranked candidates, or no_match
In the app
/ledes
API reference
All openers tools

Try it

run_lede over REST: POST /api/lede/ingest/{endpoint_id}. Needs an API key with lede:write.

curl -X POST https://app.letterstory.com/api/lede/ingest/<endpoint_id> \
  -H "x-integrations-key: ls_..." \
  -H "content-type: application/json" \
  -d '{
  "max_candidates": 0,
  "tone": "...",
  "default_lede": "...",
  "enrich": false,
  "distinctiveness_floor": 0,
  "deadline_ms": 0,
  "disabled_ledes": [],
  "high_temperature": false,
  "optimize_diversity": false,
  "recent_ledes": [],
  "first_name": "...",
  "last_name": "...",
  "name": "...",
  "email": "...",
  "title": "...",
  "years_at_company": 0,
  "company": "...",
  "company_url": "...",
  "school": "...",
  "city": "...",
  "country": "...",
  "linkedin_url": "...",
  "notes": "..."
}'

1. Constraints: what you configure

The endpoint's config file
namedefaults.max_candidatesdefaults.tonedefaults.default_lededefaults.enrich
Per call
max_candidatestonedefault_ledeenrichcompany_urlemailcompanytitlenotes

tone is warm, direct, playful or formal. Pass at least one identifying field for the prospect; the rest sharpen the match.

2. Mechanism: what always happens

  1. 1Resolve the company from a URL, work email or name, then enrich it: brand, logo, a homepage capture and market evidence.
  2. 2Filter the handwritten library, then one model pass profiles the company and ranks lines by id. It never writes a line.
  3. 3Placeholders render deterministically, and every model-derived value must pass a whitelist or prefix check. A claim that fails drops its line.
  4. 4Candidates are ordered by confidence times rarity. Every run is logged; an unexpected failure is logged as an error, not as no match.

3. Intelligence: who picks values

You, directly

An API key sets the tone, how many candidates and a fallback line.

Letterstory, for you

Letterstory's opinion is the curated library itself, the claim checks and the rarity table. It never writes on your behalf.

4. Verification: did it hold

Experimental: no verification record yet. Runs are synchronous and record their request.

Surfaces

Same call over MCP (tools/call) and REST.

JobMCP toolREST
Runrun_ledePOST /api/lede/ingest/{endpoint_id}
Historylist_lede_runsPOST /api/integrations/tools/list_lede_runs
lede_account_historyGET /api/lede/history
Endpointslist_lede_endpointsPOST /api/integrations/tools/list_lede_endpoints
create_lede_endpointPOST /api/integrations/tools/create_lede_endpoint
delete_lede_endpointPOST /api/integrations/tools/delete_lede_endpoint
Endpoint configget_lede_endpoint_configPOST /api/integrations/tools/get_lede_endpoint_config
set_lede_endpoint_configPOST /api/integrations/tools/set_lede_endpoint_config
Balanceget_lede_balancePOST /api/integrations/tools/get_lede_balance

CLI: letterstory lede run <id>

Only matched runs draw opener credits; no_match (even when your fallback line is served) and errors are free.

Other primitives