SMB Sales Boost REST API Documentation

Base URL: https://smbsalesboost.com/api/v1

Response Format: JSON (application/json)

Authentication: Bearer token (API key)

Table of Contents

Introduction | Programmatic Purchase | Authentication | Rate Limits | User Profile | Enrichments | Leads | Filter Presets | Keyword Lists | Email Schedules | Export Formats | Export History | Lead Export History | Account Settings | AI Features | Export Blacklist | Error Handling | Credits & Subscription | MCP Server | Integrations

Introduction

The SMB Sales Boost API provides RESTful access to our lead database, enrichments, export tools, filter management, AI features, credits and billing, and CRM and webhook integrations. The same API key also works with our MCP server. All API endpoints return JSON (file downloads aside) and, apart from the purchase endpoints, require authentication via Bearer token.

Quick Start

Base URL
https://smbsalesboost.com/api/v1
Response Format
JSON (application/json)
Authentication
Bearer token (API key)
Protocol
HTTPS only

Plan Requirement

API access is available for users on any paid plan (Starter, Growth, Scale, Platinum, or Enterprise). View plans to get started.


Programmatic Purchase

Purchase a subscription entirely via API - no web signup or onboarding required. This is ideal for AI agents and automated workflows that need programmatic access to SMB Sales Boost.

How It Works

(1) Call POST /api/v1/purchase with your email and desired plan, (2) complete checkout at the returned Stripe Checkout URL, (3) receive your API key by email at that address. The API never returns the key itself.

If that email already has an SMB Sales Boost account, the purchase must first be confirmed from the email sent to that address; the API key is emailed after that.

14-Day Free Trial

Starter, Growth and Scale start with a 14-day free trial by default. A payment method is required, nothing is charged until the trial ends, and the trial includes 50% of the plan's monthly credits; the plan price is then charged automatically unless the subscription is cancelled first. Send "trial": false to skip the trial and pay at checkout. Platinum and Enterprise never trial. One trial per person: the email is checked at purchase and the payment method after checkout. All charges are final; there are no refunds.

Step 1: Initiate Purchase

POST /api/v1/purchase
Create a new subscription purchase session. Returns { data: { checkoutUrl, expiresAt, plan, trial, message } }. The API key is not returned: it is emailed to the purchase address once checkout is complete. No authentication required. Rate limited to 5 requests per hour per IP.

Parameters: email (required, string), plan (required, string - "starter", "growth", "scale", "platinum", or "enterprise"), trial (optional, boolean, default true - only false skips the free trial and charges the plan price at checkout).

The trial object in the response reports eligible, days, credits, endsAt, note and, when there is no trial, a reason: not_requested, plan_not_eligible, trial_already_used or currently_subscribed.

Errors: 400 invalid_email or invalid_plan, 403 forbidden or disposable_email, 409 already_subscribed (also while that email is on a free trial; use change-plan instead), 410 plan_retired, 503 when payment processing is unavailable.

Step 2: Complete Checkout

Open the checkoutUrl in a browser to complete checkout on Stripe's hosted page. The session expires after 24 hours. After checkout, the system automatically provisions your account and generates an API key.

Step 3: Receive Your API Key by Email

Your API key (prefixed smbk_) is emailed to the purchase address once checkout is complete. Store it securely. If that address already has an SMB Sales Boost account, confirm the purchase from the email sent to that address first; the key follows.

POST /api/v1/claim-key is kept for backward compatibility only. It needs a claim token, which current purchases no longer issue, so wait for the emailed API key instead.

Account Setup (Highly Recommended)

Programmatic purchases skip the onboarding wizard. To get the most out of SMB Sales Boost, configure your account via the API before searching or exporting leads:

  1. PATCH /api/v1/me - Set your company name and website
  2. POST /api/v1/ai/suggest-categories - Get AI-powered category suggestions
  3. POST /api/v1/ai/generate-keywords - Generate targeting keywords

Authentication

All API requests require a valid API key sent as a Bearer token in the Authorization header. Generate your API key from the Dashboard → API tab (available on all paid plans).

Example Request

curl -H "Authorization: Bearer smbk_your_api_key_here" \
  https://smbsalesboost.com/api/v1/me

API Key Format

All API keys are prefixed with smbk_ for easy identification.

Security Best Practice

Never expose your API key in client-side code, public repositories, or browser requests. Store it securely as an environment variable.


Rate Limits

API requests are rate-limited to ensure fair usage. Rate limit information is included in response headers.

Rate Limit Tiers

General Endpoints
600 requests per minute per API key
Export Endpoints
5 requests per minute per API key
AI Endpoints
5 requests per minute per API key

Integration endpoints (/api/v1/integrations) also share one budget per account of 60 requests per minute, counted together with the dashboard's Integrations tab and every API key of the account (REST and MCP). Starting a CRM connection is also limited to 30 per minute per account.

The MCP endpoint (POST /mcp) accepts up to 60 requests per minute per client IP address. Each MCP tool call also counts against the limits of the REST endpoints it calls.

Rate Limit Headers

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRemaining requests in the current window
X-RateLimit-ResetWhen the window resets, as an ISO 8601 date-time
Retry-AfterSeconds to wait before retrying (only on 429)

The X-RateLimit-* headers describe the most specific limit that applies to the request: the 5 per minute limit on export and AI endpoints, the per-account integration budget on integration endpoints (60 per minute, or 30 on CRM connect), and the 600 per minute limit on every other request made with an API key. On a rate-limit 429 (error Rate limit exceeded) they describe the limit that was hit. The 600 per minute limit still counts every request made with an API key. The purchase endpoints, which take no API key, have their own per-IP limits instead (see Programmatic Purchase).

429 Responses

A rate-limited request gets {"error": "Rate limit exceeded", "message": "...", "retryAfter": 42}, where retryAfter matches the Retry-After header. The lead search endpoints (GET /api/v1/leads, GET /api/v1/leads/preview, POST /api/v1/leads/export and the email schedule trigger) can also return 429 lead_query_capacity_busy with Retry-After: 10 when search capacity is busy. Both 429s carry the X-RateLimit-* headers, so tell them apart by the body's error field (Rate limit exceeded or lead_query_capacity_busy). Both are refused before anything is charged, so retry after the delay.


User Profile

Manage your user profile including subscription details and account settings.

Endpoints

GET /api/v1/me
Retrieve your user profile including subscription details and account settings.
PATCH /api/v1/me
Update your profile. Only the fields provided will be updated. Allowed fields: firstName, lastName, companyName, companyWebsite.

Response Fields

The user profile response includes: id, email, firstName, lastName, companyName, companyWebsite, smbType (always "other", the Main database), subscriptionPlan, subscriptionStatus, isSubscribed, freeLeadsUsed, onboardingCompleted, targetCategories, targetLocations, monthlyCredits, monthlyCreditsUsed, monthlyCreditsRemaining, permanentCredits, totalCreditsRemaining, and creditOverageRate.


