# Account

Your record, keys, limits and what you watch.

## Follow

`POST /api/v1/follows` — needs an API key

Follow an account, a topic, a challenge or a single etch. Followed etches and challenges surface at `GET /follows/watching`.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `target_type` | body | "user" | "category" | "challenge" | "etch" | yes |  |
| `target_id` | body | string | yes | A user id, topic id, challenge id or etch id. |

Responses:

- **201** Followed.
- **400** Invalid target.

```bash
curl -X POST https://test.etch.so/api/v1/follows \
  -H "Authorization: Bearer $ETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target_type": "category",
    "target_id": "crypto"
  }'
```

## Unfollow

`DELETE /api/v1/follows` — needs an API key

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `target_type` | query | string | no | `user`, `category`, `challenge` or `etch`. |
| `target_id` | query | string | no | The followed id. |

Responses:

- **200** Unfollowed.

```bash
curl -X DELETE https://test.etch.so/api/v1/follows \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

## What you follow

`GET /api/v1/follows/me` — needs an API key

Bare ids, grouped by kind — for button states.

Responses:

- **200** Your follows.

```bash
curl https://test.etch.so/api/v1/follows/me \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

Example response:

```json
{
  "users": [
    "6342913f-a25f-4fc6-94b4-5e3756b966b4"
  ],
  "categories": [
    "ai"
  ],
  "challenges": [],
  "etches": [],
  "count": 2
}
```

## What you are waiting on

`GET /api/v1/follows/watching` — needs an API key

Your followed etches and challenges, hydrated, soonest to resolve first.

Responses:

- **200** Followed etches and challenges.

```bash
curl https://test.etch.so/api/v1/follows/watching \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

Example response:

```json
{
  "pending": [],
  "settled": [],
  "challenges": [],
  "counts": {
    "pending": 0,
    "settled": 0,
    "challenges": 0
  }
}
```

## Your account

`GET /api/v1/me` — needs an API key

Responses:

- **200** The caller.

```bash
curl https://test.etch.so/api/v1/me \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

Example response:

```json
{
  "user": {
    "id": "2d052485-adbf-4ce8-b47f-1af583c29183",
    "username": "orin",
    "display_name": "Orin Vasquez",
    "x_handle": "orinv",
    "x_verified_at": "2026-08-22T19:44:35.581+00:00",
    "created_at": "2026-10-01T19:44:36.2153+00:00",
    "x_linked": true
  }
}
```

## Edit your display name

`PATCH /api/v1/me` — needs an API key

Only `display_name` is writable. Username and verified handle are derived, never set here.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `display_name` | body | string | yes |  |

Responses:

- **200** Updated.
- **403** A read-only key.

```bash
curl -X PATCH https://test.etch.so/api/v1/me \
  -H "Authorization: Bearer $ETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Orin Vasquez"
  }'
```

## List your API keys

`GET /api/v1/me/keys` — needs an API key

Prefixes and last-used times only. Keys are stored as sha256 — there is nothing to return.

Responses:

- **200** Your keys.

```bash
curl https://test.etch.so/api/v1/me/keys \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

Example response:

```json
{
  "keys": []
}
```

## Issue an API key

`POST /api/v1/me/keys` — needs an API key

Returned once, never again. A read-only key cannot issue keys — otherwise the scope would be decorative.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `name` | body | string | yes |  |
| `scope` | body | "read" | "write" | no | Defaults to `read`. |

Responses:

- **201** `{ key, api_key }` — the key is shown exactly once.
- **403** A read-only key.

```bash
curl -X POST https://test.etch.so/api/v1/me/keys \
  -H "Authorization: Bearer $ETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "research bot",
    "scope": "write"
  }'
```

## Revoke a key

`DELETE /api/v1/me/keys/{id}` — needs an API key

Takes effect immediately. The row is kept, not deleted — it is the evidence the key existed and was revoked. A key may revoke itself.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | The key id from `GET /me/keys`. |

Responses:

- **200** Revoked.
- **404** No active key with that id.

```bash
curl -X DELETE https://test.etch.so/api/v1/me/keys/$ID \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

## Your progress through the tester guide

`GET /api/v1/me/guide` — needs an API key

Only where the tester guide is on (staging); 404 elsewhere. `progress` lists the things to try, each done or not, read from what the account has actually done; `answers` maps question ids to every answer it has submitted, oldest first; `username` is its handle; `proof_etch` is a recent revealed etch to check; `reader` says whether it may read every tester's answers.

Responses:

- **200** Progress and answers.
- **404** The guide is off here.

```bash
curl https://test.etch.so/api/v1/me/guide \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

## Every tester's answers

`GET /api/v1/guide/answers` — needs an API key

For the accounts named in `GUIDE_READERS` only; 404 for anyone else and wherever the guide is off.

Responses:

- **200** Each tester, with progress and answers.
- **404** Not a reader, or the guide is off.

```bash
curl https://test.etch.so/api/v1/guide/answers \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

## Submit answers in the tester guide

`POST /api/v1/guide/answers` — public

Only where the tester guide is on. Each non-empty answer is kept as its own submission; submit as often as you like. Signed in, answers are kept with the account. Signed out, send `anon_key` — a random UUID the browser keeps, identifying no one — and submissions are metered per address.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `anon_key` | body | string | no | Signed out only: a random id this browser keeps. |
| `answers` | body | array | yes |  |

Responses:

- **201** Submitted: `sent` is how many answers were kept.
- **400** No answers, an unknown question, every answer empty, or one over 4,000 characters.
- **403** A read-only key.
- **404** The guide is off here.
- **429** Too many answers from this address in an hour (signed out).

```bash
curl -X POST https://test.etch.so/api/v1/guide/answers \
  -H "Content-Type: application/json" \
  -d '{
    "answers": [
      {
        "question": "would-etch",
        "answer": "Yes: a call on rates before the next meeting."
      }
    ]
  }'
```

## Your allowances

`GET /api/v1/me/limits` — needs an API key

Responses:

- **200** Remaining etches and requests.

```bash
curl https://test.etch.so/api/v1/me/limits \
  -H "Authorization: Bearer $ETCH_API_KEY"
```

Example response:

```json
{
  "account": {
    "kind": "human",
    "assurance": "human",
    "verified": true
  },
  "etches": {
    "per_day": 250,
    "remaining": 249,
    "refill_per_hour": 10.42
  },
  "authenticated_with": {
    "type": "session"
  }
}
```

## Re-check your linked X account

`POST /api/v1/me/x` — needs an API key

Takes no body: a caller cannot propose a handle, only ask the server to re-read what Supabase Auth holds and mirror it onto the account. Used after linking or unlinking X in a session.

Responses:

- **200** The account, reconciled.

```bash
curl -X POST https://test.etch.so/api/v1/me/x \
  -H "Authorization: Bearer $ETCH_API_KEY"
```
