API Reference
The Signals API lets you programmatically access your businesses, leads, signals, and subscriptions. Use it to build custom integrations, sync leads to your CRM, or automate your sales workflow.
Base URL:
https://api.meetsignals.ai/api/v1
CLI & MCP Server
Don't want to call the API directly? We offer two ready-made tools that wrap every endpoint documented below.
Signals CLI
signals-sortlist-cli
Command-line interface for developers and AI agents. Manage businesses, subscriptions, leads, and webhooks from the terminal.
npm install -g signals-sortlist-cli
Signals MCP Server
signals-sortlist-mcp
Model Context Protocol server for AI agents. Plug Signals into Claude Desktop, Cursor, Claude Code, and any MCP-compatible client.
npx signals-sortlist-mcp
For AI Agents
Install the Signals skill for your AI agent (Cursor, Claude Code, OpenClaw, etc.):
npx skills add sortlist/signals-cli
This installs a SKILL.md that gives your agent full knowledge of the CLI commands, patterns, and workflows.
Both packages are open source: CLI source · MCP source
Authentication
All API requests (except the signal catalog) require an API key sent as a Bearer token in the
Authorization header.
API keys are scoped to your team — use the businesses endpoints to access each business's data.
Create API keys from Settings → API Keys in your dashboard.
Authorization: Bearer YOUR_API_KEY
Errors
The API returns standard HTTP status codes and a JSON error body.
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 404 | Resource not found |
| 422 | Validation error |
| 429 | Rate limit exceeded |
{
"error": "Unauthorized. Please provide a valid API key or OAuth token."
}
Pagination
List endpoints return paginated results. Use page
and per_page query parameters.
{
"meta": {
"current_page": 1,
"total_pages": 12,
"total_count": 287,
"per_page": 25
}
}
Rate limits
The API is limited to 60 requests per minute per API key.
Exceeding the limit returns a 429 response.
Signals
The signal catalog is public — no authentication required. Signals are the types of buying signals Signals can detect (e.g. LinkedIn engagers, job changers, funding rounds).
/api/v1/signals
Returns all active signal types.
{
"signals": [
{
"id": 1,
"name": "LinkedIn Company Engagers",
"slug": "linkedin-company-engagers",
"description": "Track people who engage with a company's LinkedIn posts.",
"active": true,
"default_config": { "engagement_types": ["like", "comment"] },
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
}
]
}
/api/v1/signals/:slug
Returns a single signal by its slug.
Businesses
Each team can have multiple businesses. All leads, subscriptions, and webhooks are scoped to a business. Use the business ID in the URL path when accessing those resources.
/api/v1/businesses
Returns all businesses in your team.
{
"businesses": [
{
"id": 1,
"name": "Acme Corp",
"slug": "acme-corp",
"website": "https://acme.com",
"description": "B2B SaaS for project management",
"icon": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
}
]
}
/api/v1/businesses/:id
Returns a single business with its Ideal Customer Profile.
{
"business": {
"id": 1,
"name": "Acme Corp",
"slug": "acme-corp",
"website": "https://acme.com",
"description": "B2B SaaS for project management",
"icon": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z",
"ideal_customer_profile": {
"id": 1,
"target_job_titles": ["CTO", "VP of Engineering"],
"target_locations": ["North America", "Europe"],
"target_industries": ["Software", "Information Technology"],
"company_types": ["Public Company", "Privately Held"],
"company_sizes": ["51-200", "201-500"],
"mandatory_keywords": ["SaaS", "engineering"],
"excluded_companies": [],
"lead_matching_mode": 50
}
}
}
/api/v1/businesses
Create a new business. Supports two modes:
Website mode: Pass only a website URL.
The API will automatically analyze the site and generate the business name, description, and Ideal Customer Profile.
This may take a few seconds.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | conditional | Business name (required in manual mode, auto-generated in website mode) |
| website | string | no | Website URL. If passed alone, triggers auto-analysis |
| description | string | no | Short description of the business |
| ideal_customer_profile_attributes | object | no | ICP config — see ICP attributes reference below |
curl -X POST https://api.meetsignals.ai/api/v1/businesses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "website": "https://acme.com" }'
curl -X POST https://api.meetsignals.ai/api/v1/businesses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"website": "https://acme.com",
"description": "B2B SaaS for project management",
"ideal_customer_profile_attributes": {
"target_job_titles": ["CTO", "VP of Engineering"],
"target_locations": ["North America"],
"lead_matching_mode": 50
}
}'
ICP attributes reference
The ideal_customer_profile_attributes object accepts the following fields.
Fields marked with constrained values only accept the listed options — any other value will return a 422 error.
| Field | Type | Description |
|---|---|---|
| id | integer | Required when updating an existing ICP (returned in GET response) |
| lead_matching_mode | integer | 0–100. Discovery (0–33), Balanced (34–66), High Precision (67–100) |
| target_job_titles | string[] | Free text — any job titles (e.g. "CTO", "VP of Sales") |
| mandatory_keywords | string[] | Free text — keywords to require in lead profiles |
| excluded_companies | string[] | Free text — company names to exclude from results |
| target_locations | string[] | Constrained — see allowed values below |
| target_industries | string[] | Constrained — see allowed values below |
| company_types | string[] | Constrained — see allowed values below |
| company_sizes | string[] | Constrained — see allowed values below |
Allowed values for target_locations
Allowed values for target_industries
Allowed values for company_types
Allowed values for company_sizes
/api/v1/businesses/:id
Update a business and/or its Ideal Customer Profile.
| Parameter | Type | Description |
|---|---|---|
| name | string | Business name |
| website | string | Website URL |
| description | string | Short description |
| ideal_customer_profile_attributes | object | ICP fields to update — see ICP attributes reference above |
Note: When updating the ICP, include the ICP id in ideal_customer_profile_attributes
to update the existing profile. You can get the ICP id from the GET /api/v1/businesses/:id response.
Array fields are replaced entirely — pass the full array, not just additions.
curl -X PATCH https://api.meetsignals.ai/api/v1/businesses/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ideal_customer_profile_attributes": {
"id": 1,
"target_job_titles": ["CTO", "VP of Engineering", "Head of Product"],
"target_locations": ["North America", "Europe"],
"lead_matching_mode": 70
}
}'
Integrations
Integrations are third-party connections configured on a business (e.g. Overloop, Slack, Webhooks). Use these endpoints to list integrations and fetch Overloop campaigns for use with subscriptions and lead enrollment.
/api/v1/businesses/:business_id/integrations
Returns all integrations for the business. Credentials are never exposed.
{
"integrations": [
{
"id": 5,
"type": "overloop_ai",
"display_name": "Overloop",
"created_at": "2026-03-01T10:00:00Z"
},
{
"id": 6,
"type": "webhook",
"display_name": "Webhook",
"created_at": "2026-03-05T14:30:00Z"
}
]
}
/api/v1/businesses/:business_id/integrations/:id/campaigns
Returns campaigns for an Overloop integration. Results are cached for 5 minutes.
Returns 422 if the integration is not an Overloop type.
{
"campaigns": [
{ "id": "123", "name": "Outbound Q1 2026", "status": "on" },
{ "id": "456", "name": "Event Follow-up", "status": "paused" }
]
}
curl https://api.meetsignals.ai/api/v1/businesses/1/integrations/5/campaigns \
-H "Authorization: Bearer YOUR_API_KEY"
Subscriptions
A subscription is a signal you've configured and activated — for example, "Track engagers on Apple's LinkedIn page." In the dashboard, these appear as your active signals. All subscription endpoints are scoped to a business.
/api/v1/businesses/:business_id/subscriptions
Returns all subscriptions for the business.
{
"subscriptions": [
{
"id": 42,
"name": "Apple Engagers",
"signal": {
"name": "LinkedIn Company Engagers",
"slug": "linkedin-company-engagers"
},
"config": { "linkedin_url": "https://www.linkedin.com/company/apple/" },
"active": true,
"daily_lead_limit": 100,
"integrations": [
{
"integration_id": 5,
"type": "overloop_ai",
"display_name": "Overloop",
"auto_deliver": true,
"overloop_campaign_id": "123",
"overloop_campaign_name": "Outbound Q1 2026"
}
],
"last_checked_at": "2026-03-16T09:30:00Z",
"created_at": "2026-02-01T12:00:00Z",
"updated_at": "2026-03-16T09:30:00Z"
}
]
}
/api/v1/businesses/:business_id/subscriptions/:id
Returns a subscription with stats.
{
"subscription": {
"id": 42,
"name": "Apple Engagers",
"signal": { "name": "LinkedIn Company Engagers", "slug": "linkedin-company-engagers" },
"config": { "linkedin_url": "https://www.linkedin.com/company/apple/" },
"active": true,
"daily_lead_limit": 100,
"integrations": [
{
"integration_id": 5,
"type": "overloop_ai",
"display_name": "Overloop",
"auto_deliver": true,
"overloop_campaign_id": "123",
"overloop_campaign_name": "Outbound Q1 2026"
}
],
"last_checked_at": "2026-03-16T09:30:00Z",
"created_at": "2026-02-01T12:00:00Z",
"updated_at": "2026-03-16T09:30:00Z",
"stats": {
"total_leads": 847,
"leads_today": 12
}
}
}
/api/v1/businesses/:business_id/subscriptions
Create a new subscription.
| Parameter | Type | Required | Description |
|---|---|---|---|
| signal_slug | string | yes | Slug from the signal catalog |
| name | string | yes | A name for this subscription |
| config | object | no | Signal-specific configuration |
| daily_lead_limit | integer | no | Maximum leads per day for this subscription (default: 100) |
| integrations | array | no | Integrations to link — see integration linking below |
curl -X POST https://api.meetsignals.ai/api/v1/businesses/1/subscriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"signal_slug": "linkedin-company-engagers",
"name": "Apple Engagers",
"config": {
"linkedin_url": "https://www.linkedin.com/company/apple/"
},
"integrations": [
{
"integration_id": 5,
"auto_deliver": true,
"overloop_campaign_id": "123",
"overloop_campaign_name": "Outbound Q1 2026"
}
]
}'
Linking integrations to a subscription
Pass an integrations array when creating or updating a subscription.
Each entry links an integration and configures auto-delivery. For Overloop integrations, include the campaign ID and name.
| Field | Type | Required | Description |
|---|---|---|---|
| integration_id | integer | yes | ID from the integrations list |
| auto_deliver | boolean | no | Automatically send new leads to this integration (default: false) |
| overloop_campaign_id | string | conditional | Required for Overloop when auto_deliver is true. Get IDs from the campaigns endpoint |
| overloop_campaign_name | string | no | Campaign name for display purposes |
Note: The integrations array replaces all linked integrations.
Omit the field entirely to leave integrations unchanged. Pass an empty array to remove all links.
/api/v1/businesses/:business_id/subscriptions/:id
Update a subscription's name, config, active state, or linked integrations.
curl -X PATCH https://api.meetsignals.ai/api/v1/businesses/1/subscriptions/42 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"integrations": [
{
"integration_id": 5,
"auto_deliver": true,
"overloop_campaign_id": "123",
"overloop_campaign_name": "Outbound Q1 2026"
}
]
}'
/api/v1/businesses/:business_id/subscriptions/:id/pause
Pause a subscription. The signal will stop scanning for new leads until resumed. Returns the updated subscription object.
curl -X POST https://api.meetsignals.ai/api/v1/businesses/1/subscriptions/42/pause \
-H "Authorization: Bearer YOUR_API_KEY"
# Returns the subscription with "active": false
/api/v1/businesses/:business_id/subscriptions/:id/resume
Resume a paused subscription. The signal will start scanning for new leads again. Returns the updated subscription object.
curl -X POST https://api.meetsignals.ai/api/v1/businesses/1/subscriptions/42/resume \
-H "Authorization: Bearer YOUR_API_KEY"
# Returns the subscription with "active": true
/api/v1/businesses/:business_id/subscriptions/:id
Delete a subscription. Returns 204 No Content.
Leads
Leads are the enriched profiles discovered by your active signals. Each lead includes name, email, job title, company, LinkedIn URL, and more. All lead endpoints are scoped to a business.
/api/v1/businesses/:business_id/leads
Returns leads for the business, newest first. Paginated.
| Parameter | Default | Description |
|---|---|---|
| page | 1 | Page number |
| per_page | 25 | Results per page (max 100) |
{
"leads": [
{
"id": 1234,
"external_id": "li_abc123",
"name": "John Doe",
"headline": "Senior Sales Manager at Example Corp",
"job_title": "Senior Sales Manager",
"email": "john.doe@example.com",
"phone": "+1234567890",
"linkedin_url": "https://www.linkedin.com/in/johndoe",
"profile_picture": "https://media.licdn.com/...",
"location": "San Francisco, CA",
"company": "Example Corp",
"company_logo": "https://media.licdn.com/...",
"company_industry": "Software",
"company_size": "51-200",
"company_website": "https://example.com",
"company_linkedin": "https://www.linkedin.com/company/example-corp/",
"company_founded": "2015",
"connections": 500,
"followers": 1200,
"icp_score": 85,
"engagement_type": "like",
"post_url": "https://www.linkedin.com/feed/update/urn:li:activity:123",
"signals": [
{ "name": "LinkedIn Company Engagers", "slug": "linkedin-company-engagers" }
],
"subscription_ids": [42],
"payload": { "..." : "..." },
"triggered_at": "2026-03-16T09:15:00Z",
"created_at": "2026-03-16T09:15:00Z"
}
],
"meta": {
"current_page": 1,
"total_pages": 12,
"total_count": 287,
"per_page": 25
}
}
/api/v1/businesses/:business_id/leads/:id
Returns a single lead with delivery history.
{
"lead": {
"id": 1234,
"external_id": "li_abc123",
"name": "John Doe",
"headline": "Senior Sales Manager at Example Corp",
"job_title": "Senior Sales Manager",
"email": "john.doe@example.com",
"phone": "+1234567890",
"linkedin_url": "https://www.linkedin.com/in/johndoe",
"profile_picture": "https://media.licdn.com/...",
"location": "San Francisco, CA",
"company": "Example Corp",
"company_logo": "https://media.licdn.com/...",
"company_industry": "Software",
"company_size": "51-200",
"company_website": "https://example.com",
"company_linkedin": "https://www.linkedin.com/company/example-corp/",
"company_founded": "2015",
"connections": 500,
"followers": 1200,
"icp_score": 85,
"engagement_type": "like",
"post_url": "https://www.linkedin.com/feed/update/urn:li:activity:123",
"signals": [...],
"subscription_ids": [42],
"payload": { "..." : "..." },
"triggered_at": "2026-03-16T09:15:00Z",
"created_at": "2026-03-16T09:15:00Z",
"deliveries": [
{
"id": 567,
"integration_type": "Webhook",
"status": "delivered",
"delivered_at": "2026-03-16T09:15:02Z"
}
]
}
}
/api/v1/businesses/:business_id/leads/enroll
Enroll one or more leads into an Overloop campaign. Leads without an email or LinkedIn URL are skipped. Enrollment is asynchronous — leads are queued for delivery.
| Parameter | Type | Required | Description |
|---|---|---|---|
| integration_id | integer | yes | Overloop integration ID from the integrations list |
| campaign_id | string | yes | Overloop campaign ID from the campaigns endpoint |
| lead_ids | integer[] | yes | Array of lead IDs to enroll |
curl -X POST https://api.meetsignals.ai/api/v1/businesses/1/leads/enroll \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"integration_id": 5,
"campaign_id": "123",
"lead_ids": [10, 11, 12]
}'
{
"enqueued": 2,
"skipped": 1
}
/api/v1/businesses/:business_id/leads/:id
Soft-deletes a lead. The lead will no longer appear in list results but can be recovered by the Signals team if needed.
Returns 204 No Content on success.
curl -X DELETE https://api.meetsignals.ai/api/v1/businesses/1/leads/1234 \
-H "Authorization: Bearer YOUR_API_KEY"
# 204 No Content
Webhooks
Register a URL to receive an HTTP POST in real-time whenever a new lead is discovered. Webhooks are scoped to a business.
/api/v1/businesses/:business_id/webhooks
List all registered webhooks for the business.
/api/v1/businesses/:business_id/webhooks
Register a new webhook.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | yes | The URL to receive POST requests |
| secret | string | no | Secret to sign payloads (recommended) |
curl -X POST https://api.meetsignals.ai/api/v1/businesses/1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/signals",
"secret": "whsec_your_secret_here"
}'
/api/v1/businesses/:business_id/webhooks/:id
Delete a webhook. Returns 204 No Content.
Webhook payload
When a new lead is discovered, we send a POST to your URL with this payload:
{
"event": "lead.discovered",
"lead": {
"id": 1234,
"external_id": "li_abc123",
"name": "John Doe",
"headline": "Senior Sales Manager at Example Corp",
"linkedin_url": "https://www.linkedin.com/in/johndoe",
"company": "Example Corp",
"discovered_at": "2026-03-16T09:15:00Z",
"payload": { "email": "john@example.com", "..." : "..." }
},
"monitor": {
"id": 42,
"name": "Apple Engagers",
"signal": "LinkedIn Company Engagers"
},
"team": {
"id": 1,
"name": "My Team"
}
}
Verifying signatures
If you provided a secret,
each request includes an X-Signals-Signature header.
Compute HMAC-SHA256 of the raw body with your secret and compare:
import hmac, hashlib
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
assert hmac.compare_digest(
expected, request.headers["X-Signals-Signature"]
)
const crypto = require("crypto");
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
if (expected !== req.headers["x-signals-signature"]) {
throw new Error("Invalid signature");
}
Zapier
Signals integrates with Zapier to connect your leads to 5,000+ apps. The integration uses OAuth 2.0 — connect with one click from the Zapier dashboard.
Signals CLI
Use the Signals CLI to manage signals, subscriptions, leads, and webhooks directly from the terminal.
The CLI outputs JSON, making it easy to pipe into jq or use with AI agents like Claude Code and OpenClaw.
npm install -g signals-sortlist-cli
export SIGNALS_API_KEY=your_api_key
signals businesses:list
signals leads:list --business 1
signals subscriptions:pause 42 --business 1
signals webhooks:create --url https://example.com/webhook --business 1