# API Keys

API keys let you call any profile-scoped Ethos endpoint without a browser session. You sign a one-time [SIWE](https://eips.ethereum.org/EIPS/eip-4361) message with your wallet, receive a key, and include it in the `X-Ethos-Api-Key` header on every request.

**Prerequisites:** an [Ethos profile](https://app.ethos.network) linked to your wallet, and API key access enabled for your account.

## Quick start

Create a key, send an XP tip, revoke the key — all in one script. Or try the [interactive playground](https://trust-ethos.github.io/api-docs/playground/) to run each step in your browser.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import { privateKeyToAccount } from "viem/accounts";
import { createSiweMessage } from "viem/siwe";

const API = "https://api.ethos.network/api/v2";
const CLIENT = "my-bot@1.0.0"; // identifies your app

const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");

// --- 1. Create API key ---

const message = createSiweMessage({
  domain: "api.ethos.network",
  address: account.address,
  statement: "Create Ethos API key",
  uri: "https://api.ethos.network",
  version: "1",
  chainId: 8453, // Base
  nonce: crypto.randomUUID().replaceAll("-", ""),
  issuedAt: new Date(),
  expirationTime: new Date(Date.now() + 10 * 60 * 1000), // must be within 10 min
});

const signature = await account.signMessage({ message });

const keyResp = await fetch(`${API}/api-keys`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Ethos-Client": CLIENT },
  body: JSON.stringify({
    address: account.address,
    message,
    signature,
    name: "my-bot",
  }),
});

if (!keyResp.ok) throw new Error(`Create key failed: ${await keyResp.text()}`);

const { id: keyId, token } = await keyResp.json();
console.log("Key created:", keyId);

// --- 2. Send XP tip ---

const tipResp = await fetch(`${API}/xp/tip`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Ethos-Api-Key": token,
    "X-Ethos-Client": CLIENT,
  },
  body: JSON.stringify({
    receiverUserkey: "profileId:1",
    amount: 5,
    message: "5 XP, 10 lines of code — developers.ethos.network",
  }),
});

if (!tipResp.ok) throw new Error(`Tip failed: ${await tipResp.text()}`);

const tip = await tipResp.json();
console.log(`Tip #${tip.tipId} sent. Balance: ${tip.senderNewBalance} XP`);

// --- 3. Revoke key ---

const revokeResp = await fetch(`${API}/api-keys/${keyId}`, {
  method: "DELETE",
  headers: { "X-Ethos-Api-Key": token, "X-Ethos-Client": CLIENT },
});

if (!revokeResp.ok) throw new Error(`Revoke failed: ${await revokeResp.text()}`);
console.log("Key revoked");
```

{% endtab %}

{% tab title="Python" %}

```python
from datetime import datetime, timedelta, timezone
from eth_account import Account
from eth_account.messages import encode_defunct
from siwe import SiweMessage
import requests, secrets

API = "https://api.ethos.network/api/v2"
CLIENT = "my-bot@1.0.0"  # identifies your app

account = Account.from_key("0xYOUR_PRIVATE_KEY")
now = datetime.now(timezone.utc)

# --- 1. Create API key ---

msg = SiweMessage(
    domain="api.ethos.network",
    address=account.address,
    statement="Create Ethos API key",
    uri="https://api.ethos.network",
    version="1",
    chain_id=8453,  # Base
    nonce=secrets.token_hex(16),
    issued_at=now.isoformat(),
    expiration_time=(now + timedelta(minutes=10)).isoformat(),  # must be within 10 min
)
message_text = msg.prepare_message()
signature = "0x" + account.sign_message(
    encode_defunct(text=message_text)
).signature.hex()

key_resp = requests.post(f"{API}/api-keys",
    headers={"X-Ethos-Client": CLIENT},
    json={
        "address": account.address,
        "message": message_text,
        "signature": signature,
        "name": "my-bot",
    },
)
key_resp.raise_for_status()

data = key_resp.json()
token, key_id = data["token"], data["id"]
print(f"Key created: {key_id}")

# --- 2. Send XP tip ---

tip_resp = requests.post(f"{API}/xp/tip",
    headers={"X-Ethos-Api-Key": token, "X-Ethos-Client": CLIENT},
    json={
        "receiverUserkey": "profileId:1",
        "amount": 5,
        "message": "5 XP, 10 lines of code — developers.ethos.network",
    },
)
tip_resp.raise_for_status()

tip = tip_resp.json()
print(f"Tip #{tip['tipId']} sent. Balance: {tip['senderNewBalance']} XP")

# --- 3. Revoke key ---

revoke_resp = requests.delete(
    f"{API}/api-keys/{key_id}",
    headers={"X-Ethos-Api-Key": token, "X-Ethos-Client": CLIENT},
)
revoke_resp.raise_for_status()
print("Key revoked")
```

{% endtab %}

{% tab title="curl" %}

```bash
PRIVATE_KEY="0xYOUR_PRIVATE_KEY"
ADDRESS=$(cast wallet address "$PRIVATE_KEY")
API="https://api.ethos.network/api/v2"
CLIENT="my-bot@1.0.0"
NONCE=$(openssl rand -hex 16)
NOW=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
# macOS: date -u -v+10M    Linux: date -u -d "+10 min"
EXP=$(date -u -v+10M '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \
   || date -u -d "+10 min" '+%Y-%m-%dT%H:%M:%SZ')

# --- 1. Create API key ---

MESSAGE="api.ethos.network wants you to sign in with your Ethereum account:
${ADDRESS}

Create Ethos API key

URI: https://api.ethos.network
Version: 1
Chain ID: 8453
Nonce: ${NONCE}
Issued At: ${NOW}
Expiration Time: ${EXP}"

SIGNATURE=$(cast wallet sign --private-key "$PRIVATE_KEY" "$MESSAGE")

KEY_JSON=$(curl -s -X POST "$API/api-keys" \
  -H "Content-Type: application/json" \
  -H "X-Ethos-Client: $CLIENT" \
  -d "{
    \"address\": \"${ADDRESS}\",
    \"message\": $(printf '%s' "$MESSAGE" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))"),
    \"signature\": \"${SIGNATURE}\",
    \"name\": \"my-bot\"
  }")

