> ## Documentation Index
> Fetch the complete documentation index at: https://docs.x1id.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Resolve API & SDK

> Look up the wallet a handle belongs to. Resolution is read-only and public — no wallet, no signature, and no API key. The REST base URL is https://api.x1id.io.

# Resolve API & SDK

Look up who owns a handle. Resolution is **read-only and public** — no wallet, no signature, and **no API key**. The REST base URL is `https://api.x1id.io`; there is no `/v1` prefix. The REST endpoint returns the handle's **owner**; for per-chain addresses (X1 / SOL / ETH / BTC), records, and on-chain writes, use the [`@x1id/resolve` SDK](#sdk-multi-chain-records-and-writes) below.

<Note>
  **Handles, not domains.** X1ID handles (`@jack`) and X1NS domains (`jack.x1`) are complementary — this API resolves handles. See [how they fit together](/faq#x1id-and-x1ns).
</Note>

<Info>
  The `@handle` registry is deployed on **X1 testnet** today. Resolutions reflect testnet state until the mainnet cutover, which is a separate, deliberate step — see [Trust & transparency](/trust).
</Info>

## Resolve a handle

```http theme={null}
GET /resolve/{name}
```

Look up the owner of a handle.

<ParamField path="name" type="string" required>
  The handle to resolve, with or without the leading `@`. Case-insensitive — `x1` and `@X1` resolve the same record. Given as a path segment: `/resolve/x1`.
</ParamField>

### Response

Returns the resolution object for the handle. It reports the current **owner** and whether the handle is **tokenized** — it does **not** carry an addresses map or an expiry (names are permanent, so there is nothing to expire). An unregistered handle returns `404 { "error": "handle not registered" }`.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.x1id.io/resolve/x1
  ```

  ```json Response theme={null}
  {
    "input": "x1",
    "name": "x1",
    "namespace": "handle",
    "owner": "H8FsKYhMyCN9brknY7tL4JXRbKZEjVh7XgrVmWcX1HiA",
    "tokenized": false,
    "verification": "owner"
  }
  ```
</CodeGroup>

<ResponseField name="input" type="string">
  The raw value you passed.
</ResponseField>

<ResponseField name="name" type="string">
  The normalized handle (no `@`).
</ResponseField>

<ResponseField name="namespace" type="string">
  Always `handle` for this endpoint.
</ResponseField>

<ResponseField name="owner" type="string">
  The wallet that owns the handle. For a **tokenized** handle this is informational — authority follows the NFT holder.
</ResponseField>

<ResponseField name="tokenized" type="boolean">
  Whether the handle has been turned into an NFT.
</ResponseField>

<ResponseField name="verification" type="string">
  `owner` when the owner controls the registry account.
</ResponseField>

<Tip>
  **Ownership you can audit.** Every handle is a record on the X1 registry program (`8JgnNWi24bq9uzfnT9XmkWxvaWMVgoEs9bu8QsHhLe1P`), so a resolution can be verified independently on-chain. See [Trust & transparency](/trust).
</Tip>

## Reverse lookup

```http theme={null}
GET /reverse/{address}
```

Go the other way — an address back to its `@handle`. Only the address the owner has explicitly marked as their **primary** reverse-resolves, which is what prevents impersonation. An address with no primary returns `404 { "error": "no primary set" }`.

<ParamField path="address" type="string" required>
  A base58 wallet address to reverse-resolve to its primary handle.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.x1id.io/reverse/H8FsKYhMyCN9brknY7tL4JXRbKZEjVh7XgrVmWcX1HiA
  ```

  ```json Response (no primary set) theme={null}
  {
    "error": "no primary set",
    "input": "H8FsKYhMyCN9brknY7tL4JXRbKZEjVh7XgrVmWcX1HiA"
  }
  ```
</CodeGroup>

When the address has a primary handle, the response carries that handle instead. A primary is only returned when it is still trustworthy — the pointer and the handle it names both exist, the pointer was set after the handle's current registration, and the address still holds authority over the name.

## Health

```http theme={null}
GET /health
```

A liveness check for the service.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.x1id.io/health
  ```

  ```json Response theme={null}
  { "ok": true }
  ```
</CodeGroup>

## SDK: multi-chain, records, and writes

The REST endpoint answers "who owns this handle." For **per-chain addresses**, **records**, and **on-chain writes**, use the [`@x1id/resolve`](https://www.npmjs.com/package/@x1id/resolve) SDK (`0.3.0`). It reads chain state directly over RPC — it never depends on this API's uptime — and its writes are unsigned instruction builders you sign and relay yourself; the SDK never holds keys. The `@handle` registry is on **X1 testnet** today (`https://rpc.testnet.x1.xyz`).

```bash theme={null}
npm install @x1id/resolve@0.3.0
```

<Note>
  Address derivation runs in a small Rust→WASM module, so `createResolver` needs the WASM bytes. The package ships them at `@x1id/resolve/wasm/x1_resolve_wasm.wasm` — load them once and reuse the resolver. Full loading recipes (Node and browser) are in the [SDK README](https://www.npmjs.com/package/@x1id/resolve).
</Note>

### Read: resolve across chains

`resolve(input, { chain })` returns a `Resolved` object. X1/SOL come from the handle's authority; ETH/BTC from its on-chain records. A handle with no record for the requested chain is explicit (`no-record-for-chain`), never a wrong address on the wrong chain.

<CodeGroup>
  ```ts resolve.ts theme={null}
  import { createResolver, WasmResolver } from '@x1id/resolve';

  const wasm = await WasmResolver.fromBytes(/* module bytes */);
  const x1id = createResolver({ rpcUrl: 'https://rpc.testnet.x1.xyz', wasm });

  const r = await x1id.resolve('@x1', { chain: 'SOL' });
  // {
  //   input: '@x1', name: 'x1', namespace: 'handle',
  //   address: 'H8FsKYhMyCN9brknY7tL4JXRbKZEjVh7XgrVmWcX1HiA',
  //   chain: 'SOL', verification: 'verified'
  // }

  // A handle with no record for the requested chain is explicit, never a
  // wrong address: resolve('@x1', { chain: 'ETH' }) throws
  // ResolveError { code: 'no-record-for-chain' }.
  ```
</CodeGroup>

<Warning>
  **Show the namespace before you send.** `@jack` and `jack.x1` can resolve to different owners, so every result carries its `namespace`. Render it — `@jack` or `jack.x1` — in the recipient field before a transfer is committed. `@jack.x1` (both shapes at once) throws `ResolveError { code: 'ambiguous' }` rather than guessing.
</Warning>

### Read: reverse and records

```ts theme={null}
const name = await x1id.reverse(address);
// 'nike'  → the address's primary @handle (canonical, no '@')
// null    → no primary set

const recs = await x1id.records('@x1');
// HandleRecord[]: { account, handle, coinType, chain, value, address,
//                   verified, updatedAt, stale }   ([] when none are set)
```

Records are judged against a universal staleness rule: a record whose `updatedAt` predates the handle's current registration was left behind by a previous owner of the same name, so it is flagged `stale` and its stored `verified` is forced to `false`. Use the exported `liveRecords(recs)` to keep only the ones that belong to the current owner.

### Write: sign and relay yourself

Registration and `set_primary` run from [app.x1id.io](https://app.x1id.io) today. The SDK ships builders for the rest of the registry — each returns an unsigned `{ programId, keys, data }` you assemble, sign, and send:

<CodeGroup>
  ```ts subname.ts theme={null}
  import { buildCreateSubnameIx } from '@x1id/resolve';

  const ix = buildCreateSubnameIx({
    programId,          // 8JgnNWi24bq9uzfnT9XmkWxvaWMVgoEs9bu8QsHhLe1P
    payer,              // signer — funds the subname account's rent
    owner,              // signer — the parent handle's current authority
    parent,             // the parent ["handle", name] PDA
    subname,            // the ["subname", parent, label] PDA
    label: 'team',      // issues team.you
  });
  // add ix to a transaction, sign with payer + owner, send.
  ```
</CodeGroup>

Also shipped, same **build → sign → relay** shape:

| Capability                                      | Builders                                                                                     |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Gift a handle (prepaid voucher)                 | `buildCreateVoucherIx` (+ `HandleType`), `buildClaimVoucherIx`, `buildRefundVoucherIx`       |
| Integrator rev-share                            | `buildAddIntegratorIx`, `buildSetIntegratorRateIx`, `buildRemoveIntegratorIx`                |
| Name lock / timelocked unlock                   | `buildLockHandleIx`, `buildInitiateUnlockIx`, `buildCompleteUnlockIx`, `buildCancelUnlockIx` |
| Record-write delegation                         | `buildSetRecordDelegateIx`, `buildRevokeRecordDelegateIx`                                    |
| Typed text records (website / avatar / socials) | `buildCreateTextRecordIx`, `buildUpdateTextRecordIx`, `buildCloseTextRecordIx`               |
| Domain / social attestations                    | `buildCreateAttestationIx`, `buildCloseAttestationIx`                                        |

<Info>
  **Which RPC?** Point the resolver at the chain that holds the names you want. `@handle` resolution is on **X1 testnet** today; on an RPC where the registry is not deployed a handle is an honest `not-found`, never a fabricated address. The full builder list, PDA seeds, and error codes are in the [SDK README](https://www.npmjs.com/package/@x1id/resolve).
</Info>
