# Register a handle
URL: /docs/x1id/register

> Every on-chain registration path in the SDK — permanent, lease-to-own, front-run-safe commit-reveal, and claiming a gifted name — as unsigned instruction builders you sign and relay yourself.

# Register a handle

`@x1id/resolve` ships instruction builders for **every registration path**. Each returns an unsigned `BuiltInstruction` — `{ programId, keys, data }` — that you assemble into a transaction, **sign, and relay yourself**. The SDK never holds keys, and every discriminator and account order matches the deployed program.

<LlmActions />

<Callout type="info">
  This page targets **`@x1id/resolve@0.21.0`**. The registry is on **X1 mainnet** at `8JgnNWi24bq9uzfnT9XmkWxvaWMVgoEs9bu8QsHhLe1P` (`https://rpc.mainnet.x1.xyz`; testnet works the same with `https://rpc.testnet.x1.xyz`).
</Callout>

<Callout type="warn">
  **Registrations and marketplace actions may be paused.** The registry has an emergency lockdown, used for example during a security audit. While it's on, the program rejects new registrations, NFT mints, reserved-name and gift claims, TLD creation, and new listings, offers and auctions (`ProtocolLockedDown` / `MarketPaused`). Resolution, transfers, `set_primary` and records keep working, as do exits such as cancelling a listing, completing an existing sale, and lease renewal. Check the banner on [app.x1id.io](https://app.x1id.io), or read the flag from the `Config` account with the SDK's `readPauseState`.
</Callout>

## What ships, and what doesn't

