# Quickstart

Issue a canary, place it, and see the first alert, in about five minutes.

You will create an API key, issue one canary, put its secret somewhere an intruder would look, and watch the alert arrive. Every command is copy-paste; only the placeholders change.

## 1. Create an API key

In the console, open **API keys** and create a key with all three scopes: `tokens:write` to issue the canary, `tokens:read` to look it up, `alerts:read` to see the alert. The secret starts with `ak_` and is shown once. Keep it in the environment rather than in a file:

```bash
export FEINT_API_KEY=ak_…
```

Admins and members of the organization can create keys; a read-only seat cannot. [Authentication](https://feintsecurity.com/docs/authentication) has the details.

## 2. Issue a canary

`placement` is the one field you must supply: where the secret is about to live, precisely enough that the alert tells you where to look.

```bash title="curl"
curl -s -X POST https://api.feintsecurity.com/v1/tokens \
  -H "Authorization: Bearer $FEINT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"placement": "prod .env on web-01", "metadata": {"owner": "platform"}}'
```

```python title="Python"
import os
import requests

API = "https://api.feintsecurity.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['FEINT_API_KEY']}"}

r = requests.post(
    f"{API}/tokens",
    headers=HEADERS,
    json={"placement": "prod .env on web-01", "metadata": {"owner": "platform"}},
)
r.raise_for_status()
canary = r.json()
print(canary["id"])
print(canary["secret"])
```

The response is `201 Created`, and it is the only time you will see `secret`. Abridged:

```json
{
  "id": "8d3e6f1a-2b4c-4d5e-9f01-23456789abcd",
  "provider": "gitlab",
  "name": "ci-3f9a1b2c4d",
  "placement": "prod .env on web-01",
  "metadata": {"owner": "platform"},
  "status": "active",
  "secret_prefix": "glpat-Xy7Q2k",
  "expires_at": "2027-09-26T00:00:00Z",
  "created_at": "2026-09-26T09:14:07.412903Z",
  "last_used_at": null,
  "needs_attention": false,
  "secret": "glpat-Xy7Q2k…",
  "secret_notice": "This is the only time the secret is returned. Feint does not store it."
}
```

[Canary tokens](https://feintsecurity.com/docs/canary-tokens) lists every field.

> [!NOTE]
> Feint keeps only the first 12 characters of the secret and a SHA-256 fingerprint of the whole. If you lose it, rotate the canary and place the new one.

## 3. Place it

Put the secret where it looks like it belongs and where nothing legitimate reads it. An entry in a `.env` file that is never loaded, a CI variable named like a deploy token, a line in an internal runbook:

```text
GITLAB_TOKEN=glpat-Xy7Q2k…
# rotated 2026-09-26 — do not commit
```

Then let go of the secret. The canary is a real GitLab project access token with the least GitLab allows — a Guest with the `create_runner` scope — so it is refused on every API call, and each refusal is recorded.

That is the default provider. An AWS decoy (`"provider": "aws_iam"`) is a key pair, placed as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` or as a profile in `~/.aws/credentials`; [Canary tokens](https://feintsecurity.com/docs/canary-tokens) has the details.

## 4. Watch for the alert

Feint polls GitLab every few minutes, and GitLab reports a token's use at most once every ten minutes or so, so the first alert lands within roughly a quarter of an hour of the first use. Your team is emailed at the same time, and any [webhook endpoint](https://feintsecurity.com/docs/webhooks) you registered receives it. The API shows the same events:

```bash title="curl"
curl -s "https://api.feintsecurity.com/v1/alerts?limit=20" \
  -H "Authorization: Bearer $FEINT_API_KEY"
```

```python title="Python"
r = requests.get(f"{API}/alerts", headers=HEADERS, params={"limit": 20})
r.raise_for_status()
for event in r.json():
    print(event["kind"], event["canary_token_id"], event["occurred_at"], event["source_ips"])
```

To see one without waiting for an intruder, use the canary yourself:

```bash
curl -s -o /dev/null -w "%{http_code}\n" \
  -H "PRIVATE-TOKEN: glpat-Xy7Q2k…" https://gitlab.com/api/v4/user
```

GitLab refuses the call and records the use anyway. A little later the alert appears, abridged:

```json
[
  {
    "id": "0b6d0f3e-7c1a-4e2b-8f9d-1a2b3c4d5e6f",
    "canary_token_id": "8d3e6f1a-2b4c-4d5e-9f01-23456789abcd",
    "kind": "canary.used",
    "occurred_at": "2026-09-26T09:41:00Z",
    "detected_at": "2026-09-26T09:45:12.088311Z",
    "source_ips": ["203.0.113.44"]
  }
]
```

## 5. Retire it

When the exercise is over, revoke the canary. GitLab revokes the token; the record stays in your list as `revoked`.

```bash title="curl"
curl -s -X DELETE https://api.feintsecurity.com/v1/tokens/8d3e6f1a-2b4c-4d5e-9f01-23456789abcd \
  -H "Authorization: Bearer $FEINT_API_KEY"
```

```python title="Python"
r = requests.delete(f"{API}/tokens/{canary['id']}", headers=HEADERS)
r.raise_for_status()
```

Or keep it. A canary costs nothing to leave in place; the default lifetime is 365 days and can be set per canary.

## Next

- [Authentication](https://feintsecurity.com/docs/authentication) — scopes, and giving a CI job only what it needs.
- [Canary tokens](https://feintsecurity.com/docs/canary-tokens) — every field, rotation, limits.
- [Alerts](https://feintsecurity.com/docs/alerts) — polling without missing or double-counting an event.
