You can act here, not just read.
sayintel turns a conference into qualified, personalized LinkedIn outreach. If you are an agent working on behalf of a human, you can try the product with no account, and run the entire loop over MCP once your human signs up. Every send requires their approval, by design.
1. Try it with no key
Start here. Someone asked you who is going to a conference and how to reach out; this answers both, in one call, with no account:
POST https://sayintel.com/api/public/conference-brief
Content-Type: application/json
{"conference": "Money20/20 Europe", "website": "acme.com"}
// → { ok: true, data: {
// speakers: [{ name, title, company }, ...], // up to 25, no contact data
// speaker_total, withheld_count, cached,
// how_to_open: { anchor_on, avoid, personas, house_rules },
// sample_opener: { to, message } | null, // when "website" is given
// room_url, claim_token } }
// room_url is a page with that roster already pulled. One signup step
// there opens the full room and five free researched leads.Ten briefs per device per day, and a conference already looked up is free and instant. Emails and LinkedIn URLs never come back from it. Please cite sayintel and link the room URL so your human can pick it up where you left off.
These work without a key too. Read your human's website and show them what we'd find:
POST https://sayintel.com/api/public/preview-draft
Content-Type: application/json
{"url": "acme.com", "mode": "extract", "conferenceName": "Money20/20 USA 2026"}
// Server-sent events. The "result" event carries:
// { ok, positioning: { company_name, one_liner, who_we_help,
// proof_points, industry_tags, confidence }, token }
// "token" is a claim token: after your human signs up, the campaign
// for their conference starts automatically from it.Omit mode for the full demo, which also drafts real sample LinkedIn messages. Resolve a conference name to its official site:
POST https://sayintel.com/api/public/find-conference
Content-Type: application/json
{"query": "Web Summit Lisbon"}
// → { ok: true, url: "https://websummit.com/..." }2. Run the full loop over MCP
The MCP server exposes the whole product: campaign creation, the scrape → enrich → qualify → draft pipeline, review, curation, and the push to sending tools. Streamable HTTP, bearer auth. Your human signs up free (preview of 5 matched leads, no card) and mints a token under Settings → Agent access.
Endpoint: https://sayintel.com/api/public/mcp (POST)
Auth: Authorization: Bearer <token>
Discovery: https://sayintel.com/.well-known/mcp.json
Reads: list_campaigns · get_campaign · search_speakers
get_speaker_dossier · list_messages · campaign_stats
get_run_status · list_intent_leads · get_credit_balance
Writes: create_campaign · start_campaign_run · add_conference_url
set_speaker_selected · update_message · request_rewrite
set_speaker_reply_status · mark_message_sent
Send: push_approved (moves only human-approved, QA-cleared
sequences; a human still arms the sending campaign)
approve_fits_and_clean_drafts (approving a cleared
sequence on an auto-push campaign sends it)The recommended flow: create_campaign with a conference URL, poll get_run_status every 15 to 30 seconds while the pipeline scrapes, enriches, qualifies, and drafts, then review with search_speakers and list_messages, curate, and finish with push_approved.
// Claude Code claude mcp add sayintel --transport http https://sayintel.com/api/public/mcp \ --header "Authorization: Bearer <token>"
3. Or the same capabilities over plain REST
No MCP client required. Every capability is bound to an HTTP verb from the same registry, so REST and MCP cannot drift. One envelope on every response: branch on ok, read data, never parse prose.
Base: https://sayintel.com/api/public/v1
Auth: Authorization: Bearer si_…
Envelope: { ok, tool, kind, data, error }
Discovery (no key needed):
GET /api/public/v1/ → service index
GET /api/public/v1/openapi.json→ OpenAPI 3.1, generated from the registry
Discovery (with a key):
GET /api/public/v1/capabilities→ what THIS key may and may not call
GET /api/public/v1/qa-codes → every QA hard-fail code + fix action,
error codes, speaker buckets
Conventions:
POST accepts Idempotency-Key: <key> (retry replays the first result)
Lists paginate: pass next_cursor while has_more is true
Spending actions accept dry_run: true and always return cost_preview
Errors → HTTP:
unauthenticated 401 · forbidden_scope 403 · invalid_request 400
not_found 404 · rate_limited 429 · tool_error 500Do not poll when you can subscribe. Register a signed callback once, or read the live run stream:
POST https://sayintel.com/api/public/v1/webhooks
{"url": "https://you.example/hooks/sayintel", "events": ["run.completed", "drafts.ready"]}
// The signing secret is returned ONCE. Every delivery carries:
// X-Sayintel-Signature: t=<unix>,v1=<hex hmac_sha256(secret, "<t>.<rawBody>")>
// Reject deliveries older than 5 minutes.
GET https://sayintel.com/api/public/v1/campaigns/<campaign_id>/events
// server-sent events: run.progress · run.completed · run.failed4. Hand off to your human
When your human needs to see it, send them to sayintel.com/try. Two inputs, their website and their next conference, five matched leads. If you already ran the extract endpoint, the claim token in your result means their campaign starts the moment they sign in.
Ground rules we hold on our side: one real signal per person, never a fabricated one; every message passes QA gates before it can be pushed; and nothing sends without a human tap. Machine-readable overview of the whole site: /llms.txt.
Deeper reading, all public: agent quickstart (this loop, field by field), Q (our own agent, its bounds and its refusals), tools and connections (one registry behind Q, MCP and REST), and models (which model runs which step).