Developers

Build on lead.box

Bring the companies visiting your website into your own tools: a read-only REST API, signed webhooks, Slack and Microsoft Teams alerts, and an MCP server for AI assistants.

Quickstart

  1. In lead.box, open Integrations → API and create a key.
  2. Make your first request. Every response is JSON and only contains data of your workspace.
  3. Add a webhook, Slack or Teams in the same place to receive events as they happen.
List companies from Germany
curl -H "Authorization: Bearer $LEADBOX_API_KEY" \
  "https://api.lead.box/v1/companies?since=2026-10-01T00:00:00Z&country=DE&exclude_list_id=<customers-list-id>&limit=50"
Response
{
  "data": [
    { "object": "company", "id": "…", "name": "Example GmbH", "domain": "example.com", "country": "DE",
      "industry": "Software", "pages_viewed": 7, "sessions": 2, "last_seen_at": "2026-10-24T09:12:00Z", … }
  ],
  "next_cursor": "MjAyNi0xMC0yNFQwOToxMjowMFp8…"
}

Authentication

Send your API key as a bearer token: Authorization: Bearer lbx_…. A key belongs to one workspace and can only read. Keep it on your server and never ship it to a browser or mobile app.

  • read:companies
    AllowsCompanies, segments, tags and lists.
  • read:visits
    AllowsVisits, sessions, summaries, returning companies and goals.
  • read:contacts
    AllowsPeople you saved or revealed yourself in lead.box. Off by default.

AI assistants can also connect without a key through OAuth 2.1, see MCP server.

REST API

Base URL: https://api.lead.box/v1. All endpoints are GET. Timestamps are ISO 8601 in UTC; empty values are null; the API is versioned in the path and only changes in a backwards-compatible way within v1.

  • GET /companies
    Scoperead:companies
    ReturnsIdentified companies, most recent activity first. Filters: since, q, country, industry, city, region, min_score, hot, min_pageviews, first_seen_since, tag_id, list_id, exclude_list_id, segment, site. Cursor paging; expand=visits,contacts.
  • GET /companies/{id}
    Scoperead:companies
    ReturnsOne company with tags, lists, score reasons and notes; expand=visits,contacts.
  • GET /companies/returning
    Scoperead:visits
    ReturnsCompanies back on the website after a quiet period, with the previous visit and days away (days, gap_days, limit).
  • GET /visits
    Scoperead:visits
    ReturnsPage views of identified companies (since, company_id, cursor, limit); group=sessions groups them into sessions.
  • GET /summary
    Scoperead:visits
    ReturnsOverview of a period (days or since, max 31 days): page views, sessions, active, new, hot and returning companies, top companies, pages, industries and countries.
  • GET /segments
    Scoperead:companies
    ReturnsSaved segments; api_filterable tells whether a segment can be used as a filter, api_unsupported names the reason if not.
  • GET /tags
    Scoperead:companies
    ReturnsTags of the workspace (ids for tag_id).
  • GET /lists
    Scoperead:companies
    ReturnsAccount lists of the workspace (ids for list_id and exclude_list_id).
  • GET /goals
    Scoperead:visits
    ReturnsConversion goals of the workspace.
  • GET /goals/conversions
    Scoperead:visits
    ReturnsGoal conversions of identified companies, newest first (days, goal_id, limit).
  • GET /sites
    ReturnsThe tracked website of the workspace.
  • GET /me
    ReturnsWorkspace, key name, scopes and limits of the calling key.

Saved segments work as a filter (segment=<id>) with the same meaning as in the app: countries and regions, industries, company size, tags, account lists, visited URL, visit counts and dates.

