Developer documentation

Introduction

CapyScout turns an email address into a scored, source-backed decision - a classification, fit/risk/confidence scores, a recommended action and route, a suggested reply, and company + person enrichment. There are three ways to call it, all sharing one API key, one billing model, and the same result:

  • REST API - call from your backend, signup flow, or CRM webhook.
  • MCP server - let an AI agent (Claude, Cursor, Windsurf) call CapyScout as a tool.
  • Clay - enrich a whole table with the HTTP API block, no code.

Authentication

Every request needs an account API key. Create one in Settings → Integrations. Keys are shown once - store the secret securely. Send it as a Bearer token (or the X-Api-Key header). The account is resolved from the key, so request bodies never carry an account id.

Authorization: Bearer cs_live_xxxxxxxxxxxxxxxxxxxxxxxx

Base URL

Production base URL is https://app.capyscout.com (local dev: http://localhost:8080). All REST endpoints are versioned under /api/v1. The MCP server is at /mcp.

Enrich an email

POST /api/v1/enrich/email

Synchronous. Returns the full Intelligence Card. Warm lookups (cached domain/identity) return in well under a second; cold lookups run a live crawl + model pass and take longer.

Request

curl -X POST https://app.capyscout.com/api/v1/enrich/email \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
        "email": "jane@acme.com",
        "domain": "acme.com",            // optional override
        "source": "signup",              // optional
        "message": "Looking for a demo", // optional
        "scenario": "signup_triage"      // optional, defaults to signup_triage
      }'

Response 200

{
  "object": "enrichment",
  "id": "enr_9aZ2xQ1p",
  "status": "complete",
  "cached": false,
  "latency_ms": 8421,
  "data": {
    "email": "jane@acme.com",
    "classification": "high_fit_lead",
    "classificationLabel": "High-fit lead",
    "fitScore": 88, "riskScore": 12, "confidence": 91,
    "recommendedAction": "Route to sales - high-priority follow-up",
    "fullName": "Jane Chen", "jobTitle": "VP of Engineering",
    "company": { "name": "Acme", "domain": "acme.com", "industry": "B2B SaaS" },
    "signals": ["Work email verified", "Hiring engineers", "Has pricing page"],
    "suggestedReply": "Hi Jane - thanks for signing up...",
    "evidence": [ ... ], "uncertain": [ ... ]
  }
}

Flat response (Clay, Zapier, Make)

POST /api/v1/enrich/email/flat

Same enrichment, but the card is flattened to a single-level snake_case object so no-code tools can map each field straight to a column. This is the endpoint to use in a Clay HTTP API block. Same auth, billing, quotas, and cutoffs as the standard endpoint.

curl -X POST https://app.capyscout.com/api/v1/enrich/email/flat \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "email": "jane@acme.com" }'

Response 200

{
  "id": "enr_9aZ2xQ1p",
  "cached": false,
  "latency_ms": 8421,
  "email": "jane@acme.com",
  "scenario": "signup_triage",
  "classification": "high_fit_lead",
  "classification_label": "High-fit lead",
  "fit_score": 88,
  "risk_score": 12,
  "confidence": 91,
  "recommended_action": "Route to sales - high-priority follow-up",
  "recommended_route": "Sales / founder",
  "full_name": "Jane Chen",
  "job_title": "VP of Engineering",
  "seniority": "Exec",
  "person_linkedin": "https://linkedin.com/in/janechen",
  "person_location": "San Francisco, US",
  "identity_verified": true,
  "company_name": "Acme",
  "company_domain": "acme.com",
  "company_industry": "B2B SaaS",
  "company_size": "51-200",
  "company_category": "B2B SaaS",
  "website_reachable": true,
  "suggested_reply": "Hi Jane - thanks for signing up...",
  "why_it_matters": "VP-level buyer at a growing SaaS...",
  "signals": "Work email verified; Hiring engineers; Has pricing page"
}

signals is a single semicolon-joined string so it drops cleanly into one column. Errors keep the standard error envelope (see below), so map error.message too.

Enrich a company

POST /api/v1/enrich/company

Domain in, firmographics out - company name, description, tech stack, pricing/careers signals, domain age, LinkedIn, and a free SEC EDGAR funding signal (US). Company-only, no person layer.