Enrichments

Enrich a list of website URLs you already have. Each URL is checked against our database first; anything we do not already have is fetched live. We never add the URLs you submit, or the results we find for them, to our lead database, which we keep curated so it stays clean and focused on high-quality leads.

What each URL costs

1 credit
A URL in our database that you have not bought before, including its buying signals and category enrichments.
0.1 credit
A URL we do not have and fetch live for you.
0 credits
A URL you have already exported, one we got no data for, or one without the contact details you asked for.

A URL found in our database is never fetched live, so lowering databaseMatchPercent cannot be used to buy database records at the live price.

Starting a run

POST /api/v1/enrichments with { "urls": [...] }, up to 100,000 URLs after deduplication by host. Send the full URL including any path - a deep link often carries better contact data than the root.

maxCredits
Most credits this run may spend. Omit to allow your whole available balance, which is the default.
databaseMatchPercent
0-100, default 100. Caps what share of maxCredits may go to NEW database matches. Unused database budget flows to live fetching, never the reverse. Records you have already paid for are always returned free and are not affected by this.
contactTypes
phone, email or either: the minimum contact details a result must carry to be delivered and charged. phone returns every result with a phone number, including those that also have an email; email returns every result with an email, including those that also have a phone number. Default either, meaning a phone number OR an email - the widest setting, not the most restrictive.
emailResults
Email you a download link when the file is ready. Defaults to your account setting. The file is always downloadable, so there is no flag to disable the download.

Runs are asynchronous

Submitting returns a run id immediately and a large list can take hours. Poll GET /api/v1/enrichments/:id until status is completed or partial, then call GET /api/v1/enrichments/:id/download for a short-lived presigned URL.

Credits are charged in two stages - database matches when they are matched, live results when the file is published - so credits.final tells you whether the total is settled.

Every URL is accounted for

The file has one row per URL you submitted, each with a status column. Only the first two cost anything.

matched_paid
In our database, new to you. 1 credit.
scraped
Fetched live. 0.1 credit.
matched_free
In our database, you already bought it. Free.
scrape_failed
No data came back for it - unreachable, or nothing usable found. Free.
skipped_db_budget
A database match outside your credit limit. Free.
skipped_scrape_budget
A live result outside your credit limit. Free.
matched_no_contact
Matched, but without the contact type you asked for. Free.
scraped_no_contact
Fetched, but without the contact type you asked for. Free.
skipped_invalid
Not a usable URL. Free.

Leads

Search, filter, and export leads from our database. Supports keyword filtering, location filtering, industry filtering, and pagination.

Lead Database

Every lead comes from the Main database (API value other), which covers small and medium businesses of all types. Contact info includes both phone numbers and email addresses. The database parameter is optional. Every value is accepted and resolves to the Main database, so existing integrations keep working unchanged.

How Search Works: Two-Phase Query Model

Phase 1 - Include (OR Logic)

All positive keywords and include terms are combined with OR logic. A lead matches if any positive keyword matches in any of the OR group search columns.

Default OR group columns
Registered URL, Crawled URL, Company Name, Categories (AI Enrichment)

Phase 2 - Exclude (AND Logic)

Negative keywords are checked against the same columns as positive keywords. A lead is excluded if any negative keyword matches in any of those columns.

URL Space-to-Wildcard

For URL columns, spaces in search terms are automatically replaced with % wildcards. For example, "dental clinic" becomes %dental%clinic% to match URLs like example.com/dental-clinic.

AI Category Estimation

Leads in the Main database are progressively enriched by AI with estimated business type categories. Each lead may include an aiCategoryEstimation field containing an array of 1-3 category names, or null if not yet classified.

Endpoints

GET /api/v1/leads
Search and filter leads. Requires at least one positive filter (positiveKeywords, nameIncludeTerms, etc.).
GET /api/v1/leads/preview
Preview leads with masked contact info (masked, not hashed: for example (512) ***-**42 and hel*@domain.com). Accepts the same filters as GET /api/v1/leads, including the contact term filters (emailPrimaryInclude, phonePrimaryInclude and the rest) and the company-column include, exclude and empty filters. Does not consume credits. Leads you already purchased come back unmasked (contactExported: true); pass excludePurchased=true to return only not-yet-purchased leads.
POST /api/v1/leads/export
Export filtered leads to CSV, JSON, or XLSX. Subject to export rate limits. Supports credit-optimized ordering via maxCredits and maxResults parameters. Pass excludePurchased=true to export only not-yet-purchased leads. Each file's data is plain UTF-8 text for csv and json files and base64 for xlsx files. Pass inline=false (body field or ?inline=false) to omit each file's body and receive a per-file exportId plus downloadUrl instead - the recommended shape for large exports. XLSX is capped at 20,000 rows per file; for larger runs use a csv export format, or an xlsx format with splitFiles true, splitMode max_rows, maxRowsPerFile of 20,000 or less and includeTotal false. Any xlsx export that would put more than 20,000 rows in one file (a single file, a split part, or the includeTotal file, which holds every lead) returns 400 export_too_large after the leads are charged and recorded as received. Do not simply repeat it: an export picks new leads first, so a repeat can charge for different leads. Re-run it with maxCredits 0 (and without excludePurchased) and a fixed format, which returns the leads you already have at no cost, or re-export them with POST /api/v1/lead-export-history/re-export.
GET /api/v1/leads/other/schema-types
Returns a sorted list of all distinct website schema types found in the Main database. Use these values with the websiteSchemaFilter parameter on GET /api/v1/leads.

Key Parameters

