# 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

```bash
curl "https://app.signal.geysera.com/signal-api/v1/accounts?page_size=5" \
  -H "Authorization: Bearer sk_sig_…"
```

```json
{ "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:

```bash
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

```bash
# 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](./workflows/receive-identified-visitors-by-webhook.md)

---

## Before you build: four things that will save you a day

**1. `total` is not a row count.** It is what your plan permits you to resolve.
Rates computed against it change when your billing changes. Use it to page, not
to divide.

**2. Unknown query parameters are ignored, not rejected.** `?limit=10` returns
200 and the default page size. Assert that the response's `page_size` is what
you asked for — one line, catches every parameter typo you will ever write.

**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](./case-studies/why-attribution-covers-less-than-you-think.md)
before you build a target around a rate.

---

## Discover the surface instead of trusting this page

```bash
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](./workflows/receive-identified-visitors-by-webhook.md) — the integration most people should build first
- [Export and keep in sync](./workflows/export-and-keep-in-sync.md) — paging, rate limits, incremental strategies
- [Ask questions in natural language](./workflows/ask-questions-in-natural-language.md) — the copilot API, and using it from another agent

**Reference**

- [Full API guide](./guide.md) — 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](./case-studies/most-of-your-visitors-were-not-people.md)
- [The pipeline that reported success while doing nothing](./case-studies/the-pipeline-that-reported-success-while-doing-nothing.md)
- [Why attribution covers less than you think](./case-studies/why-attribution-covers-less-than-you-think.md)

---

## What is not available yet

Stated plainly so you do not design around something that does not exist:

- **Writes.** No endpoint creates or changes anything. The idempotency and
  concurrency contracts that would need are not finished, and shipping writes
  without them would be a promise we could not keep.
- **Per-key narrowing below the workspace.** Every key sees the whole workspace.
- **More than one webhook event type.** Today: `signal.visitor_identified`.