curl -X POST https://app.capyscout.com/api/v1/enrich/company \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "stripe.com" }'

Response 200

{
  "id": null, "cached": false, "latency_ms": 1240,
  "data": {
    "domain": "stripe.com",
    "name": "Stripe",
    "description": "Financial infrastructure for the internet",
    "websiteReachable": true,
    "hasPricingPage": true,
    "hasCareersPage": true,
    "techStack": ["HubSpot", "Segment"],
    "domainAgeDays": 5487,
    "linkedinUrl": "https://linkedin.com/company/stripe",
    "secFundingFiling": true,
    "secFundingDate": "2024-04-01",
    "secFilingCount": 12
  }
}

Discover companies

POST /api/v1/discover

Describe who to find in plain English and get merged company results from competitor, technology, and local sources. Searching is free - it costs no credits, only your per-plan monthly search cap; enriching the results is what spends credits. The API is available on Lite and up.

curl -X POST https://app.capyscout.com/api/v1/discover \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "query": "Shopify stores in Austin with 4.5+ stars" }'

Response 200

{
  "object": "discovery",
  "interpretation": "Shopify stores in Austin, TX with 4.5+ stars",
  "count": 24,
  "results": [
    { "name": "Acme Goods", "domain": "acmegoods.com", "reason": "Runs Shopify",
      "rating": 4.7, "reviews": 212, "match_score": 88, "location": "Austin, TX" }
  ],
  "searches_remaining": 476
}

A query that is too vague returns count: 0 with a clarification question instead of spending a search.

Verify an email (1 credit)

POST /api/v1/verify/email

Authoritative mailbox deliverability (valid / invalid / catch-all) plus quality flags. Costs one credit per completed verification. For a free syntax/MX/quality verdict, use /api/v1/check/email below.

curl -X POST https://app.capyscout.com/api/v1/verify/email \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "email": "jane@acme.com" }'

Response 200

{
  "email": "jane@acme.com", "domain": "acme.com",
  "deliverable": true, "verification_status": "valid",
  "has_mx": true, "is_disposable": false, "is_role_account": false,
  "is_free_email": false, "is_work_email": true
}

Recent events (free)

GET /api/v1/feed?limit=20

Your most recent enrichment and signal events, newest first. Read-only and consumes no credits; limit is clamped to 1-100.

curl -H "Authorization: Bearer cs_live_xxx" \
  "https://app.capyscout.com/api/v1/feed?limit=20"

Response 200

{
  "object": "list",
  "count": 20,
  "data": [
    { "id": "enr_9aZ2xQ1p", "created_at": "2026-07-18T14:03:00", "data": { ... } }
  ]
}

Check an email (free, rate-limited)

POST /api/v1/check/email

No credits consumed. Returns syntax, MX deliverability, free/disposable/role flags, and a 0-100 quality score with a plain verdict (business, personal, role_account, disposable, undeliverable, invalid). Great for gating a signup form or filtering a batch before sending to /enrich/email.

Rate limit: 200 checks/hour per API key (configurable on enterprise plans). Returns 429 with a Retry-After epoch when exceeded.

curl -X POST https://app.capyscout.com/api/v1/check/email \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "email": "jane@acme.com" }'

Response 200

{
  "email": "jane@acme.com",
  "domain": "acme.com",
  "syntax_valid": true,
  "deliverable": true,
  "has_mx": true,
  "mx_provider": "Google Workspace",
  "is_free_email": false,
  "is_disposable": false,
  "is_role_account": false,
  "is_work_email": true,
  "quality_score": 92,
  "verdict": "business"
}

Find competitors

POST /api/v1/intel/competitors

Domain in, a scored list of rival companies out. Each competitor carries a website, a reason (product_overlap, market_overlap, keyword_overlap), and a 0-100 threat score. Built from public search + reasoning.

curl -X POST https://app.capyscout.com/api/v1/intel/competitors \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "stripe.com" }'

Response 200

{
  "id": null, "cached": false, "latency_ms": 3120,
  "data": {
    "domain": "stripe.com",
    "companyName": "Stripe",
    "competitors": [
      { "name": "Adyen", "website": "adyen.com", "reason": "product_overlap", "threatScore": 88 },
      { "name": "Square", "website": "squareup.com", "reason": "product_overlap", "threatScore": 74 },
      { "name": "Checkout.com", "website": "checkout.com", "reason": "keyword_overlap", "threatScore": 61 }
    ]
  }
}