Registration is in the SDK. Only **`transfer`** and **`set_primary`** are not yet exposed as builders — run those from [app.x1id.io](https://app.x1id.io) for now.

| Flow                           | Builder(s)                                                                              |
| ------------------------------ | --------------------------------------------------------------------------------------- |
| Permanent (one-time)           | `buildRegisterIx` (+ `fetchConfigTreasury`)                                             |
| Lease-to-own                   | `buildRegisterLeasedIx`, `buildRenewLeaseIx`, `buildReclaimExpiredLeaseIx`              |
| Commit–reveal (front-run-safe) | `commitmentHash`, `buildCommitIx`, `buildRegisterRevealedIx`, `buildCancelCommitmentIx` |
| Claim a gifted name            | `buildClaimVoucherIx`, `buildClaimVoucherTokenizedIx`                                   |
| Reserved name (admin)          | `buildRegisterReservedIx`                                                               |

<Callout type="warn">
  **Price is computed on-chain.** `register`, `register_leased` and `register_revealed` all compute their price at execution time (a time ramp over the on-chain tier table that rises over time) — there is **no client-supplied price or slippage parameter**; the payer pays whatever the program charges as of the landing slot. These builders do not know or assert the price. To show the payer a figure first, **simulate the transaction**, or read the live `/quote/:name` [REST endpoint](/docs/x1id/api#price-quote).
</Callout>

### Deriving PDAs

The builders do **not** derive PDAs for you (a second on-curve implementation is how wrong addresses get derived silently). Derive them with your runtime's canonical `findProgramAddress`, or the [`WasmResolver`](/docs/x1id/sdk#wasmresolver) helpers. The seeds you need:

* `["config"]` — the singleton config account
* `["handle", name_bytes]` — the handle being claimed (`name` is the canonical form, from `parseName(input).canonical`)

## Permanent registration

The default: pay the one-time ramp price and claim an unregistered `["handle", name]` PDA.

```ts
import { buildRegisterIx, fetchConfigTreasury, HandleType } from '@x1id/resolve';

const treasury = await fetchConfigTreasury(rpc, configAccount); // Config.treasury, read fresh
const ix = buildRegisterIx({
  programId,
  payer,                   // signer — pays the fee and the Handle account's rent
  owner,                   // need NOT sign — register on someone else's behalf (gifting, onboarding)
  config: configAccount,   // the ["config"] PDA
  treasury,                // Config.treasury — admin-rotatable, don't cache across a session
  handle,                  // the ["handle", name] PDA — must be unregistered
  name: 'payday-bot',      // canonical form
  handleType: HandleType.Agent,
  // optional revenue-share pair (#7397) — supply BOTH or NEITHER:
  // integrator, integratorAllowlist,
});
// assemble, sign with `payer`, relay.
```

`HandleType` is descriptive only (it gates nothing): `Human` (0), `Merchant` (1), `Org` (2), `Agent` (3).

<Callout type="info">
  **The NFT-included default.** The product's standard consumer registration folds a tradeable capability **pNFT** into the single one-time price (see [Fees](/docs/x1id/pricing)). That path is the program's `register_tokenized` instruction, which app.x1id.io uses; in `@x1id/resolve@0.21.0` it does **not** yet have a standalone builder. `buildRegisterIx` builds the base `register` (a name-only handle owned by `owner`), which can be minted into its NFT later. The one tokenizing path with a builder today is the **gift** claim, [`buildClaimVoucherTokenizedIx`](#claim-a-gifted-name), which registers *and* mints the pNFT in one instruction.
</Callout>

## Lease-to-own

A sibling of `register` for term leases — the permanent path is untouched and stays the default. Pay a fraction up front and build equity toward the permanent price; `payment` is in **lamports**, capped on-chain at the permanent price (offering the full price settles the handle permanently in one instruction).

```ts
import { buildRegisterLeasedIx, buildRenewLeaseIx, buildReclaimExpiredLeaseIx, fetchConfigTreasury, HandleType } from '@x1id/resolve';

const treasury = await fetchConfigTreasury(rpc, configAccount);

// 1. Claim under a lease, paying the first installment now:
const leaseIx = buildRegisterLeasedIx({
  programId, payer, owner, config: configAccount, treasury, handle,
  name: 'studio', handleType: HandleType.Org,
  payment: 25_000_000_000n, // lamports toward the lease; >= permanent price settles it outright
});

// 2. Pay more toward a lease — PERMISSIONLESS (anyone may keep someone else's lease alive):
const renewIx = buildRenewLeaseIx({
  programId, payer, config: configAccount, treasury, handle, payment: 10_000_000_000n,
});

// 3. Take over a leased handle whose grace period has ENDED, under a fresh lease:
const reclaimIx = buildReclaimExpiredLeaseIx({
  programId, payer, owner, config: configAccount, treasury, handle,
  handleType: HandleType.Org, payment: 25_000_000_000n,
});
```

The program enforces `now >= expires_at + grace` before a reclaim succeeds. `renew_lease` and `reclaim_expired_lease` are both permissionless by design.

## Commit–reveal (front-run-safe)

The anti-front-running path: stake out a hash of your intended name, wait `Config.min_commitment_age_secs`, then reveal. The name never touches the chain until reveal. The result is a **permanent** registration, identical to `register`.

```ts
import {
  commitmentHash, buildCommitIx, buildRegisterRevealedIx, buildCancelCommitmentIx,
  fetchConfigTreasury, HandleType,
} from '@x1id/resolve';

// 1. Generate a secret and persist it BEFORE committing — a lost secret strands the commitment.
const secret = crypto.getRandomValues(new Uint8Array(32));
const hash = await commitmentHash({
  name: 'moonshot', owner, handleType: HandleType.Human, secret, programId,
});
// derive ["commitment", hash] with your runtime's findProgramAddress → `commitment`

// 2. Commit — only the hash goes on chain:
const commitIx = buildCommitIx({ programId, payer, commitment, commitmentHash: hash });

// 3. After min_commitment_age_secs (and before max), reveal:
const treasury = await fetchConfigTreasury(rpc, configAccount);
const revealIx = buildRegisterRevealedIx({
  programId, payer, owner, config: configAccount, treasury, handle, commitment,
  name: 'moonshot', handleType: HandleType.Human, secret,
  // optional integrator pair, same contract as register's
});

// Escape hatch — reclaim an unconsumed commitment's rent at any age (payer-only):
const cancelIx = buildCancelCommitmentIx({ programId, payer, commitment });
```

The commitment hash is `sha256(name ‖ owner ‖ handle_type ‖ secret ‖ program_id)` — the program id in the preimage is domain separation, so a commitment can't replay against a different deployment. The reveal's `owner` **must** match the one the commitment hashed, or it fails closed (`CommitmentMismatch`). The reveal is valid only inside the window: at least `min_commitment_age_secs` after the commit (`CommitmentTooNew`) and strictly before `max_commitment_age_secs` (`CommitmentTooOld`). Use `fetchCommitment(rpc, commitmentAccount)` to read a commitment's `payer` / `committedAt`.

<Callout type="warn">
  **Persist the secret the moment you generate it, before `commit` is even sent.** Without it the hash can't be recomputed, so the commitment can only ever be cancelled (which at least needs its address, discoverable via `getProgramAccounts` on the payer).
</Callout>

## Claim a gifted name

A [gift](/docs/x1id/features#gift-a-handle) is a prepaid voucher: the giver escrows the mint price into `["voucher", name]`, and the recipient claims it later, paying only the transaction fee and the handle's rent. **`buildClaimVoucherTokenizedIx` is the default path** the live gift flow uses — it registers the handle *and* mints its tradeable capability pNFT in one instruction.

```ts
import { buildClaimVoucherTokenizedIx } from '@x1id/resolve';

const ix = buildClaimVoucherTokenizedIx({
  programId, claimer, config: configAccount, treasury, voucher,
  voucherPayer,          // the original giver — receives the voucher's rent on close
  owner,                 // who the pNFT + ownership go to (bound recipient, or claimer for an open voucher)
  handle, name: 'gift1',
  mint, ownerTokenAccount, metadata, masterEdition, tokenRecord,
  collectionMint, collectionMetadata, collectionMasterEdition, collectionAuthority,
  // integrator — required iff the voucher carries one
});
```

<Callout type="warn">
  **Prepend a compute-budget instruction.** The register + 3 Metaplex CPIs exceed the 200k default compute budget — add a `ComputeBudget.setComputeUnitLimit` (the app uses \~400k) in the **same** transaction, or the claim fails on-chain.
</Callout>

A name-only variant, `buildClaimVoucherIx`, claims without minting the pNFT; on a recipient-bound gift only the bound recipient can sign it (`VoucherClaimNotRecipient`), so a relayer claiming on someone's behalf uses the tokenized path. A gift's expiry can be at most 366 days out (`VoucherExpiryTooFar`). To *create* a gift, use `buildCreateVoucherIx`; to refund an unclaimed one after expiry, `buildRefundVoucherIx` — see the [SDK README](https://www.npmjs.com/package/@x1id/resolve).

## Reserved names (admin only)

`buildRegisterReservedIx` builds `register_reserved` — the **admin-only**, free registration of a reserved name **into the treasury** (on success the handle's owner is set on-chain to `Config.treasury`, not to an arbitrary target). The `admin` account must equal `Config.admin` and must sign; there is no price and no treasury-payment account. Reserved and premium names reach the public through the [marketplace](/docs/x1id/features#buy-and-sell-handles), not ordinary registration — this builder exists for the operator who seeds that inventory, not for an end user claiming a name.

A treasury-held reserved name can also be released to the verified holder of a matching domain. That path needs the **treasury multisig's** on-chain approval: `open_reserved_claim_approval` (permissionless) creates an empty approval account for the name, and `set_reserved_claim_approval`, signed only by the treasury (`NotTreasury` otherwise), stamps the sha256 of the one approved domain. The attestor can grant the name only to a holder of that approved domain (`ReservedClaimNotApproved` otherwise), so neither the admin nor the attestor alone can decide who receives a reserved name.

## Next

<Cards>
  <Card href="/docs/x1id/records" icon="Link" title="Point it at addresses" description="After registering, add and verify the multi-chain records a handle resolves to." />

  <Card href="/docs/x1id/pricing" icon="Tag" title="How pricing works" description="One-time pricing, the ramp, lease-to-own, and the NFT-included model." />

  <Card href="/docs/x1id/sdk" icon="Code" title="SDK reference" description="resolve, reverse, records, and loading the WASM module." />
</Cards>
