> For the complete documentation index, see [llms.txt](https://docs.tokenbot.com/home/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tokenbot.com/home/api-docs/authentication.md).

# Authentication

TokenBot supports a few authentication methods depending on which API you're calling.

| API         | Methods                                                       |
| ----------- | ------------------------------------------------------------- |
| REST API    | **API key** (primary) or **signed request** (CLI fast-path)   |
| GraphQL API | **JWT** bearer token (from `login`) or CLI **signed request** |

***

## REST API Authentication

### API Key (primary)

Send your API key via the `X-API-Key` header:

```bash
curl https://api.tokenbot.com/v1/exchanges \
  -H "X-API-Key: tb_live_your_api_key"
```

You can also pass the same key as a bearer token — it's treated identically:

```bash
curl https://api.tokenbot.com/v1/exchanges \
  -H "Authorization: Bearer tb_live_your_api_key"
```

> Note: the REST API does **not** verify standalone JWTs. `Authorization: Bearer <value>` is interpreted as an API key.

#### API Key Format

| Environment | Format          | Example             |
| ----------- | --------------- | ------------------- |
| Production  | `tb_live_<...>` | `tb_live_a1b2c3...` |
| Test        | `tb_test_<...>` | `tb_test_a1b2c3...` |

#### Managing API Keys

Create and manage keys with the CLI:

```bash
tokenbot apikey create --name "ci-bot" --env live
tokenbot apikey list
tokenbot apikey revoke <id>
```

Or via the REST endpoints (these require a key with **ADMIN** permission):

| Operation | Endpoint                       |
| --------- | ------------------------------ |
| Create    | `POST /v1/api-keys`            |
| List      | `GET /v1/api-keys`             |
| Revoke    | `DELETE /v1/api-keys/:id`      |
| Rotate    | `POST /v1/api-keys/:id/rotate` |

### Signed Request (CLI fast-path)

The `tokenbot` CLI authenticates with a **secp256k1 keypair** instead of an API key. Each request carries four headers and a signature; no key is transmitted. The REST API verifies the signature first, and falls back to API-key auth when these headers are absent.

| Header           | Value                                             |
| ---------------- | ------------------------------------------------- |
| `x-tb-pubkey`    | Your compressed public key (hex)                  |
| `x-tb-timestamp` | Unix milliseconds                                 |
| `x-tb-nonce`     | 16 random bytes (hex)                             |
| `x-tb-sig`       | secp256k1 signature (hex) of the canonical string |

The **canonical string** that gets signed is:

```
${METHOD}\n${path}\n${timestamp}\n${nonce}\n${sha256(body)}
```

where `path` includes the query string and `body` is the raw request body (the empty string hashes to the SHA-256 of `""`). Requests are accepted within a ±60-second clock-skew window, and nonces are remembered for a 10-minute replay window.

> This scheme is implemented for you by `@tokenbot-org/cli-core`; you normally don't construct it by hand. It's documented here so server integrations can verify it.

***

## GraphQL API Authentication

The production GraphQL API is an **Express server** (deployed on ECS Fargate) at `https://gql-api.tokenbot.com/graphql`. It accepts two auth methods:

1. **JWT bearer token** — obtain an `access_token` from the `login` mutation (or OAuth / passkey login) and send it as `Authorization: Bearer <access_token>`.
2. **CLI signed request** — the same `x-tb-*` signed-request scheme used by the REST API (see above); this is how the `tokenbot` CLI authenticates.

Public operations (`register`, `login`, `forgot_password`, etc.) require no authentication.

> Heads-up: GraphQL previously ran on AWS AppSync with Cognito. That has been retired — there is no Cognito, AppSync API key, or `x-api-key` auth anymore.

### Logging In

```graphql
mutation {
  login(email: "user@example.com", password: "password", recaptcha_token: "token") {
    ... on AuthPayload {
      access_token
      refresh_token
      requires_2fa
      user { id email }
    }
    ... on Login2FAPendingPayload {
      user_id
      message
    }
  }
}
```

`login` returns a custom JWT payload — **`access_token` and `refresh_token`** (snake\_case). There is no `id_token`. Send the access token as `Authorization: Bearer <access_token>` on subsequent requests. If 2FA is enabled, you'll receive the 2FA-pending variant and must call `verify_2fa_code`.

## OAuth Providers

Social login is available through the GraphQL API:

| Provider  | Mutation             |
| --------- | -------------------- |
| Google    | `google_auth(token)` |
| Twitter/X | `x_auth(token)`      |
| Apple     | `apple_auth(token)`  |
| Telegram  | `telegram_auth(...)` |

All return the same `LoginResult` type with access/refresh tokens.

## Passkeys / WebAuthn

Passwordless authentication using FIDO2/WebAuthn:

1. **Register:** `generatePasskeyRegistrationChallenge` → `registerPasskey(credential, device_name)`
2. **Login:** `generatePasskeyLoginChallenge(email)` → `loginWithPasskey(credential, recaptcha_token)`
3. **Manage:** `userPasskeys`, `renamePasskey`, `deletePasskey`

## Two-Factor Authentication (2FA)

TOTP-based 2FA:

1. **Setup:** `generate_2fa` → returns secret/QR → `enable_2fa(token)` to confirm
2. **Login:** when 2FA is enabled, `login` returns the 2FA-pending payload → call `verify_2fa_code(user_id, token)`
3. **Disable:** `disable_2fa`

***

## Security Best Practices

* Never commit API keys to version control.
* Rotate keys periodically via `POST /v1/api-keys/:id/rotate` (or `tokenbot apikey`).
* Use the minimum required permissions for each key.
* Enable 2FA on your account.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tokenbot.com/home/api-docs/authentication.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