Fetch a prior enrichment

GET /api/v1/enrich/{id}

curl https://app.capyscout.com/api/v1/enrich/enr_9aZ2xQ1p \
  -H "Authorization: Bearer cs_live_xxx"

Returns a compact summary of the stored decision. Scoped to your account; unknown ids return 404.

Monitoring: watchlist & signals

Read and manage the companies you monitor and the signals you track - the same data the app shows. Writes respect your plan limits (watchlist size and signal count); over-limit items are skipped and reported in the response.

GET /api/v1/companies - watched companies with score and Why Now

curl https://app.capyscout.com/api/v1/companies \
  -H "Authorization: Bearer cs_live_xxx"

→ { "object": "list", "data": [
    { "domain": "acme.com", "name": "Acme Corp", "score": 42,
      "why_now": "New CRO, hiring 5 AEs...", "last_scan": "2026-07-14T08:00" } ] }

POST /api/v1/companies - add domains to the watchlist

curl -X POST https://app.capyscout.com/api/v1/companies \
  -H "Authorization: Bearer cs_live_xxx" -H "Content-Type: application/json" \
  -d '{ "domains": ["acme.com", "beta.io"], "tags": "Q3 targets" }'

→ { "object": "add_result", "added": 2, "already_monitored": 0,
    "capped_out": 0, "watchlist": "My Watchlist" }

GET /api/v1/companies/{domain}/signals - signals that fired on a company

curl https://app.capyscout.com/api/v1/companies/acme.com/signals \
  -H "Authorization: Bearer cs_live_xxx"

GET /api/v1/signals - list your custom signals

POST /api/v1/signals - add a signal (plain English)

curl -X POST https://app.capyscout.com/api/v1/signals \
  -H "Authorization: Bearer cs_live_xxx" -H "Content-Type: application/json" \
  -d '{ "question": "Did they hire a new CRO?", "name": "New CRO", "rank": "High" }'

→ { "object": "add_result", "added": 1, "watchlist": "My Watchlist" }

If added is 0, your plan's signal limit is reached - upgrade to add more. New signals fire to your configured webhook (Slack, Teams, Clay, or any URL) as they are detected.

Idempotency

Safely retry POSTs without double-charging. Send an Idempotency-Key header; a repeat with the same key returns the original result and is not billed again.

-H "Idempotency-Key: 6f1c8b2e-...-a7"

Response schema

Key fields on the enrichment (nested data / flat response):

FieldTypeDescription
classificationstringe.g. high_fit_lead, low_intent, risky_signup
fit_scoreint 0-100How well the lead fits your ICP
risk_scoreint 0-100Abuse / fraud / bad-fit risk
confidenceint 0-100Confidence in the decision
recommended_actionstringThe one next step to take
full_name / job_titlestringVerified identity (when available)
company_*stringname, domain, industry, size, category
suggested_replystringReady-to-send opener

Errors, rate limits & cutoffs

Errors use a consistent envelope. No internal details are ever leaked.

{ "error": { "type": "quota_exceeded", "code": "plan_limit_reached",
             "message": "Your plan's enrichment quota has been reached." } }
StatusMeaning
400Invalid or missing email.
401Missing / invalid / revoked API key.
402Plan enrichment quota reached (no overage).
429Rate limit or system cutoff. Check Retry-After.
500Transient enrichment error - safe to retry.

Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. A 120 requests/minute per-key burst limit applies by default.

MCP server

CapyScout ships a Model Context Protocol server at POST /mcp (JSON-RPC 2.0, Streamable-HTTP). Point any MCP-capable agent at it with your API key. Tools are metered and plan-limited exactly like REST.

Tools: enrich_email, get_enrichment, check_email, list_competitors, list_watchlist, add_to_watchlist, list_signals, add_signal, get_company_signals.

Client config

{
  "mcpServers": {
    "capyscout": {
      "url": "https://app.capyscout.com/mcp",
      "headers": { "Authorization": "Bearer cs_live_xxx" }
    }
  }
}

Call a tool (raw JSON-RPC)

