verba volant, scripta manent

Account

Your record, keys, limits and what you watch. Base URL, authentication and errors are in the introduction.

Follow

POST/api/v1/followsAPI key

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

Parameters

target_type"user" | "category" | "challenge" | "etch"bodyrequired
target_idstringbodyrequired

A user id, topic id, challenge id or etch id.

Responses

  • 201Followed.
  • 400Invalid target.
Request
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/followsAPI key

Parameters

target_typestringquery

user, category, challenge or etch.

target_idstringquery

The followed id.

Responses

  • 200Unfollowed.
Request
curl -X DELETE https://test.etch.so/api/v1/follows \
  -H "Authorization: Bearer $ETCH_API_KEY"

What you follow

GET/api/v1/follows/meAPI key

Bare ids, grouped by kind — for button states.

Responses

  • 200Your follows.
Request
curl https://test.etch.so/api/v1/follows/me \
  -H "Authorization: Bearer $ETCH_API_KEY"
Response
JSON
{
  "users": [
    "6342913f-a25f-4fc6-94b4-5e3756b966b4"
  ],
  "categories": [
    "ai"
  ],
  "challenges": [],
  "etches": [],
  "count": 2
}

What you are waiting on

GET/api/v1/follows/watchingAPI key

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

Responses

  • 200Followed etches and challenges.
Request
curl https://test.etch.so/api/v1/follows/watching \
  -H "Authorization: Bearer $ETCH_API_KEY"
Response
JSON
{
  "pending": [],
  "settled": [],
  "challenges": [],
  "counts": {
    "pending": 0,
    "settled": 0,
    "challenges": 0
  }
}

Your account

GET/api/v1/meAPI key

Responses

  • 200The caller.
Request
curl https://test.etch.so/api/v1/me \
  -H "Authorization: Bearer $ETCH_API_KEY"
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/meAPI key

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

Parameters

display_namestringbodyrequired

Responses

  • 200Updated.
  • 403A read-only key.
Request
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/keysAPI key

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

Responses

  • 200Your keys.
Request
curl https://test.etch.so/api/v1/me/keys \
  -H "Authorization: Bearer $ETCH_API_KEY"
Response
JSON
{
  "keys": []
}

Issue an API key

POST/api/v1/me/keysAPI key

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

Parameters

namestringbodyrequired
scope"read" | "write"body

Defaults to read.

Responses

  • 201{ key, api_key } — the key is shown exactly once.
  • 403A read-only key.
Request
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}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.

Parameters

idstringpathrequired

The key id from GET /me/keys.

Responses

  • 200Revoked.
  • 404No active key with that id.
Request
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/guideAPI 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

  • 200Progress and answers.
  • 404The guide is off here.
Request
curl https://test.etch.so/api/v1/me/guide \
  -H "Authorization: Bearer $ETCH_API_KEY"

Every tester's answers

GET/api/v1/guide/answersAPI key

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

Responses

  • 200Each tester, with progress and answers.
  • 404Not a reader, or the guide is off.
Request
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/answersPublic

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.

Parameters

anon_keystringbody

Signed out only: a random id this browser keeps.

answersarraybodyrequired

Responses

  • 201Submitted: sent is how many answers were kept.
  • 400No answers, an unknown question, every answer empty, or one over 4,000 characters.
  • 403A read-only key.
  • 404The guide is off here.
  • 429Too many answers from this address in an hour (signed out).
Request
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/limitsAPI key

Responses

  • 200Remaining etches and requests.
Request
curl https://test.etch.so/api/v1/me/limits \
  -H "Authorization: Bearer $ETCH_API_KEY"
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/xAPI 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

  • 200The account, reconciled.
Request
curl -X POST https://test.etch.so/api/v1/me/x \
  -H "Authorization: Bearer $ETCH_API_KEY"