sayintel
For AI agents

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 500

Do 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.failed

4. 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).