Contents
API docs

Authentication

API keys, the scopes they carry, and what a refused request looks like.

View as Markdown

The API authenticates with keys, not sessions. A key is created in the console, belongs to the organization rather than to the person who made it, and carries a fixed set of scopes chosen when it is created.

Creating a key#

In the console, API keys → Create key. Give it a name that says what will hold it (ci-deploy, siem-poller) and tick the scopes it needs. The secret begins with ak_ and is shown once; Feint cannot show it again. Admins and members can create and delete keys; a read-only seat can use the console but not manage keys.

Keys are managed only in the console. There is no endpoint for creating, listing or deleting them.

Sending it#

Every request carries the key as a bearer token:

curl https://api.feintsecurity.com/v1/tokens \
  -H "Authorization: Bearer $FEINT_API_KEY"
import os
import requests

r = requests.get(
    "https://api.feintsecurity.com/v1/tokens",
    headers={"Authorization": f"Bearer {os.environ['FEINT_API_KEY']}"},
)

A console session is not accepted by the API, and a key is not accepted by the console. They are separate credentials for separate surfaces.

Acting as a client#

For MSSP partners. A key belongs to your organization and acts as it by default. To act as one of your clients instead, add its id as the X-Feint-Client header; GET /v1/clients lists the ids. Every endpoint honours the header, and every canary and event comes back with a client naming whose it is.

curl https://api.feintsecurity.com/v1/tokens \
  -H "Authorization: Bearer $FEINT_API_KEY" \
  -H "X-Feint-Client: cli_4f1c9a2b7e3d4c5a8b6e0f1d2c3b4a59"
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['FEINT_API_KEY']}"}
clients = requests.get("https://api.feintsecurity.com/v1/clients", headers=headers).json()
for client in clients:
    alerts = requests.get(
        "https://api.feintsecurity.com/v1/alerts",
        headers={**headers, "X-Feint-Client": client["id"]},
    ).json()

An id that is not one of your live clients is a 404, whatever it names.

Scopes#

Scope Grants
alerts:read View alerts generated by canary use
tokens:read List canary tokens and their placement metadata
tokens:write Request, rotate and revoke canary tokens

Scopes do not imply one another. A key with only tokens:write can issue, rotate and revoke canaries but cannot list them; a key that polls for alerts needs only alerts:read. Give each holder the least it needs:

Holder Scopes
A CI job that plants a canary in each new environment tokens:write
A script that rotates canaries on a schedule tokens:write, tokens:read
A SIEM or on-call integration reading alerts alerts:read

The scope each endpoint needs is listed in the overview.

When a request is refused#

Status detail Why
401 A valid API key is required. No Authorization header, or the key is not one Feint issued.
401 This API key is no longer valid. The key was deleted in the console.
403 This API key is missing the 'tokens:write' scope. The key is valid but was not given the scope this endpoint needs.
404 No such client. X-Feint-Client names no live client of your organization.

Feint confirms each key's standing with the key store and caches the answer for 30 seconds, so a deleted key stops working within that time.

Match on the status code, not the message; the wording may change.

Keeping keys safe#

  • Store a key in your CI system's masked variables or a secrets manager, never in the repository.
  • One key per holder, named for it, so that deleting one key stops one thing.
  • To rotate, create a new key, move the holder over, then delete the old one.
  • If a key leaks, delete it in the console. Anything using it is refused within 30 seconds.