Quickstart
Five minutes from nothing to your first identified company.
Signal tells you which companies and people visit your website. This page gets you a working integration; the guides go deeper.
1. Get a key
Sign in, go to Exports & API, name a key and choose what it reaches.
| Scope | Reaches |
|---|---|
read | the REST endpoints |
copilot | the natural-language endpoint |
The key is shown once. It is stored hashed — if you lose it, create another and revoke the old one. Scopes are fixed at creation: to change what a key reaches, make a new one. A credential whose powers can grow after it was reviewed is one nobody can reason about.
2. Your first call
curl "https://app.signal.geysera.com/signal-api/v1/accounts?page_size=5" \
-H "Authorization: Bearer sk_sig_…"
{ "accounts": [ … ], "total": 1927, "page": 1, "page_size": 5 }
That is the whole authentication story: one header.
3. Ask a question instead
With a copilot-scoped key:
curl https://app.signal.geysera.com/agent-api/signal/copilot/ask \
-H "Authorization: Bearer sk_sig_…" -H "Content-Type: application/json" \
-d '{"question": "which companies visited most last week?", "thread_id": null}'
The response carries the answer, the tools that ran, and the figures behind it. It is read-only whatever it is asked.
4. Stop polling
# Register an endpoint; we POST the moment someone is identified.
Webhooks are how most integrations should work. Polling asks us every few minutes whether anything happened; a webhook tells you when it does. → Receive identified visitors by webhook
Before you build: four things that will save you a day
1. total includes masked records. On the free plan, people identified
after the first 150 of their month come back with is_locked: true and their
contact fields null. Count is_locked: false rows when you mean "people we can
contact".
2. Unknown query parameters are rejected, not ignored. ?limit=10 returns
422 VALIDATION_ERROR; details.errors[].field names limit, and the
message lists the parameters the endpoint accepts. A typo'd filter fails loudly
instead of returning unfiltered data — so treat a 422 as a bug in your call,
not a transient error to retry.
3. 401 and 403 mean different things. 401 = the key is missing,
malformed, unknown or revoked. 403 = the key is fine and lacks the scope.
Re-issuing a key fixes the first and never the second.
4. Identification yield depends on your audience, not your setup. Two accounts with identical configuration matched ~50% and ~0% of visitors. That is a property of who visits you, not a misconfiguration. Read why attribution covers less than you think before you build a target around a rate.
Discover the surface instead of trusting this page
curl https://app.signal.geysera.com/agent-api/capabilities
Public, no key needed, and generated from the routes' own auth dependencies — so it cannot describe an endpoint that does not exist or omit one that does. It carries the endpoint list with the scope each needs, the pagination bounds, the rate limit and its scope, the error vocabulary, and the response headers you should act on.
Every number in this documentation is derived from that endpoint or asserted
against the code by a test. If this page and /capabilities ever disagree,
/capabilities is right — and that is a bug we want to hear about.
Where to go next
Guides — what you are trying to do
- Receive identified visitors by webhook — the integration most people should build first
- Export and keep in sync — paging, rate limits, incremental strategies
- Ask questions in natural language — the copilot API, and using it from another agent
Reference
- Full API guide — scopes, conventions, limits
GET /signal-api/v1/openapi.json— OpenAPI 3.1 for the REST endpoints
Case studies — what we learned running this
- Most of your visitors were not people
- The pipeline that reported success while doing nothing
- Why attribution covers less than you think
What is not available yet
Stated plainly so you do not design around something that does not exist:
- Writes on visitors, accounts, attribution and recommendations. Those are
read-only. A visitor's status or a recommendation changes through the copilot
or the dashboard. Audiences and copilot conversations do accept writes —
the guide's Audiences section lists them, and
POST /audiences/{key}/runsspends enrichment credits, so treat it like any other paid call. - An optimistic-locking contract on the writes that do exist. Audience
writes take an
Idempotency-Key, so retrying one is safe. There is noexpected_updated_atcheck, so two people editing the same audience overwrite each other rather than one getting a conflict. - Per-key narrowing below the workspace. Every key sees the whole workspace.
- More than one webhook event type. Today:
signal.visitor_identified.
Markdown source: /developers/quickstart.md