Skip to content

Authentication

The Tether API supports two authentication methods:

  • JWT access tokens (issued by passwordless auth)
  • API keys (long-lived management tokens)

Passwordless Auth

Tether uses magic code authentication — no passwords.

Request a Code

POST /auth/send-code
Content-Type: application/json

{
  "email": "you@example.com"
}

A magic code is sent to the provided email.

Exchange Code for Tokens (manual entry flow)

POST /auth/verify-code
Content-Type: application/json

{
  "email": "you@example.com",
  "code": "123456"
}

Response:

{
  "accessToken": "eyJ...",
  "refreshToken": "eyJ...",
  "email": "you@example.com"
}

When users click the magic link from email, clients can exchange the opaque token directly (without exposing email/code in the URL):

POST /auth/exchange-code
Content-Type: application/json

{
  "token": "opaque-one-time-token"
}

Response:

{
  "accessToken": "eyJ...",
  "refreshToken": "eyJ...",
  "email": "you@example.com"
}

Refresh Token (body mode)

POST /auth/refresh
Content-Type: application/json

{
  "refreshToken": "eyJ..."
}

Response:

{
  "accessToken": "eyJ...",
  "refreshToken": "eyJ..."
}

For browser clients, use cookie mode so refresh tokens are never readable by JavaScript:

  • Send header X-Tether-Session-Mode: cookie on:
  • POST /auth/verify-code
  • POST /auth/exchange-code
  • POST /auth/refresh
  • POST /auth/logout
  • Include credentials in browser requests.
  • API sets tether_refresh_token as an httpOnly cookie.
  • In cookie mode, refreshToken in JSON responses is blank (use cookie rotation instead).

Cookie-mode refresh call:

POST /auth/refresh
X-Tether-Session-Mode: cookie

Note

Refresh tokens are rotated on each use — the old token is invalidated and a new pair is returned. Always store and use the new refreshToken from the response (or updated cookie in cookie mode).

Using Access Tokens

Include the access token in the Authorization header:

Authorization: Bearer eyJ...

Get Current User

GET /auth/me
Authorization: Bearer eyJ...

Response:

{
  "email": "you@example.com",
  "createdAt": 1736899200000
}

List Active Sessions

Lists active refresh-token sessions for the currently authenticated user.

GET /sessions
Authorization: Bearer eyJ...

Response:

{
  "sessions": [
    {
      "tokenId": "f7d9...",
      "userAgent": "Mozilla/5.0 ...",
      "ipAddress": "203.0.113.10",
      "createdAt": 1736899200000,
      "lastUsedAt": 1736899800000,
      "expiresAt": 1739500000000
    }
  ]
}

Note

/sessions accepts access JWTs (not API keys).

Logout

Body mode:

POST /auth/logout
Content-Type: application/json

{
  "refreshToken": "eyJ..."
}

Cookie mode:

POST /auth/logout
X-Tether-Session-Mode: cookie

Invalidates the current refresh-token session.

API Keys

API keys are long-lived tokens for programmatic access. Use them to manage agents from CI/CD pipelines, scripts, or backend services without going through the magic code flow.

API keys use the sk-tether-name- prefix for easy identification and leak detection.

Create an API Key

Requires JWT authentication.

POST /api-keys
Authorization: Bearer eyJ...
Content-Type: application/json

{
  "name": "CI Pipeline",
  "expiresInDays": 90
}

Response:

{
  "id": "key_abc123",
  "key": "sk-tether-name-...",
  "name": "CI Pipeline",
  "keyPrefix": "sk-tether-name-abc",
  "expiresAt": 1748304000000,
  "createdAt": 1740528000000
}

Note

Timestamps are Unix epoch milliseconds. expiresAt is 0 if the key doesn't expire.

Warning

The full key value is shown only once. Store it securely — the API stores only a hash.

List API Keys

GET /api-keys
Authorization: Bearer eyJ...

Response:

[
  {
    "id": "key_abc123",
    "name": "CI Pipeline",
    "keyPrefix": "sk-tether-name-abc",
    "expiresAt": 1748304000000,
    "createdAt": 1740528000000,
    "lastUsedAt": 1740571200000,
    "revoked": false
  }
]

Revoke an API Key

DELETE /api-keys/{id}
Authorization: Bearer eyJ...

Using API Keys

Include the API key in the Authorization header, just like a JWT:

Authorization: Bearer sk-tether-name-...

API keys can be used with most agent endpoints (/agents/*) and domain endpoints (/domains/*). Creating and managing API keys themselves requires JWT authentication.

Note

Key lifecycle endpoints (GET /agents/{id}/keys, POST /agents/{id}/keys/rotate, POST /agents/{id}/keys/{keyId}/revoke) accept bearer auth (JWT or API key). Rotation/revocation require step-up verification (stepUpCode or challenge + proof).

For automation with API keys, prefer challenge + proof step-up.

Limits

Limit Value
API keys per account 10
Rate limit 10 requests/minute

Info

If you need more API keys, please contact us at support@tether.name.

Security Notes

  • API keys are hashed before storage — they cannot be retrieved after creation.
  • The sk-tether-name- prefix enables automated leak detection in logs and repositories.
  • Revoked keys are rejected immediately.
  • Set expiresInDays to limit key lifetime. Omit for non-expiring keys.

Public Endpoints

The following endpoints do not require authentication:

  • GET / — Health check
  • POST /challenge — Request a challenge
  • POST /challenge/verify — Submit proof
  • GET /challenge/{code} — Check challenge status
  • GET /AGENTS.md — Agent-readable verification guidance
  • GET /.well-known/tether-name.json — Machine-readable endpoint map and protocol metadata
  • GET /version — Build metadata
  • GET /stats — Global verification stats