curl -X POST https://app.capyscout.com/mcp \
  -H "Authorization: Bearer cs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
        "jsonrpc": "2.0", "id": 1,
        "method": "tools/call",
        "params": {
          "name": "enrich_email",
          "arguments": { "email": "jane@acme.com" }
        }
      }'

The tool returns a compact, token-efficient card (classification, scores, recommended action, key signals, suggested reply) as both text and structuredContent.

Connect over OAuth (Claude.ai, no key)

MCP clients that speak OAuth - like Claude.ai Custom Connectors - can connect to /mcp without a pasted key. Add CapyScout as a connector, sign in, and approve; the client discovers everything automatically (PKCE + Dynamic Client Registration).

  1. Claude.ai → Settings → Connectors → Add custom connector.
  2. Paste https://app.capyscout.com/mcp as the connector URL.
  3. Click Connect, sign in, and approve. Manage or revoke under Settings → Integrations → Connected apps.
GET  /.well-known/oauth-protected-resource    # RFC 9728
GET  /.well-known/oauth-authorization-server  # RFC 8414
POST /oauth/register   # RFC 7591 dynamic client registration
GET  /oauth/authorize  # user sign-in + consent (PKCE, S256)
POST /oauth/token      # authorization_code + refresh_token grants

Clay integration

Enrich a whole Clay table with CapyScout using the built-in HTTP API enrichment block - no code. Every row's email becomes a scored decision with company + person fields as columns.

  1. In a Clay table with an email column, add a column → HTTP API.
  2. Method: POST. URL: https://app.capyscout.com/api/v1/enrich/email/flat
  3. Headers: Authorization: Bearer cs_live_xxx and Content-Type: application/json
  4. Body: { "email": "{{email}}" } (map {{email}} to your column)
  5. Run the column, then map response fields (fit_score, classification, company_name, ...) to new columns.

Use the flat endpoint so each field maps to one column. For agentic, one-off enrichment inside Claygent, point Clay's MCP block at /mcp instead.

Delivery destinations

Beyond calling CapyScout, you can have CapyScout call you. In Workflows, an IF/THEN rule fires an action whenever a decision matches your conditions (for example classification = high_fit_lead or risk_score > 70). Each action delivers to one destination. All HTTP destinations are outbound POSTs from CapyScout - you never open an inbound port.

You configure the destination in the workflow action; there is no separate API call. The table below shows what each destination expects and the exact payload CapyScout sends.

DestinationYou providePayload shape
SlackIncoming webhook URLSlack { "text": ... } message
Microsoft TeamsIncoming webhook URLTeams MessageCard
ZapierCatch Hook URLFlat Brief JSON
HubSpotWorkflow / webhook URLFlat Brief JSON
Custom webhookAny HTTPS URLFlat Brief JSON
EmailRecipient addressPlain-text summary email
TagTag nameInternal - no outbound call
Test your destination before your first real signup

Every workflow has a Test button that fires a synthetic sample event (jane@acme.com, classification high_fit_lead, fit 88) at your configured destination right now. You can verify Slack messages arrive, check your webhook receives the payload, and confirm email delivery — without touching production traffic.

  1. Go to Workflows and open or create a rule.
  2. Configure the action (Slack URL, webhook endpoint, email address, etc.).
  3. Click the Test button on the rule row — CapyScout fires the sample event immediately.
  4. Check the Last run status badge; it turns green on a 2xx response and shows the HTTP code on failure.

Slack