Company object
{
  "object": "company",
  "id": "…", "name": "Example GmbH", "domain": "example.com", "website": "https://example.com",
  "logo_url": "https://…", "description": "…", "industry": "Software",
  "size": "51-200", "employee_range": "51-200", "revenue_range": "10M-50M", "founded_year": 2012,
  "address": { "postal_code": "10115", "city": "Berlin", "region": "Berlin", "country": "DE", "continent": "EU" },
  "country": "DE", "city": "Berlin", "phone": "+49 30 000000",
  "linkedin_url": "https://www.linkedin.com/company/example",
  "social": { "linkedin": "https://www.linkedin.com/company/example", "crunchbase": null },
  "total_funding": null, "keywords": ["saas"], "tech_stack": ["HubSpot", "WordPress"],
  "score": 72, "is_hot": true, "icp_tier": "A", "lifecycle_stage": "mql",
  "score_reasons": { "tier": "hot", "factors": { … }, "summary": "…", "recommended_action": "…", "computed_at": "…" },
  "tags": [{ "id": "…", "name": "Prospect", "color": "#…", "lifecycle_stage": null }],
  "lists": [{ "id": "…", "name": "Q4 Targets" }],
  "notes": [{ "id": "…", "content": "…", "created_at": "…", "updated_at": null }],   // detail endpoint only
  "pages_viewed": 7, "sessions": 2, "first_seen_at": "…", "last_seen_at": "…",
  "last_page_url": "https://your-site.com/pricing", "last_referrer": "https://www.google.com/",
  "site": { "id": "…", "name": "Your site", "domain": "your-site.com" },
  "visits": [ … ],      // with expand=visits (last 90 days)
  "contacts": [ … ]     // with expand=contacts (needs read:contacts)
}
Visit object
{
  "object": "visit", "id": "…", "company_id": "…", "session_id": "…",
  "page_url": "https://your-site.com/pricing", "page_title": "Pricing",
  "referrer": "https://www.google.com/", "duration_seconds": 35, "visited_at": "…",
  "device_type": "desktop", "browser": "Chrome", "os": "macOS", "country": "DE", "city": "Berlin",
  "utm": { "source": "linkedin", "medium": "cpc", "campaign": "q4", "term": null, "content": null }
}
// group=sessions → { "object": "session", "session_id", "company_id", "started_at", "ended_at",
//   "duration_seconds", "referrer", "utm", "device_type", "country", "pages": [{ "url", "title", "visited_at" }] }

Pagination

List endpoints return up to limit items (1 to 100, default 50) and a next_cursor. Pass it as cursor to get the next page; it is null on the last page. Results are ordered by last activity, newest first.

Read all pages
let cursor = null;
do {
  const url = new URL('https://api.lead.box/v1/companies');
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);
  const page = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } }).then((r) => r.json());
  handle(page.data);
  cursor = page.next_cursor;
} while (cursor);

Errors

Errors use the HTTP status code and a JSON body with a stable machine-readable code and a human-readable message.

HTTP/1.1 400 Bad Request
{ "error": { "code": "invalid_filter", "message": "min_score must be 0-100" } }
  • 400
    Codeinvalid_filter, invalid_since, invalid_period, invalid_cursor, invalid_company_id
    MeaningA parameter is missing or malformed. The message names it.
  • 401
    Codeunauthorized
    MeaningNo key, an unknown key or a revoked key.
  • 403
    Codeinsufficient_scope, key_disabled, api_access_disabled
    MeaningThe key lacks a scope, or the key or the API access of the workspace was disabled by support.
  • 404
    Codenot_found, segment_not_found
    MeaningThe company or segment does not exist in this workspace.
  • 422
    Codesegment_unsupported
    MeaningThe saved segment uses OR groups or visitor-level fields that cannot be used as an API filter.
  • 429
    Coderate_limited, too_many_auth_failures
    MeaningA limit was reached. Wait for Retry-After seconds.
  • 504
    Codequery_timeout
    MeaningThe query took longer than 5 seconds. Narrow the filters or lower the limit.

Webhooks

lead.box sends a signed JSON POST (version v1) to your HTTPS endpoint. Each delivery carries the full company object and, where it applies, the visit or goal that triggered it. Contacts are never sent by webhook. Use the delivery id to ignore duplicates.

  • company.identified
    When it is sentA new company is identified on your website.
  • company.scored_hot
    When it is sentA company is classified as hot.
  • company.returned
    When it is sentA known company is back after at least 7 days without a visit (at most once per company and day).
  • goal.converted
    When it is sentAn identified company reaches one of your goals (at most once per goal, company and day); includes the goal.
  • visit.new
    When it is sentA visit of an identified company is recorded.
  • test.ping
    When it is sentSent by the "Send test" button in the settings.
Payload
{
  "version": "v1",
  "id": "8f0c…",              // delivery id, use for de-duplication
  "event": "company.identified", // | company.scored_hot | company.returned | goal.converted | visit.new | test.ping
  "created_at": "2026-10-18T09:12:00Z",
  "workspace_id": "…",
  "data": {
    "company": { …full company object as above, without notes/contacts… },
    "visit": { …visit object… },  // for visit.new, company.returned and goal.converted
    "goal": { "id": "…", "name": "Pricing page visited" },  // only for goal.converted
    "converted_at": "2026-10-18T09:11:40Z"                  // only for goal.converted
  }
}

Every request carries X-LeadBox-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 over <t>.<raw body> with your signing secret. Reject timestamps older than five minutes. Answer with a 2xx status within 10 seconds; failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h, and a webhook that keeps failing is paused.

