Docs

StormGTM API

Barometer reviews leads over a JSON API at https://stormgtm.com. Call it with curl, the Node client, the stormgtm CLI, or any MCP client. A condensed version for models lives at /llms.txt.

Quickstart

Every account starts with 100 free credits. Create a key in the dashboard, then:

export STORMGTM_API_KEY=sgtm_live_...

curl https://stormgtm.com/v1/check \
  -H "authorization: Bearer $STORMGTM_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "email": "jane.doe@acme.io",
    "context": { "name": "Jane Doe", "company": "Acme" }
  }'

Authentication

Send Authorization: Bearer sgtm_live_… on every request. Create and revoke keys in the dashboard; a key is shown once, when you create it. All request and response bodies are JSON.

STORMGTM_API_KEYYour key, sgtm_live_…. Read by the Node client, the CLI, and the MCP server.
STORMGTM_API_URLBase URL. Set it to https://stormgtm.com.

POST /v1/check

Check one lead synchronously. Fast checks usually finish in 1–4 seconds; SMTP servers that stall can take up to ~25 seconds.

Request
{
  "email": "jane@acme.io",            // required
  "tier": "fast" | "deep",            // default fast (1 credit); deep = 5 credits
  "smtp": true,                       // set false to skip the SMTP probe
  "context": {                        // all optional; more context, better verdicts
    "name": "Jane Doe", "firstName": "Jane", "lastName": "Doe",
    "company": "Acme", "companyDomain": "acme.io", "title": "CTO",
    "source": "github", "sourceUrl": "https://github.com/acme/api",
    "githubLogin": "janedoe", "linkedinUrl": "…", "notes": "…"
  },
  "policy": { … }                     // optional; overrides your account default
}
Response
{
  "id": "chk_…",
  "email": "Jane@Acme.io", "normalized": "jane@acme.io",
  "verdict": "deliverable" | "risky" | "undeliverable" | "unknown",
  "score": 0-100,
  "tier": "fast",
  "reasons": [{ "code": "smtp_accepted", "impact": "positive", "weight": 45, "detail": "RCPT accepted by aspmx.l.google.com" }],
  "facts": { "mx": "yes", "provider": "google", "smtp": "accepted", "catchAll": false, "spf": true, "dmarc": true, … },
  "policy": { "allowed": true, "violations": [] },
  "billable": true,
  "credits": 1
}

Verdicts

deliverableMailbox confirmed or strongly evidenced. Send.
riskyLikely delivers but unconfirmed: catch-all, role inbox, or weak evidence. Send carefully or enrich.
undeliverableWill bounce, or must not be mailed. Drop it.
unknownGreylisted, timed out, or no signal. Free. Retry later or use a batch (batches retry automatically).

policy.allowed is separate from the verdict: a lead can be deliverable and still outside your policy. Gate sends on both.

Reasons

The score starts at 50 and each reason adds its weight. A fatal reason makes the lead undeliverable regardless of the rest.

CodeWeightMeaning
syntax_invalidfatalNot a valid address
placeholder_local / placeholder_domainfatalyou@, firstname.lastname@, example.com, company.com
no_replyfatalnoreply@, mailer-daemon@, notifications@
disposable_domainfatalThrowaway providers (120k+ domains)
domain_typofatalgmial.com, acme.con — facts.suggestion holds the fix
no_mx / null_mxfatalDomain cannot receive mail
smtp_rejectedfatalMail server refused the mailbox
history_bouncedfatalA hard bounce was reported for this address
role_account−20info@, sales@, support@ — caps the verdict at risky
smtp_catch_all−15Domain accepts every address; mailbox not confirmed
smtp_accepted+45 (+15 on Yahoo and gateways)Mail server accepted RCPT TO
not_catch_all+5A random address was rejected, so acceptance is meaningful
history_replied / history_delivered+50 / +35Outcomes you or others reported
github_profile_email / github_commit_email+35 / +30Address is public on GitHub or authored commits
web_mention+20 (+28 on own domain)Exact address found on the public web
name_pattern_match / mismatch+12 / −12Local part vs. the lead's name
llm_verdict−20 … +20Reviewer model on ambiguous results (deep tier)

Policy

Set a default policy on your account, or send policy with a check or batch to override it. Violations come back in policy.violations and never change the score.

"policy": {
  "blockTlds": ["ru", "cn"],
  "blockDomains": ["qq.com"],
  "blockLocals": ["npm"],
  "blockRoleAccounts": true,
  "blockFreeMail": false,
  "blockAnonymous": true,
  "blockSocialHosts": true,
  "blockCatchAll": false
}

POST /v1/batches

Queue up to 10,000 leads. Greylisted or silent servers are retried after 5, 15, and 45 minutes before settling as unknown.

{
  "leads": [{ "email": "jane@acme.io", "context": { "name": "Jane Doe" } }, …],
  "tier": "fast",
  "policy": { … },
  "webhookUrl": "https://you.example/hooks/leads"   // optional, HTTPS only
}
→ 202 { "id": "bat_…", "status": "queued", "total": 2, "maxCredits": 2 }

Poll GET /v1/batches/:id?offset=0&limit=100 for status and results. When done we POST {"event":"batch.completed","batch":{"id","total","completedAt","summary"}} to your webhook.

POST /v1/outcomes

Report what happened after you sent. Bounces make future checks of that address undeliverable; replies and deliveries raise confidence. Addresses are stored hashed.

{ "email": "jane@acme.io", "kind": "bounced" | "delivered" | "replied" | "opened" | "complained",
  "occurredAt": "2026-10-01T12:00:00Z", "detail": "550 5.1.1" }

// or many at once
{ "outcomes": [ … up to 1000 … ] }

Other endpoints

GET /v1/meAccount, credit balance, pricing, 30-day usage.
GET /v1/checks?limit=50&before=…Recent checks.
GET /v1/checks/:idOne stored result.
GET /v1/batchesRecent batches.

Errors

Errors look like {"error":{"code":"insufficient_credits","message":"…"}}. Codes: missing_api_key, invalid_api_key (401), insufficient_credits (402), invalid_request (422, with issues), batch_too_large (413).

Agents and MCP

Give your agent the MCP server (stormgtm-mcp) with STORMGTM_API_KEY set, and it gets check_lead, check_batch, batch_status, report_outcome, and credits tools. Setup is in the Quickstart.

The rule we recommend agents follow:

  • Send only when verdict is deliverable and policy.allowed is true.
  • risky: enrich with more context and tier: "deep", or ask a human.
  • unknown is free: retry later or use a batch.
  • Report bounces and replies to /v1/outcomes.