positiveKeywords (required), negativeKeywords, orColumns, stateInclude, stateExclude, cityInclude, cityExclude, zipInclude, zipExclude, streetInclude, streetExclude, minNumLocations, maxNumLocations, minRatingValue, maxRatingValue, minReviewCount, maxReviewCount (numeric ranges), buyingSignalTypeFilter, lastBuyingSignalFrom, lastBuyingSignalTo, sortBy, sortOrder, page, limit, database, and various column-specific include/exclude terms. Empty/non-empty filters per column are also supported via streetEmptyFilter, cityEmptyFilter, stateEmptyFilter, zipEmptyFilter plus phonePrimaryEmptyFilter, phoneTotalEmptyFilter, emailPrimaryEmptyFilter, emailTier1EmptyFilter through emailTier6EmptyFilter, descriptionEmptyFilter, and the new firmographic and technology columns such as employeeCountEmptyFilter, foundingDateEmptyFilter, isChainEmptyFilter, priceRangeEmptyFilter, legalNameEmptyFilter, industryNaicsEmptyFilter, dunsEmptyFilter, founderEmptyFilter, founderEmailEmptyFilter, founderPhoneEmptyFilter, openingHoursEmptyFilter, areaServedEmptyFilter, offersEmptyFilter, brandEmptyFilter, sloganEmptyFilter, logoUrlEmptyFilter, socialProfilesEmptyFilter, platformEmptyFilter, bookingOpsCrmSoftwareEmptyFilter, reputationChatSoftwareEmptyFilter, marketingEmailSoftwareEmptyFilter, adPixelsEmptyFilter, paymentFinancingSoftwareEmptyFilter, emailPrimaryMxEmptyFilter, emailDomainMxEmptyFilter, emailSecurityEmptyFilter, addressSourceEmptyFilter. The firmographic and technology columns from employeeCount through paymentFinancingSoftware also support include/exclude term filters (e.g. employeeCountInclude/employeeCountExclude, founderEmailInclude/founderEmailExclude); founder email and phone values stay masked until a lead is unlocked or exported. The Email Primary Provider, Website Email Provider, Website Email Security, and Address Source columns have no include/exclude terms; they use exact-value multi-choice matching via emailPrimaryMxValues, emailDomainMxValues, emailSecurityValues, and addressSourceValues - each a JSON array of exact values matched with IN semantics. Number of Job Postings uses minOpenPositions/maxOpenPositions, and the short description uses descriptionIncludeTerms/descriptionExcludeTerms. Each empty filter accepts 'any' | 'only_empty' | 'exclude_empty'.

Buying Signal Type Filtering

Every lead carries a Buying Signal Type list describing why its Last Buying Signal timestamp last changed. Filter on it with buyingSignalTypeFilter (comma-separated values). Values: Newly Added, Phone Primary, Total Phones, Email Primary, Tier 1 Emails, Tier 2 Emails, Address Full, plus per-source social profile signals - Instagram, LinkedIn, Facebook, YouTube, TikTok, X (Twitter), Google Maps, Yelp, Pinterest, and Other Social - recorded when a social profile for that source is added or changed for a business. Social profile changes also bump the lead's Last Buying Signal timestamp, so recently-active businesses surface under lastBuyingSignalFrom / lastBuyingSignalTo date filters. Last Buying Signal is also the default sort (sortBy=lastBuyingSignal).

The former names updateReasonFilter, lastUpdatedFrom, lastUpdatedTo and sortBy=lastUpdated are still accepted (ignored when the new name is also sent), and lead responses still include a deprecated Last Updated key with the same value as Last Buying Signal. Default-format export files (POST /api/v1/leads/export, Export History downloads, email schedule attachments) name these columns Last Buying Signal and Buying Signal Type in their CSV/XLSX headers and JSON keys; they no longer have Last Updated / Update Reason columns. To keep the old header names, export with a custom export format (formatId) that maps lastUpdated and lastUpdatedReason to the names you need.

Credit-Optimized Export Parameters

The export endpoint supports additional parameters for controlling credit usage:

maxLeads
Cap total leads exported. Leads over the cap, and new leads cut because credits ran short, are counted in the response's overflowCount and kept in the lead reservoir while it has room (one pool per account, shared with your email schedules, up to your plan's monthly credit allowance; leads that do not fit are not kept), then added to your next export that also sets maxLeads.
maxResults
Cap total results returned (both new and previously-exported leads). New leads are prioritized first, then previously-exported leads fill remaining slots.
maxCredits
Cap credits spent on this export. Only new (credit-consuming) leads count toward this cap. The export is also capped at your spendable balance, so the effective cap is the smaller of the two. Set to 0 to only receive previously-exported leads at no credit cost.
excludePurchased
When true, export only leads you have not purchased yet (already-exported leads, matched by lead id or shared contact point, are skipped). Credit and dedup math are unchanged. Default false.

On credit plans, GET /api/v1/leads and POST /api/v1/leads/export always return leads in credit-optimized order: new leads (credit-consuming) sorted first, then previously-exported leads, each group sorted by Last Buying Signal descending (or your custom sort). New leads are cut to the smaller of maxCredits and your spendable balance (credit balance plus any overage-budget headroom); when the balance was the limit the response has the X-Credits-Capped: true header, so omitting maxCredits caps the request at your balance instead of failing. 402 insufficient_credits only happens if a concurrent request spends the balance between the check and the charge.

What Costs a Credit

Each new lead returned by GET /api/v1/leads or POST /api/v1/leads/export costs 1 credit. Charging is sticky: a lead you have already received through any channel (dashboard, API, MCP or email schedule) is never charged again, even if its data changed later, and a new lead that shares a primary phone or primary email with a lead you already received is also free.

Purchased-Lead Reveal on Preview

The preview endpoint reveals leads you have already purchased: any preview lead that matches a prior export (by lead id or by a shared phone or email contact point) is returned unmasked with contactExported: true, the same as on the dashboard. Leads you have not bought stay masked. Pass excludePurchased=true on preview to return only not-yet-purchased leads. A shared contact point here includes the lead's Total Phones and Email Tier values, while charging compares primary phone and primary email only, so a lead revealed only through one of those lists can still cost a credit.


Filter Presets

Save and manage reusable filter configurations. Create, update, delete, and list your saved filter presets for quick access to your most-used search criteria.

Endpoints

GET /api/v1/filter-presets
List all your saved filter presets.
POST /api/v1/filter-presets
Create a new filter preset. Requires a name and filters object.
DELETE /api/v1/filter-presets/:id
Delete a filter preset by ID.

Preset Object

Each preset includes: id, name, filters (containing positiveKeywords, stateInclude, etc.), and createdAt timestamp.


Keyword Lists

Manage keyword lists for targeted lead searching. Create lists of include and exclude keywords to refine your lead searches and automate filtering.

Endpoints

GET /api/v1/keyword-lists
List all your keyword lists.
POST /api/v1/keyword-lists
Create a new keyword list. Requires name (trimmed, 1-255 characters), type (positive/negative) and keywords (a non-empty array of strings); sourceCategories is optional (an array of strings, or null). Any other field is ignored, and invalid input returns 400 validation_error.
PUT /api/v1/keyword-lists/:id
Update an existing keyword list.
DELETE /api/v1/keyword-lists/:id
Delete a keyword list by ID.

Keyword List Object

Each list includes: id, name, type (positive or negative), keywords array, and createdAt timestamp.


Email Schedules

Set up automated email delivery of leads matching your saved filters. Configure delivery frequency, filter presets, export format preferences, and multi-recipient splitting.

Endpoints

GET /api/v1/email-schedules
List all your email export schedules.
POST /api/v1/email-schedules
Create a new email schedule. Requires name, filterPresetId, intervalValue (a whole number from 1 to 720), intervalUnit and recipients. isActive defaults to true: an active schedule sends its first email within about 15 minutes; send false to create it paused.
PATCH /api/v1/email-schedules/:id
Update an existing email schedule. All fields are optional; intervalValue must be a whole number from 1 to 720.
DELETE /api/v1/email-schedules/:id
Delete an email schedule by ID.
POST /api/v1/email-schedules/:id/trigger
Manually trigger an email schedule to send immediately.