Verify the signature
import crypto from 'node:crypto';

// rawBody = the exact request body string, header = X-LeadBox-Signature
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto.createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

Optional filters per webhook: only companies from selected countries, or leave out companies on an account list (for example existing customers).

Slack & Microsoft Teams

Paste an incoming webhook address from Slack, or a Teams workflow webhook address, under Integrations and pick the events. lead.box posts a short message in the language of the workspace owner: the company, industry, size and location, the page it looked at, and a button that opens the company in lead.box. Country and list filters apply here too. No code needed.

MCP server

Connect AI assistants that support remote MCP servers (Streamable HTTP) to https://api.lead.box/mcp. All tools are read-only and only see one workspace.

  • get_activity_summary
    What it doesOverview of a period: page views, sessions, active, new, hot and returning companies plus top lists. A good first call for "How was the week?".
  • search_companies
    What it doesSearch companies with the same filters as the REST API, including saved segments, tags and lists.
  • get_company
    What it doesFull profile of one company, optionally with recent sessions and contacts.
  • get_company_timeline
    What it doesChronological sessions and pages of one company.
  • list_recent_visits
    What it doesRecent page views of identified companies, grouped into sessions.
  • list_hot_companies
    What it doesCompanies currently flagged hot.
  • list_returning_companies
    What it doesCompanies that came back after a quiet period.
  • list_goal_conversions
    What it doesGoals and recent conversions of identified companies.
  • list_segments
    What it doesSaved segments (ids for search_companies).
  • list_tags_and_lists
    What it doesTags and account lists (ids for search_companies).

Without a key: add the MCP address as a custom connector in your assistant. You sign in with lead.box, choose the workspace and allow read access (OAuth 2.1). You can disconnect apps any time in lead.box under Integrations → Connected apps.

With a key:

claude mcp add --transport http leadbox https://api.lead.box/mcp \
  --header "Authorization: Bearer $LEADBOX_API_KEY"

Questions to try

  • How was our website traffic this week? Which companies were most active?
  • Which companies came back after more than two weeks, and what did they look at?
  • Show German software companies that visited our pricing page in the last 30 days.
  • Which companies reached the goal "Pricing page visited" yesterday?
  • Summarise the visit history of example.com before my call.

Limits

  • Requests per key / OAuth app
    Value60 / minute · 10,000 / day (UTC)
  • Requests per workspace (all keys + apps)
    Value120 / minute · 20,000 / day (UTC)
  • Page size (limit)
    Value1–100, default 50 · cursor paging only
  • expand=visits
    Valuemax 50 visits per company, last 90 days
  • Query time
    Valuemax 5 s per query, else 504
  • MCP result
    Valuemax 50 rows · 50,000 characters ("more_available" hint) · max 10 calls per batch
  • Invalid keys per IP
    Value20 within 10 minutes → blocked for 15 minutes
  • Webhook size
    Valuemax 256 KB (otherwise slimmed, truncated: true)
  • Webhook delivery
    Value10 s timeout · max 2 in parallel per workspace

Exceeding a limit returns HTTP 429 with Retry-After and X-RateLimit-* headers.

Data & privacy

  • Company-level data only: the API, webhooks and MCP never return visitor IP addresses or visitor IDs.
  • Contacts are only people you saved or revealed yourself in lead.box, and only with the read:contacts scope.
  • URLs are returned without query strings.
  • Every key, app and webhook is bound to one workspace; another workspace's data is never reachable.

Changelog

  1. 2026-10-24

    • New REST endpoints: /summary, /companies/returning, /goals, /goals/conversions, /tags, /lists.
    • New company filters: city, region, min_pageviews, first_seen_since, tag_id, list_id, exclude_list_id.
    • Saved segments now work as API filters with the same meaning as in the app.
    • New webhook events company.returned and goal.converted; per-webhook filters by country and account list.
    • Slack and Microsoft Teams messages; managers can manage webhooks.
    • MCP server: four new tools (activity summary, returning companies, goal conversions, tags and lists).
  2. 2026-10-18

    • REST API v1 at api.lead.box, signed webhooks and the remote MCP server.

B2B Lead Identification Platform

lead.box — Identify the companies visiting your website

lead.box turns anonymous B2B website visitors into named companies. GDPR-first, first-party only, with EU data processing.

What lead.box does

How it works

  1. Add a single lightweight tracking snippet to your website.
  2. lead.box identifies the companies behind each visit using first-party IP intelligence.
  3. Hot leads are scored, enriched with contact data and exported as a file for your sales team.

Quick links