Signals / API Docs

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.

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.

Request header
Authorization: Bearer YOUR_API_KEY

Errors

The API returns standard HTTP status codes and a JSON error body.

Status Meaning
401Missing or invalid API key
404Resource not found
422Validation error
429Rate limit exceeded
Error response
{
  "error": "Unauthorized. Please provide a valid API key or OAuth token."
}

Pagination

List endpoints return paginated results. Use page and per_page query parameters.

Pagination metadata
{
  "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).

GET /api/v1/signals

Returns all active signal types.

Response
{
  "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"
    }
  ]
}
GET /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.

GET /api/v1/businesses

Returns all businesses in your team.

Response
{
  "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"
    }
  ]
}
GET /api/v1/businesses/:id

Returns a single business with its Ideal Customer Profile.

Response
{
  "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
    }
  }
}
POST /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
Website mode (auto-analysis)
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" }'
Manual mode
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

North America South America Europe Asia Africa Oceania Middle East United States Canada United Kingdom Germany France Netherlands Australia India Brazil Singapore Japan Israel United Arab Emirates

Allowed values for target_industries

Accounting Advertising Aerospace & Defense Agriculture Automotive Banking & Financial Services Biotechnology Construction Consulting Consumer Goods E-Commerce & Retail Education Energy & Utilities Entertainment & Media Environmental Services Food & Beverage Government & Public Sector Healthcare & Pharmaceuticals Hospitality & Tourism Human Resources & Staffing Insurance Legal Services Logistics & Supply Chain Manufacturing Marketing Mining & Metals Non-Profit & NGO Real Estate Software Development & SaaS Telecommunications Transportation

Allowed values for company_types

Public Company Private Company Startup Non-Profit Educational Government Self-Employed Partnership Sole Proprietorship

Allowed values for company_sizes

1-10 11-50 51-200 201-500 501-1000 1001-5000 5001-10000 10000+
PATCH /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.

Update ICP
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.

GET /api/v1/businesses/:business_id/integrations

Returns all integrations for the business. Credentials are never exposed.

Response
{
  "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"
    }
  ]
}
GET /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.

Response
{
  "campaigns": [
    { "id": "123", "name": "Outbound Q1 2026", "status": "on" },
    { "id": "456", "name": "Event Follow-up", "status": "paused" }
  ]
}
Example request
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.

GET /api/v1/businesses/:business_id/subscriptions

Returns all subscriptions for the business.

Response
{
  "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"
    }
  ]
}
GET /api/v1/businesses/:business_id/subscriptions/:id

Returns a subscription with stats.

Response
{
  "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
    }
  }
}
POST /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
Example request bash
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.

PATCH /api/v1/businesses/:business_id/subscriptions/:id

Update a subscription's name, config, active state, or linked integrations.

Link an Overloop integration
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"
      }
    ]
  }'
POST /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.

Example
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
POST /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.

Example
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
DELETE /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.

GET /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)
Response
{
  "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
  }
}
GET /api/v1/businesses/:business_id/leads/:id

Returns a single lead with delivery history.

Response (partial)
{
  "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"
      }
    ]
  }
}
POST /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
Example request
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]
  }'
Response
{
  "enqueued": 2,
  "skipped": 1
}
DELETE /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.

Example
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.

GET /api/v1/businesses/:business_id/webhooks

List all registered webhooks for the business.

POST /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)
Example request
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"
  }'
DELETE /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:

POST to your webhook URL
{
  "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:

Python
import hmac, hashlib

expected = "sha256=" + hmac.new(
    secret.encode(), raw_body, hashlib.sha256
).hexdigest()

assert hmac.compare_digest(
    expected, request.headers["X-Signals-Signature"]
)
JavaScript / Node.js
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.

Open Signals on Zapier

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.

Install & setup
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

View on GitHub