Skip to main content
A signal watches one thing happening in the market — a company opening roles you care about, a funding round, a technology showing up in a stack, a person engaging with your LinkedIn page — and turns it into leads. Topo checks the signal’s provider every day, qualifies what it finds against your import filters, and adds the matching people or companies to a destination list it creates for the signal and keeps under the same name. The Signals API covers the full lifecycle of a signal: browse the catalog, preview what a preset would detect, create a signal, edit what it watches, duplicate it, hold it, retire it, restore it, and read back what it imported. Everything the dashboard’s Signals section does with a signal is available here. Definitions for your own first-party events live in Settings → Signals.
Reading the catalog, previewing a preset, and reading your signals and the imports feed requires the signals:read scope. Creating, editing, duplicating, pausing, resuming, archiving, and unarchiving requires signals:write.

Before you start

1

Create an API key

In the Topo dashboard, go to Settings → Developers → API keys, click Create Key, and grant signals:read, signals:write, or both. The raw key is shown once — see Authentication.
2

Pick a preset

A signal is always built from a catalog preset, which decides what gets detected and which detection filters you may send. See Browse the catalog.
3

Preview, then create

When the preset’s preview_supported is true, send your filters to POST /v1/signals/preview and check the sample before you create the signal. See Preview a signal.

Browse the catalog

GET /v1/signals/presets returns every preset you can create a signal from. Presets that are announced but not open for creation yet are omitted, so anything in this response is safe to pass as signal_type.
The catalog covers LinkedIn engagement and keyword mentions, hiring, job-description keywords, funding, technology adoption, news, and your own first-party events (CUSTOM_EVENT, fed by the Events API).

Preview a signal before creating it

POST /v1/signals/preview samples what a preset would detect with a given set of detection filters, without creating anything. Use it to check that your search_filters catch the right companies or people before you commit to a signal. It takes the same signal_type and search_filters as POST /v1/signals, and only needs signals:read.
The sample holds up to 10 recent matches over a 30-day window, newest first. total_count is how many facts the provider reports over that window, or null when the provider does not count. Company-scope presets fill the company fields; people-scope presets fill the person fields.
A preview is wider than a live run: no import filters, no dedup against your lists, and no daily cap are applied. Use it to validate the detection filters, not to predict how many entries the signal will import per day.
A preset whose preview_supported is false returns 422.

Create a signal

POST /v1/signals provisions the whole thing in one call: the detection search, the destination list, and the import rules. The signal starts ACTIVE and runs a first check immediately instead of waiting for its daily slot.
A successful call returns 201 Created with the signal:
url opens the signal in the Topo dashboard and list_url opens its destination list, so you can link from your own tools straight to what a signal collected.

Request fields

string
required
The catalog preset to build. Must be one of the signal_type values from GET /v1/signals/presets; a preset that is not open for creation returns 404.
string
required
Name of the signal and of the destination list Topo creates for it. Leading and trailing whitespace is trimmed, and a name that is empty after trimming is rejected.
object
Detection filters, as an object of filter key to a list of plain strings — {"job_title_or": ["VP Sales"]}. Only keys in the preset’s filter_keys are accepted, and every key in required_filter_keys must be present with a non-empty value.
string
PEOPLE or COMPANIES. Leave it out to follow the preset’s own scope. A COMPANY-scope preset may set PEOPLE to import the people behind the companies it detects; a PEOPLE-scope preset cannot import companies unless its scope_selectable_at_creation is true.
string
What your own events identify — PEOPLE for email or LinkedIn subjects, COMPANIES for domains. Required for the custom-event preset and rejected for every other preset.
object
Filters narrowing who or what is imported once a fact is detected — personas for a PEOPLE target, firmographics for a COMPANIES target. Same shape as search_filters.
string (uuid)
Sequence template that imported leads are enrolled into. People destinations only. A template from another workspace returns 404.
boolean
default:"true"
Whether imported leads get their email enriched.
boolean
default:"true"
Whether imported leads get their phone enriched.
integer
Daily ceiling on imported entries. Must be at least 1 and within your organization’s lead limit. Cannot be combined with max_companies_per_run.
integer
Daily ceiling on detected companies consumed. Only valid on a company signal importing people.
integer
How many people to import per detected company. Requires max_companies_per_run.

How the daily ceiling is resolved

max_per_day on the response is the number Topo actually enforces:
Setting both max_per_run and max_companies_per_run is rejected — the daily contact ceiling is derived from the company pair, so the two would contradict each other.

Signal status

Each of POST /v1/signals/{signal_id}/pause, /resume, /archive, and /unarchive returns the updated signal with its new status.
  • Resume makes the signal due right away, so the next check runs without waiting for the daily slot.
  • Archive retires the signal. Resuming or re-archiving an archived signal returns 409.
  • Unarchive restores an archived signal as PAUSED, with its filters, destination list, and imported entries intact. Resume it when you want it checking again. Unarchiving a signal that is not archived returns 409.
Prefer pausing when a signal should only stop for now. There is no delete in /v1 — archiving preserves the provenance linking every imported entry to the event that produced it, and unarchiving brings the signal back whenever you need it.

Edit a signal

PATCH /v1/signals/{signal_id} is a partial update: fields you leave out keep their current value, and the response is the full signal detail after the change. Renaming also renames the destination list.
You can change name, search_filters, import_search_filters, the caps (max_per_run, max_companies_per_run, max_contacts_per_company), enrich_email, enrich_phones, and sequence_template_id. Sending a filters object replaces the current filters for that key set, so send the complete set you want, not just the changed keys. Send null for a cap or sequence_template_id to clear it. The signal_type and import_target are fixed once the signal exists — to change what kind of thing a signal watches, or whether it imports people or companies, duplicate it with the new configuration and archive the original. An archived signal can only be renamed; any other edit returns 422 until you unarchive it. The same 422 rules as creation apply: unknown filter keys, missing required filters, or contradictory caps are rejected and the signal is left unchanged.

Duplicate a signal

POST /v1/signals/{signal_id}/duplicate copies a signal — same preset, detection filters, import filters, caps, enrichment, and sequence template — onto a new destination list. The copy starts ACTIVE and runs a first check immediately; the original is left as it was. Use it to fan a working configuration out across regions or personas, or as the first step of changing something PATCH cannot.
string
Name of the copy and of the destination list created for it. Leave it out to get the source name followed by (Copy).
A successful call returns 201 Created with the new signal, in the same shape as POST /v1/signals. Duplicating an archived signal works: the copy is active even though the source is not.

List and read signals

GET /v1/signals returns the workspace’s signals, most recently created first, in the standard pagination envelope. Archived signals are hidden unless you ask for them with include_archived=true.
GET /v1/signals/{signal_id} returns the same fields plus the signal’s full configuration: search_filters, import_search_filters, sequence_template_id, import_target, and event_subject. The filters come back in exactly the shape POST /v1/signals and PATCH /v1/signals/{signal_id} accept, so you can read one signal, tweak it, and send it back without translation.
A signal that belongs to another workspace returns 404, never 403.

Read the imports feed

GET /v1/signals/imports is the audit trail of every signal in the workspace: which signal fired, who or which company it was about, and what Topo did with it. Entries are ordered by when the underlying fact occurred, most recent first.

Import status

Filters

kind tells you which half of the entry is filled in: a CONTACT entry carries the person fields (first_name, email, company_domain), an ACCOUNT entry carries account_name and account_domain. contact_id and account_id are populated once the record exists in your workspace.

Errors

See Errors & Limits for the full error envelope and the X-RateLimit-* headers. Example configuration error (422)

Troubleshooting