---
title: "Look up a reserved account by handle"
method: GET
path: "/v1/account/claim/lookup"
tags: ["claim"]
---

# Look up a reserved account by handle

`GET /v1/account/claim/lookup`

Look up a pre-registered (reserved) account by handle and return the set of
verification methods available to claim it. Lets a prospective claimer confirm
"this account is mine" and pick how they want to prove ownership.

**This endpoint is unauthenticated.** To avoid enumeration, the response is
identical for all miss reasons:

- Handle does not exist
- Handle resolves to an organic account (never reserved)
- Handle resolves to a reserved account that has already been claimed

In all of those cases, the response is ``{"is_claimable": false, ...}`` with
all other fields null/empty. Only handles that resolve to *unclaimed
pre-registered* accounts produce a positive response.

The endpoint is rate-limited per client IP. Both hit and miss branches
execute the same DB query pattern so response timing does not leak account
state.

The ``claim_options`` list contains one entry per verifiable identity attached
to the account. The frontend renders one button per option and dispatches on
``method`` to pick the right verification flow:

- ``method="otp"`` — email or SMS OTP. ``display`` is **masked** (e.g.
  ``a***@gmail.com``) since the underlying identity is PII.
- ``method="oauth_*"`` — OAuth flow for the named platform. ``display`` is the
  raw social handle/URL since social platforms are public by definition.

Only OAuth providers with working verification today are included
(see ``LinkedIdentityType.OAUTH_CLAIMABLE``). New platforms must be added to
that inclusion list explicitly once their verify endpoint ships.

Error codes:
- 422: Missing or malformed handle parameter
- 429: Rate limit exceeded for this client IP

## Query parameters

- `handle` string, required — Handle to look up (case-insensitive)

## Response `200`

Successful Response

- ClaimLookupResponse — Response for the public claim lookup endpoint. On a hit (account exists, is in the PRE_REGISTERED state), returns the full set of claim paths available so the frontend can render verification buttons for the user to choose from. On a miss — account doesn't exist, is organic, or has already been claimed — returns ``is_claimable=false`` with all other fields null/empty. The miss response is intentionally identical across miss reasons to avoid enumeration.
  - `is_claimable` boolean, required — True if the handle resolves to a pre-registered account that has not yet been claimed
  - `handle` string, nullable — The normalized handle. Only set when is_claimable=true.
  - `claim_options` ClaimOption[] — Verification methods available to claim this account. Empty when is_claimable=false.
    - `type` string, required — Display label for the identity type: email, phone, x, youtube, etc.
    - `display` string, required — Identity value safe for unauthenticated display. PII (email/phone) is masked; social handles are passed through.
    - `method` string, required — Machine-readable verification method the frontend should invoke. Maps to grove_api.types.VerificationMethod values.
  - `pre_registered_at` string, nullable — ISO timestamp of when the account was reserved. Only set when is_claimable=true.

## Other responses

- `422` — Validation Error
- `429` — Rate limit exceeded

---

[API](https://skmtc.net/grove/apis/grove-api.md) · [All operations](https://skmtc.net/grove/apis/grove-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/grove/grove-api/versions/f286b48a8105/schema)
