# 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        |


---

# Agent Instructions: 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:

```
GET https://developers.ethos.network/api-documentation/api-key-authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