Schedule Object Fields

Each schedule includes: id, name, filterPresetId, exportFormatId, intervalValue, intervalUnit, isActive, pauseReason (null, "user", or "insufficient_credits"), recipients (array of objects with email and optional splitParts), distributionMode, fullCopyRecipients, maxLeadsPerEmail, lastSent, totalSentCount, createdAt.

Max Leads Per Email

maxLeadsPerEmail (optional, 1-100,000, null for no cap) limits how many leads one email carries. New leads that do not fit, and new leads your credits could not cover, are kept in the lead reservoir (one pool per account, shared by your schedules and API exports, up to your plan's monthly credit allowance; when it is full, more leads are not kept) and sent with a later email. On credit plans, leads you already received that do not fit are not kept. Schedules pause only when no credits are left to spend.

Combined File Feature

When file splitting is enabled, you can configure a combined file that includes an assignee column and file name column, sent to dedicated combined recipients.


Export Formats

Customize which columns are included in your lead exports. Create and manage export format templates for consistent data delivery. Supports CSV, JSON, and XLSX file types.

Endpoints

GET /api/v1/export-formats
List all your export format templates.
GET /api/v1/export-formats/:id
Get a specific export format by ID.
POST /api/v1/export-formats
Create a new export format with field mappings.
PATCH /api/v1/export-formats/:id
Update an existing export format.
DELETE /api/v1/export-formats/:id
Delete an export format by ID.
POST /api/v1/export-formats/:id/set-default
Set an export format as the default.

Export Format Fields

Each format includes: id, name, fileType, startRow, fieldMappings, isDefault, splitFiles, splitMode, maxRowsPerFile, numberOfParts, includeTotal, includeHeaders, databaseType (always "other", the Main database), combinedAssigneeColumnName, combinedFileNameColumnName, createdAt.

File Splitting Options

max_rows
Split by max rows per file (requires maxRowsPerFile, min 10)
equal_parts
Split into equal parts (requires numberOfParts, min 2)
parts_max_rows
Number of parts with max rows each (requires both)

XLSX files hold at most 20,000 rows each. An export that would put more than 20,000 rows in one xlsx file (a single file, a split part, or the includeTotal file, which holds every lead) returns 400 export_too_large, so an xlsx format for large exports needs splitFiles true, splitMode max_rows, maxRowsPerFile of 20,000 or less and includeTotal false.


Export History

View your past exports with details including date, row count, filters used, and export format. Each export file is kept for 90 days and can be downloaded again at any time in that window. After that, re-export the leads from your lead-level export history at no credit cost.

Endpoints

GET /api/v1/export-history
List your export history with file details including fileName, fileType, leadCount, fileSize, formatName, databaseType, createdAt, expiresAt (when the file stops being downloadable), downloadAvailable, and unavailableReason.
GET /api/v1/export-history/:id/download
Download a previously exported file by its history ID. Returns the raw file. Files can be downloaded for 90 days after the export; an expired or unavailable file returns 410.
GET /api/v1/export-history/:id/download-url
Get a short-lived direct download link for the file (valid for 15 minutes). Fetch the link without an Authorization header.

Lead Export History

Lead Export History has one entry per lead you have received, through any channel, with a snapshot of the lead's data from its most recent export or refresh. Refreshing and re-exporting these leads is always free: no credits are charged for leads you already received, even if their data has changed since.

Sticky export rule: once a lead is in your export history, it is free to re-export or refresh at any time. Credits are charged only for leads that are new to you (see Credits & Subscription).

Endpoints

GET /api/v1/lead-export-history
List your entries, newest export first, with pagination and optional filters. Each entry is one lead, with the time and channel of its most recent export and its stored snapshot (fields below). An invalid limit, offset or date returns 400.
GET /api/v1/lead-export-history/ids
Every tracking id matching the filters (databaseType, dateFrom, dateTo, exportMethod), newest first, with no pagination: {"data": [1001, 1002], "totalCount": 2}. Use it to select entries for a bulk refresh or re-export.
POST /api/v1/lead-export-history/refresh
Replace the stored snapshots with the latest live data. No credits charged. Send trackingIds or a filters object (databaseType, dateFrom, dateTo, exportMethod) to refresh every matching entry, at most 5,000 entries per request (a filters object that matches more than 5,000 entries, or none, returns 400). An invalid filters value also returns 400 bad_request: a dateFrom or dateTo that is not a valid date with a year from 1 to 9999 (for example 'filters.dateFrom must be a valid date (e.g. 2026-08-18)'), or a databaseType or exportMethod that is not a string. When a trackingIds array is sent, the filters are ignored and not checked. Returns {"data": {"results": [...], "refreshed": 2}}. In each result, changed says whether the lead was updated since its snapshot (by its last-updated date), changedFields lists the changed fields (kept until a refresh-and-export clears them), and refreshed is false for an id that is not in your history or a lead that is no longer in the database. A refresh that finds newer data for a lead sends lead.updated to your integrations, unless the lead is on your export blacklist.
POST /api/v1/lead-export-history/re-export
Download the selected entries again as one file built from their stored snapshots (live data only for an entry that has no snapshot; nothing is refreshed). No credits charged. trackingIds is required (up to 5,000); formatId is optional (one of your export formats; omitted or not found means a CSV with the default columns). Returns the file itself, not a JSON envelope.
POST /api/v1/lead-export-history/refresh-and-export
Refresh the snapshots with live data, clear their changed-field markers, then download them as one file. No credits charged. Send trackingIds or a filters object (at most 5,000 entries, with the same 400 for an invalid filters value, as for refresh), plus an optional formatId (as for re-export). Everything is checked before any entry changes: a 400 or 404 response leaves the entries unchanged (snapshots, leadLastUpdated and lastChangedFields). Like refresh, it sends lead.updated to your integrations for each lead it finds newer data for, unless the lead is on your export blacklist. Returns a file like re-export; its name starts with refreshed-export-.

List Parameters

ParameterDescription
limitNumber of entries per page (1-200, default 50)
offsetPagination offset (default 0)
databaseTypeFilter by database: 'other' (the Main database). Any value is accepted and resolves to the Main database. Omit to list every export.
dateFromOnly entries whose most recent export is on or after this date (YYYY-MM-DD)
dateToOnly entries whose most recent export is on or before this date (YYYY-MM-DD; the whole day counts)
exportMethodFilter by the channel of each lead's most recent export: 'dashboard', 'api' (REST API and MCP), 'email_schedule', 'crm_push' (pushed to an integration from the dashboard, with POST /api/v1/integrations/:id/push-lead or with the MCP tool push_lead_to_integration) or 'enrichment' (a database match paid for in an enrichment run)

Example Response

{
  "data": [
    {
      "id": 1001,
      "userId": "usr_abc123",
      "leadId": 42,
      "databaseType": "other",
      "exportedAt": "2026-05-01T10:00:00.000Z",
      "leadLastUpdated": "2026-04-28T08:00:00.000Z",
      "exportMethod": "crm_push",
      "leadSnapshot": {
        "wCompanyName": "Acme Roofing",
        "wPhonePrimary": "(555) 123-4567",
        "wEmailPrimary": "[email protected]"
      },
      "lastChangedFields": null,
      "lead": {
        "wCompanyName": "Acme Roofing",
        "wPhonePrimary": "(555) 123-4567",
        "wEmailPrimary": "[email protected]"
      }
    }
  ],
  "totalCount": 245,
  "limit": 50,
  "offset": 0
}

Entry Fields

Notes


Account Settings

Every account uses the Main database (API value other). The database settings endpoint is kept for backward compatibility with existing integrations.

Endpoints

GET /api/v1/settings/database
Get the lead database your account uses. currentDatabase is always "other" (the Main database).

Response Fields

currentDatabase (always "other"), canSwitch (boolean), daysRemaining (number), lastSwitchedAt (ISO timestamp or null).


AI Features

Access AI-powered tools including keyword suggestions, category recommendations, and website analysis. Leverage machine learning to optimize your prospecting strategy.

Rate limit: AI endpoints are limited to 5 requests per minute.

Endpoints

POST /api/v1/ai/suggest-categories
Get AI-powered category suggestions based on your business type and target market. Requires companyName, companyDescription, and productService.
POST /api/v1/ai/generate-keywords
Trigger AI keyword generation based on your account's target categories and business profile. This is an asynchronous operation - keywords will be generated in the background and saved to your keyword lists.

Category Suggestion Parameters

Required: companyName, companyDescription, productService. Optional: companyWebsite, excludeCategories. Suggestions are drawn from the Main database's categories.


Export Blacklist

Manage your export blacklist to keep specific domains out of your exports and out of integration pushes: pushing a blacklisted database lead (POST /api/v1/integrations/:id/push-lead with a leadId, or the MCP tool push_lead_to_integration) returns 400 before anything is charged or sent, and a Lead Export History refresh sends no lead.updated for a blacklisted lead. Your own data pushed with {"lead": {...}} is not checked. Add and remove entries to prevent duplicate outreach.

Endpoints

GET /api/v1/export-blacklist
List all your blacklist entries with entryType (domain), value, and createdAt.
POST /api/v1/export-blacklist
Add entries to your export blacklist. Requires an entries array with entryType and value for each entry.
DELETE /api/v1/export-blacklist/:id
Remove a blacklist entry by ID.

Entry Types

domain
Block all leads from a specific domain (e.g., competitor.com)

Error Handling

All errors follow a consistent format with an error code and a human-readable message. Validation errors (400 validation_error, or 400 invalid_request from POST /api/v1/enrichments) can also include a details array with one entry per problem, each with a path (the field, as an array of keys such as ["status"]) and a message.

Error Response Format

{
  "error": "bad_request",
  "message": "No valid fields provided. Allowed: firstName, lastName, companyName, companyWebsite"
}

HTTP Status Codes

StatusMeaning
200OK - Request succeeded
201Created - Resource created successfully
400Bad Request - Invalid parameters or missing required fields
401Unauthorized - Invalid or missing API key
402Payment Required - insufficient_credits: lead searches and exports are capped at your spendable balance, so they return it only when a concurrent request spent the balance first; POST /api/v1/integrations/:id/push-lead returns it whenever a lead that is new to you cannot be paid from your balance or overage budget. Also a declined card on a purchase
403Forbidden - API access not available on your plan
404Not Found - Resource does not exist
409Conflict - the request conflicts with the current state (for example integration_exists, integration_limit, conflict when an integration changed while your update was being applied, or a plan change still in progress)
429Too Many Requests - Rate limit exceeded, or lead_query_capacity_busy (tell them apart by the body's error field; both carry the X-RateLimit-* headers); retry after the Retry-After header
500Internal Server Error - Something went wrong on our end
502Bad Gateway - provider_error: a connected CRM's API failed (integration field and pipeline endpoints)
503Service Unavailable - temporary; retry after the Retry-After header (except provider_unavailable on integration endpoints, which has no Retry-After and is not temporary)

Credits & Subscription

All paid plans (Starter, Growth, Scale, Platinum, and Enterprise) use a credit-based system where each new lead queried or exported costs 1 credit. Charging is sticky: a lead you have already received through any channel (dashboard, API, MCP or email schedule) is never charged again, even if its data changed later, and a new lead that shares a primary phone or primary email with a lead you already received is also free. Lead searches and exports are always capped at your spendable balance (credit balance plus any overage-budget headroom) instead of failing, with the X-Credits-Capped: true header when the balance was the limit; for them, 402 insufficient_credits only happens if a concurrent request spends the balance between the check and the charge. Pushing a database lead to an integration (POST /api/v1/integrations/:id/push-lead with a leadId, or the MCP tool push_lead_to_integration) is not capped; the same sticky rules apply. A lead you already received is free, even at a 0 balance. A new lead costs 1 credit, taken from your balance or, when the balance is 0 and your overage budget is enabled with room left, billed as overage; if neither can cover it, the push returns 402 insufficient_credits and nothing is pushed. A pushed lead counts as received (exportMethod crm_push in Lead Export History), so it is never charged again. On all plans, unused credits roll over each billing cycle and never expire while your subscription is active - but the remaining balance is forfeited when your subscription ends, and expires when a plan downgrade takes effect at renewal. Use these endpoints to purchase additional credits or manage your subscription.

Credit balance: Your current credit balance is always available via GET /api/v1/me - see the monthlyCredits, monthlyCreditsRemaining, permanentCredits, and totalCreditsRemaining fields.

Endpoints

POST /api/v1/purchase-credits
Purchase additional credits using your saved payment method (charged off-session via Stripe). Purchased credits roll over month to month while your subscription is active, but the remaining balance is forfeited when your subscription ends and expires when a plan downgrade takes effect at renewal. Minimum 100 credits. Pricing: Starter 10¢/credit, Growth 7.5¢/credit, Scale 5¢/credit, Platinum 3¢/credit, Enterprise 2¢/credit. Available on all paid plans.
GET /api/v1/overage-budget
Get your overage budget configuration and usage: an opt-in spending limit that lets lead usage continue past a zero credit balance. Extra credits accrue at your plan's overage rate and are charged to your card on file once per day. The budget applies per billing cycle and resets at renewal.
PATCH /api/v1/overage-budget
Enable, disable, or edit the overage budget. budgetType is 'dollars' (default) or 'credits'; budgetAmount is a whole number of dollars or credits per billing cycle (max 5x your plan's monthly credit allocation, or its dollar equivalent). Requires an active credit plan and a payment method on file. Disabling clears the config; accrued unbilled overage is still collected by the daily billing runs once it reaches Stripe's $0.50 minimum (smaller remaining balances are written off).
POST /api/v1/subscription/cancel
Cancel your subscription at the end of the current billing period. Your access continues until the period ends. Any remaining credit balance is forfeited when the subscription ends.

Purchase Credits Parameters

creditCount (integer, minimum 100) or dollarAmount (number, minimum 1) - send one, not both. idempotencyKey (optional, 8-64 letters, digits, _ or -; also accepted as the Idempotency-Key header) - reuse it when retrying the same purchase so the card is not charged twice.


MCP Server

SMB Sales Boost provides a remote Model Context Protocol (MCP) server that lets AI assistants and LLMs interact with your account directly. The REST API endpoints are available as MCP tools (pushing your own lead data to an integration is REST only), enabling AI-powered lead search, export, account management and integration workflows such as connecting a CRM (when available), creating webhooks and sending leads to them.

What is MCP?

The Model Context Protocol is an open standard that enables AI models to securely access tools and data sources. With our MCP server, you can connect Claude, ChatGPT, or any MCP-compatible AI assistant to search, filter, and export leads, and to send them to your CRM or webhooks, through natural language.

Connection Details

Endpoint
https://smbsalesboost.com/mcp
Transport
Streamable HTTP (stateless)
Method
POST
Auth
Bearer token (your API key) in Authorization header

Client Configuration

Add this to your MCP client configuration (e.g., Claude Desktop, Cursor, or any MCP-compatible tool):

{
  "mcpServers": {
    "smb-sales-boost": {
      "type": "streamable-http",
      "url": "https://smbsalesboost.com/mcp",
      "headers": {
        "Authorization": "Bearer smbk_your_api_key_here"
      }
    }
  }
}

Available Tools (71)

The AI assistant can call these tools on your behalf using natural language. Key tools include: get_profile, update_profile, search_leads, export_leads, list_filter_presets, create_filter_preset, list_keyword_lists, create_keyword_list, list_email_schedules, create_email_schedule, list_export_formats, create_export_format, list_export_history, ai_suggest_categories, ai_generate_keywords, list_export_blacklist, purchase_credits, get_auto_top_up, update_auto_top_up, get_overage_budget, update_overage_budget, and more.

Integration tools (17):

With the 17 integration tools (list_integration_providers through list_pipedrive_deal_fields), the assistant can send lead data (business contact details) to outside systems, so it should confirm the destination with you first. When an API key or MCP client creates a webhook, connects a new CRM account, changes where an integration sends data, adds or re-activates an event, or re-enables an integration (including a successful test or retry, or a reconnect, that turns a needs_attention integration back on), the account owner is emailed for each change. Each account gets up to 10 of these emails an hour for destination changes and 10 for other changes; past that, one alert is sent and further changes are combined into summary emails. Changes made through MCP are labeled with the channel and the API key used, for example "Created via MCP with API key smbk_AbCdEfG..." in the Integrations tab. See Integrations for the endpoints, events and error codes.

Requirements

MCP access uses the same API key authentication as the REST API. An active paid subscription (Starter, Growth, Scale, Platinum, or Enterprise) is required. The MCP endpoint accepts up to 60 requests per minute per client IP address, and each tool call also counts against the rate limits of the REST endpoints it calls. Usage tracking works the same as for the REST API.

Errors

Most tools report a failed call as the API's message followed by the HTTP status and error code, for example (HTTP 409, integration_exists) or (HTTP 402, insufficient_credits), plus up to 5 validation details, so the error codes under Error Handling and Integrations apply to MCP too. When the API sends no error code, only the status is shown (a rate-limited call ends in (HTTP 429)). The purchase, claim_key, start_enrichment, re_export_lead_export_history and refresh_and_export_lead_export_history tools return the API's message only.


Integrations

SMB Sales Boost can send lead data to your CRM, automation tools, and any HTTPS endpoint. Set up destinations from the Integrations tab in your dashboard, or with the endpoints below, which are also available as MCP tools (pushing your own lead data is REST only; see MCP Server). They live under /api/v1/integrations, use the same API key (Authorization: Bearer smbk_...), paid-plan requirement and rate limits as the rest of the API, and return the usual {"data": ...} and {"error", "message"} shapes. A 400 validation_error can also include a details array (see Error Handling).

Safeguards

An integration sends business contact details to an outside system automatically, on every subscribed event, until it is paused or deleted. So that nothing changes unnoticed:

Supported providers

HubSpot (hubspot)
Per-user OAuth. Creates or updates Contacts matched by email (a lead without an email creates no Contact) and Companies matched by website domain, or by exact company name when the lead has no website of its own (none, or a social or listing page such as Facebook or Yelp), and links each Contact to its Company (available when configured)
Salesforce (salesforce)
Per-user OAuth, Production or Sandbox. Creates or updates Leads matched by Email; a lead without an email creates a new Lead each time, and Company defaults to "Unknown" when the lead has no company name (available when configured)
Pipedrive (pipedrive)
Per-user OAuth; per-company API domain handled automatically. Matches Organizations by exact name and Persons by the SMB Sales Boost lead id, then by email; adds a note with the lead id on each delivery of a lead; optional Deals, see Rules worth knowing (available when configured)
Zapier (zapier)
Webhook trigger URL with HMAC signature (Live)
n8n (n8n)
Webhook node with HMAC signature (Live)
Make.com (make)
Custom webhook trigger with HMAC signature (Live)
Pipedream (pipedream)
HTTP source workflow with HMAC signature (Live)
Clay (clay)
Clay table "Import data" webhook with HMAC-signed JSON rows (Live)
Generic Webhook (generic_webhook)
Any HTTPS endpoint, signed JSON payload (Live)

HubSpot, Salesforce and Pipedrive are available when they are configured for this deployment; otherwise connecting them, or using an already connected one, returns 503 provider_unavailable. GET /api/v1/integrations/providers shows the current availability of every provider.

Events

Subscribe destinations to one or more of these events. A webhook receives each event as a signed JSON POST (see Delivery format below).

HubSpot, Salesforce and Pipedrive integrations take only lead.created and lead.updated; a subscription to any other event returns 400 validation_error.

Delivery format

Each webhook delivery is an HTTPS POST with these headers:

HeaderValue
Content-Typeapplication/json
User-AgentSMB-Sales-Boost-Webhooks/1.0
X-SMB-EventThe event type, for example lead.created
X-SMB-TimestampUnix seconds when this attempt was signed
X-SMB-Signaturet=<Unix seconds>,v1=<hex> (see Verifying signatures)
X-SMB-API-KeyOnly when the webhook has a header API key set for this destination URL (see Safeguards)

The body is an envelope; data depends on the event (shortened here):

{
  "id": "evt_1760000000000_k3j9qz",
  "type": "lead.created",
  "occurredAt": "2026-10-08T12:00:00.000Z",
  "userId": "your-user-id",
  "integrationId": 42,
  "provider": "zapier",
  "data": {
    "id": 123456,
    "databaseType": "other",
    "source": "api",
    "companyName": "Acme Plumbing",
    "website": "https://acmeplumbing.example",
    "email": "[email protected]",
    "phone": "(512) 555-0100",
    "city": "Austin",
    "state": "TX",
    "lastBuyingSignal": "2026-10-01T00:00:00.000Z",
    "buyingSignalType": ["Newly Added"]
  }
}
Deliverydata
Live lead.created / lead.updated, and a leadId push (lead.created)A flat lead summary: id, databaseType, source (dashboard, api, email_schedule or crm_push; a push has crm_push), companyName, website, email, emailSecondary, emailTier1 to emailTier6, phone, phoneSecondary, phoneTotal, address, city, state, zip, country, category, description, lastBuyingSignal and buyingSignalType (a list). lastUpdated and lastUpdatedReason carry the same two values under their older names.
Push of your own data (lead.created){"lead": {...}, "source": "manual_push"} with your own object as sent (see Push one lead)
Test (lead.created){"test": true, "message": "This is a test event from SMB Sales Boost integrations", "sample": {"id": 0, "companyName": "Acme Test Co", "website": "https://example.com"}}
export.completed, email_schedule.sent, credit.low_balanceThe fields listed under Events

A receiver that handles pushes and tests must read three lead.created shapes: the flat lead summary (live events and leadId pushes), your own data from a push, and the test event.

Verifying signatures

X-SMB-Signature is t=<Unix seconds>,v1=<hex>. t is the same value as X-SMB-Timestamp, and v1 is the hex HMAC-SHA256 of "<t>." + rawBody, keyed with your signing secret exactly as it was returned (use the string as is; do not hex-decode it). Check it against the raw request body before you parse the JSON, compare using a timing-safe comparison after checking that the lengths match, and reject a t older than 5 minutes (every attempt is signed with a fresh t).

// Node.js (Express): verify an SMB Sales Boost webhook before using it
const crypto = require("crypto");
const express = require("express");

const app = express();
const SIGNING_SECRET = process.env.SMB_SIGNING_SECRET; // exactly as returned on create
const MAX_AGE_SECONDS = 5 * 60;

// Mount this route before app.use(express.json()), which would consume the raw body.
app.post("/smb-webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!Buffer.isBuffer(req.body)) return res.status(400).send("Expected a JSON body");

  // X-SMB-Signature: t=<Unix seconds>,v1=<hex>
  const parts = {};
  for (const item of (req.get("X-SMB-Signature") || "").split(",")) {
    const i = item.indexOf("=");
    if (i > 0) parts[item.slice(0, i).trim()] = item.slice(i + 1).trim();
  }
  const t = Number(parts.t);
  if (!Number.isInteger(t) || !parts.v1) return res.status(400).send("Missing signature");
  if (Math.abs(Date.now() / 1000 - t) > MAX_AGE_SECONDS) return res.status(400).send("Stale timestamp");

  // HMAC-SHA256 of "<t>." followed by the raw body bytes, keyed with the secret string
  const expected = crypto
    .createHmac("sha256", SIGNING_SECRET)
    .update(parts.t + ".")
    .update(req.body)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send("Bad signature");
  }

  const event = JSON.parse(req.body.toString("utf8"));
  res.sendStatus(200); // answer within 8 seconds; do slow work afterwards
  console.log(event.type, event.id); // use event.id to skip duplicates
});

app.listen(3000);

Endpoints

MethodPathDescription
GET/api/v1/integrations/providersList providers and runtime availability
GET/api/v1/integrationsList your integrations with subscriptions and who created or last changed them
GET/api/v1/integrations/:idSingle integration with subscription detail
POST/api/v1/integrations/webhookCreate a webhook integration; returns the signing secret once
PATCH/api/v1/integrations/:idUpdate display name, status, subscriptions (the list you send replaces them all), destination URL, header API key or Pipedrive deal settings
DELETE/api/v1/integrations/:idDelete an integration permanently, with its subscriptions and delivery history
GET/api/v1/integrations/:id/deliveriesThe 50 newest deliveries, each with its attempt count, last status code, error and the first 1000 characters of your endpoint's response
POST/api/v1/integrations/:id/deliveries/:deliveryId/retrySend the stored data of a past delivery again as a new event, in one attempt
POST/api/v1/integrations/:id/testSend one test lead.created event to the real destination (on a CRM this writes a real record for the sample company Acme Test Co that is not removed)
POST/api/v1/integrations/:id/push-leadPush one lead now: leadId (1 credit if new to you) or your own lead data (free)
POST/api/v1/integrations/:provider/connectStart a HubSpot, Salesforce or Pipedrive connection, or reconnect one already connected; returns authorizationUrl
GET/api/v1/integrations/:id/field-mappingCRM field list and current mapping (?refresh=true reloads the CRM fields)
PATCH/api/v1/integrations/:id/field-mappingChange which CRM field receives each lead field (replaces the whole saved mapping)
DELETE/api/v1/integrations/:id/field-mappingReset the mapping to the defaults
POST/api/v1/integrations/:id/field-mapping/testDry-run a mapping (creates, then deletes, synthetic CRM records)
GET/api/v1/integrations/:id/pipedrive/pipelinesPipedrive pipelines and stages
GET/api/v1/integrations/:id/pipedrive/deal-fieldsPipedrive deal fields and the deal field map (?refresh=true reloads them)

Errors: 400 validation_error or bad_request, 402 insufficient_credits (push-lead), 404 not_found, 409 integration_exists (create, or a PATCH that re-points to a destination another integration of the same provider already uses) or integration_limit (at most 25 integrations per account; also on connect, see Connect a CRM), 409 conflict (PATCH only: when the integration changes while the update is being applied, for example the owner pauses it at the same moment, the update is applied again on top of that change; only when it changes again does the PATCH return 409, nothing changed, so read it again and retry. If the API key making the PATCH is revoked in that moment, it returns 401 unauthorized "This API key has been revoked" and nothing is applied), 429 (all integration endpoints share one budget per account of 60 requests per minute, counted together with the dashboard; connect is also limited to 30 per minute), 502 provider_error (the CRM's API failed) and 503 provider_unavailable. A 503 provider_unavailable is not temporary (no Retry-After; do not retry): it means the CRM is not configured for this deployment. It can come from the field-mapping, Pipedrive pipeline and deal-field endpoints, test, retry, push-lead and connect. An unknown provider in the connect path is a 404. A 500 internal_error on create, PATCH or push-lead may come after the change was saved (or a credit was taken), so check GET /api/v1/integrations and the deliveries before repeating it; a 503 service_unavailable only happens before anything is written.

Responses

Every success is wrapped in {"data": ...}. Test, retry and push answer HTTP 200 even when the destination fails; read data.status. When a webhook test or push gets a non-2xx answer, data.error holds the first 500 characters of your endpoint's response (HTTP <status> when it sent no body). Paths starting with .../ are under /api/v1/integrations/:id/.

Endpointdata
GET /api/v1/integrations/providersA list of {id, name, tagline, description, authMethod, ownerSetupRequired, defaultEvents, supportsPerLeadPush, helpText, available}, plus setupHelpUrl when set and reason when unavailable
GET /api/v1/integrations (a list), GET and PATCH /api/v1/integrations/:idAn integration: {id, provider, providerName, status, displayName, externalAccountId, authMethod, metadata, consecutiveFailures, lastFailureAt, lastSuccessAt, tokenExpiresAt, createdAt, updatedAt, createdVia, lastChangedVia, lastChangedAt, subscriptions: [{id, eventType, targetUrl, isActive, config}]}. status is connected, disabled or needs_attention. createdVia and lastChangedVia are null or, for example, {"channel": "api", "apiKey": {"id": 7, "keyPrefix": "smbk_AbCdEfG...", "name": "Zap agent", "revoked": false}}; channel is dashboard, api or mcp, and apiKey is null for a dashboard change.
POST /api/v1/integrations/webhook{"integration": {...}, "signingSecret": "..."} (see Create a webhook integration)
DELETE /api/v1/integrations/:id{"message": "Integration deleted"}
GET .../deliveriesNewest first, a list of {id, eventType, targetUrl, status, statusCode, attemptCount, latencyMs, errorMessage, responseBody, createdAt, completedAt}. CRM rows have a targetUrl such as hubspot://contacts.
POST .../test, POST .../deliveries/:deliveryId/retry{"status": "success" or "failed", "statusCode", "latencyMs", "error"}: statusCode and latencyMs come from webhooks, and error, when present, explains the failure
POST .../push-lead{"status", "tracked", "creditsUsed", "result", "error"}: result is {contactId, companyId} for HubSpot (each only when written), {leadId} for Salesforce and {statusCode, latencyMs} for a webhook (none for Pipedrive)
GET .../field-mappingHubSpot {smbFields, contactFields, companyFields, cachedAt, mapping}; Salesforce {smbFields, leadFields, contactFields, accountFields, cachedAt, mapping} (only the lead scope is mapped); Pipedrive {smbFields, personFields, organizationFields, cachedAt, mapping}. CRM field lists are cached for up to 10 minutes.
PATCH and DELETE .../field-mapping{"mapping": {...}}, the mapping now saved (after DELETE every scope is empty, which means the defaults)
POST .../field-mapping/test{ok, scopes: [{scope, label, ok, issues: [{smbFieldId, targetField, message, severity}]}]}, where severity is error or warning
GET .../pipedrive/pipelinesA list of {id, name, active, stages: [{id, name, orderNr}]}
GET .../pipedrive/deal-fields{smbFields, dealFields, cachedAt, fieldMap}

Rules worth knowing

Create a webhook integration

curl -X POST https://smbsalesboost.com/api/v1/integrations/webhook \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer smbk_your_api_key_here" \
  -d '{
    "provider": "zapier",
    "displayName": "My Zap",
    "targetUrl": "https://hooks.zapier.com/hooks/catch/XXX/YYY",
    "events": ["lead.created", "export.completed"]
  }'

Body fields:

The response is {"data": {"integration": {...}, "signingSecret": "..."}}. Store the signing secret now, because it is shown only once. If the request times out, the integration may have been created: check GET /api/v1/integrations first. Repeating the same provider and targetUrl returns 409 integration_exists; a different provider or a changed URL (even a trailing slash) creates a second integration with its own secret. If the integration exists but you did not get its signing secret, delete it (DELETE /api/v1/integrations/:id, which also deletes its delivery history) and create it again.

Connect a CRM

POST /api/v1/integrations/hubspot/connect (or salesforce, pipedrive) returns {"data": {"provider", "authorizationUrl", "expiresAt"}}. Salesforce also takes {"environment": "production"} (the default) or {"environment": "sandbox"}; sending environment for HubSpot or Pipedrive returns 400. Open authorizationUrl in a browser and approve access within 10 minutes. A new CRM integration then appears in GET /api/v1/integrations with its initial events: HubSpot lead.created on (lead.updated present but off; to turn it on, PATCH subscriptions with both events, see Rules worth knowing), Salesforce lead.created, Pipedrive lead.created and lead.updated. Only the account owner should approve it: whoever approves attaches their CRM to the account. When the account already has 25 integrations, connect returns 409 integration_limit unless one of them is an integration of that CRM that the sign-in could reconnect. The browser returns through /api/integrations/<provider>/callback, which is a redirect target, not an API endpoint.

Connecting a CRM account that is already connected re-authorizes that integration in place: it gets new access, a needs_attention integration becomes connected again, a paused one stays paused, and its events, field mapping, Pipedrive deal settings and delivery history are kept. The 25-integration limit applies only to new connections. So when a CRM integration stops working because its access was removed or expired in the CRM, reconnect it: click Reconnect on the integration in the dashboard's Integrations tab, or connect again through the API or MCP and sign in to the same CRM account (for a Salesforce Sandbox integration, send {"environment": "sandbox"}). PATCH {"status": "connected"} alone does not restore access. Signing in to a different account of the same CRM creates a new integration.

If approval does not complete, nothing is connected or changed and the dashboard URL shows <provider>_error=<reason>, for example key_revoked (the API key that created the link was revoked), integration_limit (the account has 25 integrations and the CRM account you approved is not already connected), invalid_state (the link expired or was already used; start again), already_connected (rare: two approvals of the same CRM account finished at the same moment) or the CRM's own error, such as access_denied. A HubSpot connect fails, creating nothing, when HubSpot does not confirm the account, with the message "We could not confirm which HubSpot account you signed in to, so nothing was connected or changed. Please try connecting again in a minute." Check GET /api/v1/integrations to confirm a connection.

Push one lead

POST /api/v1/integrations/:id/push-lead sends one lead to this integration now, in a single attempt with no automatic retries. Send {"leadId": 123} for a lead from the database (tracked like an export), or {"lead": {...}} for your own data (untracked and free; REST only, the MCP tool push_lead_to_integration takes leadId).


Ready to Find Your Next Customers? Get access to growing local business leads updated every 5 minutes. See plans starting at $49/mo →