Posts a formatted message to a channel via a Slack incoming webhook.

  1. In Slack, open Apps and add Incoming Webhooks (or create a Slack app with the Incoming Webhooks feature enabled).
  2. Choose the channel to post to and copy the webhook URL (looks like https://hooks.slack.com/services/T000/B000/XXXX).
  3. In CapyScout → Workflows, add an action, pick Slack, and paste the webhook URL.

Payload CapyScout POSTs to the webhook URL

{
  "text": "*CapyScout* - High-fit leads triggered\n>*Email:* jane@acme.com\n>*Classification:* high_fit_lead\n>*Fit:* 88  |  *Risk:* 8\n>*Action:* Route to sales and follow up within 15 minutes."
}

Microsoft Teams

Posts a card to a Teams channel via an incoming webhook connector.

  1. In Teams, open the channel → Manage channelConnectors → add Incoming Webhook.
  2. Name it CapyScout, optionally add an icon, and copy the generated URL.
  3. In CapyScout → Workflows, add an action, pick Microsoft Teams, and paste the webhook URL.

Payload CapyScout POSTs to the webhook URL

{
  "@type": "MessageCard",
  "@context": "https://schema.org/extensions",
  "summary": "jane@acme.com - high_fit_lead (fit: 88, risk: 8)",
  "themeColor": "7c3ff7",
  "title": "CapyScout - high_fit_lead",
  "text": "jane@acme.com - high_fit_lead (fit: 88, risk: 8)"
}

Zapier

Sends the full Brief JSON to a Zapier Catch Hook, so you can fan out to thousands of downstream apps (Sheets, Salesforce, Notion, SMS, and more).

  1. In Zapier, create a Zap with the Webhooks by Zapier trigger → Catch Hook.
  2. Copy the custom webhook URL Zapier generates.
  3. In CapyScout → Workflows, add an action, pick Zapier, and paste the URL. Map fields in later Zap steps.

Payload CapyScout POSTs to the Catch Hook

{
  "id": 1234,
  "email": "jane@acme.com",
  "domain": "acme.com",
  "scenario": "signup_triage",
  "classification": "high_fit_lead",
  "fitScore": 88,
  "riskScore": 8,
  "confidence": 82,
  "recommendedAction": "Route to sales and follow up within 15 minutes.",
  "source": "signup_form",
  "accountId": 42
}

HubSpot

Sends the same flat Brief JSON to a HubSpot webhook so you can create or update contacts and enroll them in workflows. HubSpot webhook actions are part of workflow automation (Operations Hub) or a custom integration endpoint.

  1. In HubSpot, create a workflow with a Send a webhook action (or use a custom endpoint that accepts JSON).
  2. Set the method to POST and copy the target URL.
  3. In CapyScout → Workflows, add an action, pick HubSpot, and paste the URL. Map email, companyName, and scores to HubSpot properties.

Payload CapyScout POSTs to the HubSpot endpoint

{
  "id": 1234,
  "email": "jane@acme.com",
  "domain": "acme.com",
  "scenario": "signup_triage",
  "classification": "high_fit_lead",
  "fitScore": 88,
  "riskScore": 8,
  "confidence": 82,
  "recommendedAction": "Route to sales and follow up within 15 minutes.",
  "source": "signup_form",
  "accountId": 42
}

Custom webhook

Sends the flat Brief JSON to any HTTPS endpoint you control - your backend, a queue consumer, or an internal service. This is the same payload used by the Zapier and HubSpot destinations; only the label differs.

  1. Expose an HTTPS endpoint that accepts POST with a JSON body.
  2. In CapyScout → Workflows, add an action, pick Webhook, and paste your URL.
  3. Read the fields you need; respond 2xx to mark delivery successful.
POST https://your-service.com/hook
Content-Type: application/json

{
  "id": 1234,
  "email": "jane@acme.com",
  "domain": "acme.com",
  "scenario": "signup_triage",
  "classification": "high_fit_lead",
  "fitScore": 88,
  "riskScore": 8,
  "confidence": 82,
  "recommendedAction": "Route to sales and follow up within 15 minutes.",
  "source": "signup_form",
  "accountId": 42
}

Delivery is best-effort and logged to the workflow run history. Non-2xx responses are recorded as failed so you can spot broken endpoints in the Workflows UI.

Email

Sends a plain-text summary email to any address. Messages are sent from notifications@capyscout.com - no inbound setup required, just enter the recipient.

  1. In CapyScout → Workflows, add an action, pick Email, and enter the recipient address.
  2. Add notifications@capyscout.com to your allowlist if your mail server filters aggressively.

Message CapyScout sends

From: notifications@capyscout.com
Subject: CapyScout: high_fit_lead - jane@acme.com

Workflow "High-fit leads" fired.

Email:          jane@acme.com
Company:        Acme Inc
Scenario:       signup_triage
Classification: high_fit_lead
Fit / Risk:     88 / 8
Recommended:    Route to sales and follow up within 15 minutes.

Tag

An internal action that labels the matching Brief for later filtering. It makes no outbound HTTP call and needs no setup beyond choosing a tag name in the workflow action.

Ready to build?

Create an API key and make your first call in under a minute.

Get your API key