TOKEN=$(echo "$KEY_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
KEY_ID=$(echo "$KEY_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Key created: $KEY_ID"

# --- 2. Send XP tip ---

curl -s -X POST "$API/xp/tip" \
  -H "Content-Type: application/json" \
  -H "X-Ethos-Api-Key: $TOKEN" \
  -H "X-Ethos-Client: $CLIENT" \
  -d '{
    "receiverUserkey": "profileId:1",
    "amount": 5,
    "message": "5 XP, 10 lines of code — developers.ethos.network"
  }'

# --- 3. Revoke key ---

curl -s -X DELETE "$API/api-keys/$KEY_ID" \
  -H "X-Ethos-Api-Key: $TOKEN" \
  -H "X-Ethos-Client: $CLIENT"
echo "Key revoked"
```

{% endtab %}
{% endtabs %}

## Creating a key

Key creation uses a SIWE (Sign-In with Ethereum) signature to prove wallet ownership. The server verifies the signature and returns a JWT-based API key.

### SIWE message requirements

| Field            | Value                       | Notes                                                                                    |
| ---------------- | --------------------------- | ---------------------------------------------------------------------------------------- |
| `domain`         | `api.ethos.network`         | Must match exactly                                                                       |
| `chainId`        | `8453`                      | Base mainnet                                                                             |
| `expirationTime` | Within 10 minutes           | Required — messages without it are rejected                                              |
| `nonce`          | Random hex string           | Single-use — the server rejects replayed nonces. Generate a fresh one for every request. |
| `uri`            | `https://api.ethos.network` |                                                                                          |
| `version`        | `1`                         |                                                                                          |

### Request: `POST /api/v2/api-keys`

| Field       | Type   | Required | Description                                         |
| ----------- | ------ | -------- | --------------------------------------------------- |
| `address`   | string | Yes      | Your Ethereum address (0x-prefixed, checksummed)    |
| `message`   | string | Yes      | The SIWE message you signed                         |
| `signature` | string | Yes      | ECDSA signature (0x-prefixed, 132 hex chars)        |
| `name`      | string | Yes      | Label for this key (1-100 chars)                    |
| `expiresAt` | string | No       | ISO 8601 datetime. Default: 90 days. Max: 365 days. |

### Response

```json
{
  "id": "cm...",
  "token": "ethos_eyJhbGci...",
  "name": "my-bot",
  "expiresAt": "2026-06-25T00:00:00.000Z"
}
```

{% hint style="warning" %}
The `token` is only returned once. There is no way to retrieve it later. Store it securely immediately.
{% endhint %}

## Authenticating requests

Include the key in the `X-Ethos-Api-Key` header on every request:

```http
X-Ethos-Api-Key: ethos_eyJhbGci...
X-Ethos-Client: my-bot@1.0.0
```

The key authenticates as the Ethos profile that created it. All profile-scoped endpoints (tips, reviews, votes, vouches) work the same way they do with browser session auth.

{% hint style="info" %}
If both `Authorization` (Bearer) and `X-Ethos-Api-Key` are present, the Bearer token takes priority.
{% endhint %}

## Revoking a key

```
DELETE /api/v2/api-keys/{id}
```

Pass the `id` returned when you created the key. Revocation takes effect immediately — subsequent requests with the revoked key return `401 Unauthorized`.

You can revoke a key using the key itself (as shown in the quick start) or with browser session auth.

## Reference

### Key lifecycle

| Property                | Value                                                           |
| ----------------------- | --------------------------------------------------------------- |
| Default expiry          | 90 days                                                         |
| Maximum expiry          | 365 days                                                        |
| Active keys per profile | 25                                                              |
| Usage tracking          | `lastUsedAt` updated per request (throttled to once per minute) |

### Endpoints

| Method   | Path                    | Auth                          |
| -------- | ----------------------- | ----------------------------- |
| `POST`   | `/api/v2/api-keys`      | None (SIWE signature in body) |
| `GET`    | `/api/v2/api-keys`      | API key or browser session    |
| `DELETE` | `/api/v2/api-keys/{id}` | API key or browser session    |

### Error codes

| HTTP | Code                 | Meaning                                     |
| ---- | -------------------- | ------------------------------------------- |
| 400  | `INVALID_AMOUNT`     | XP tip amount must be a positive integer    |
| 400  | `CANNOT_TIP_SELF`    | Sender and receiver are the same profile    |
| 400  | `INSUFFICIENT_XP`    | Not enough XP in your balance               |
| 401  | —                    | Invalid, expired, or revoked API key        |
| 403  | —                    | API key access not enabled for this profile |
| 404  | `RECEIVER_NOT_FOUND` | No Ethos user matches the receiver userkey  |
| 404  | `KEY_NOT_FOUND`      | Key ID not found or not owned by you        |
