Skip to main content
Let users claim a username during signup or in account settings, inside your own application. You configure registration once; each user authorizes their own name, receiving details and fee. This guide uses Owner 0.12.0 and Holder 0.10.0 with the native-claim testnet deployment. Older contracts need compatible native-claim support; updating a frontend alone does not add it.
  1. Configure registration with the Owner SDK.
  2. Build the signup flow with the Holder SDK.
  3. Recover interrupted requests before retrying.
  4. Add app approval only if your membership rules require it.
Use Lookup to read the resulting records. You can build the interface yourself or run the optional open-source claim application.

Configure registration

Claiming starts disabled. The current on-chain namespace owner signs the configuration before users can claim. A console team role alone does not grant that authority. Choose the following settings: The receiving wallet may differ from the namespace owner’s wallet. In Public mode, ordinary owner issue and issueBatch calls are blocked even while claims are paused. Reserved assignment is the explicit exception.

Where username fees go

The Registrar stores the fee and receiving wallet on chain. Show the exact amount before asking the user to authorize it. One successful claim transaction:
  • Transfers the username fee from the claimant directly to the configured treasury.
  • Issues the name to the claimant.
  • Stores the user’s complete receiving destination and the operation receipt.
These effects succeed together or roll back together. The treasury does not need a separate withdrawal step. If the claim fails, its username fee transfer does not settle. Stellar network fees are separate and may still apply. A confirmed claim is not an escrowed application awaiting manual owner approval.

Username price and network costs

Free registration means a zero username fee. Contract execution and storage rent still cost XLM, and initial storage rent can be higher than later actions. Review the current transaction estimate. Owner 0.12.0 and Holder 0.10.0 default to a 5 XLM network-fee limit per transaction, including storage restoration. This limit is separate from the namespace’s username price; it is not the amount charged. An estimate above the limit is rejected before wallet signing. Applications can explicitly set maxNetworkFeeStroops after their user reviews the estimate. Native operations also respect maxNativeFeeStroops; when both are set, the lower limit applies. See write-client fee limits. Do not automatically raise a limit or assume an earlier estimate remains valid. The owner can update the fee or treasury with another on-chain settings transaction. A pending claim using older settings cannot silently charge a different price. Reconcile the original receipt and transaction before preparing a replacement. If the original did not apply and can no longer apply, create a new request ID and obtain the user’s authorization for the changed terms. Username prices are separate from the namespace application fee and refund rules.

Open or restricted registration

App approval is automatic backend admission. The human namespace owner does not sign every signup. App-approved registration requires a dedicated approval account and explicit lifetime and allowance limits. The total allowance is cumulative; raising it requires the owner’s authorization. Key rotation, mode changes and ownership transfer do not reset usage or the active rate-limit window. A wallet quota does not prove one private app account or one person. The app determines whether its account qualifies; the contract checks its approval and the claim rules. Membership is checked when the approval is issued. Logout, account suspension and unlinking a wallet do not recall an already signed approval or undo a confirmed claim. Ordinary app access remains governed by the app’s login and account rules.

Reserve usernames

  1. Reserve available labels with reserveNames, before or after opening registration. A reserved, unassigned label has no holder and does not resolve.
  2. Assign a reserved name with assignReserved. You do not need to release it first. This owner-authorized action bypasses ordinary username fees and claim quotas; network costs apply.
  3. Use releaseReservations when you want a label to follow normal claim availability again. Assignment alone leaves its reservation in place.
Releasing a reservation does not revoke an issued name. Reservations do not block existing holders’ permitted payment updates, transfers or renewals.

User signup and receiving details

  1. The user authenticates with your app and deliberately links a supported wallet.
  2. Your app checks the namespace’s current claim capability, rules and availability.
  3. The user chooses a name and receiving details and reviews any fee.
  4. The user authorizes the transaction. The contract enforces the selected rules.
  5. Your app verifies the confirmed receipt and current holder before associating the name with the existing app account.
Users can receive at a G address with or without a supported memo, a complete M address, or a C contract. A required memo or embedded M ID stays part of the destination. The SDK claim signer still uses a G transaction-source account. See destination examples. The examples below use a G claimant. Current contracts also accept C claimants when contract_claim_version() == 1; Holder’s contractWallet adapter supplies their authorization while a separate G signer submits. This requires a compatible wallet integration and does not extend the browser preparation routes automatically. See write-client configuration. Signing in again does not claim another name or charge again. An existing holder can link their name without making another paid claim. Keep a stable internal app account ID: transferring a username must never give its new holder access to the previous holder’s private account or data.

Confirmation and interruption

Persist the original public operation reference before submission. After a timeout or wallet interruption, recover that operation’s result before preparing another claim. A completed receipt describes historical success; separately verify current ownership, because the name may since have transferred or been reissued. A quote does not reserve a name. The first valid claim confirmed on chain wins. An unavailable or archived read is not proof that a name is free. Pausing new registration or an approval-service outage does not remove existing holders’ permitted contract operations.

Owner SDK example

Install the matching packages:
Supply the native-claim deployment explicitly. The illustrative namespace acme must be one your operator wallet owns.
A nonzero feeAmount is the owner-selected number of XLM base units; one XLM is 10_000_000 base units. Use bigint and exact decimal conversion. Read back owner.claimPolicy("acme") to display the current settings. Each settings action is a separate transaction; preserve its hash and reconcile an uncertain result before repeating it. owner.pauseClaims(namespace) pauses new public claims without revoking issued names. The owner can also switch back to Manual mode; Public allocation is not an immutable fairness guarantee. Reservation batches contain 1–23 distinct canonical labels and succeed or fail as a group. assignReserved(namespace, label, holderG) uses the holder’s default receiving address. A custom destination needs that holder’s consent: the SDK’s simple method leaves a later setPayment to the holder. The contract API also exposes an atomic assignment with both parties’ authorization.

Claim from your application

For open registration, no approval service is needed. Keep the selected intent unchanged while the wallet reviews and submits it.
The five-minute business deadline above is an example, not a reservation. The contract accepts at most one hour, and restricted mode may require a shorter lifetime. A successful historical recovery returns status: "replayed" and transaction: null; it sends no second claim transaction. To use different receiving instructions, change the destination passed to createClaimIntent before review: Use the recipient’s actual route. An M address contains a base G account and a 64-bit ID; that ID is not a transaction memo. In this G-account example, the name holder and username-fee payer remain the user’s G account even when the receiving destination differs.

Recover before replacing a request

What recovery verifies

Holder 0.10.0 checks Registrar attestation and deployed code after each receipt read or transaction confirmation. It requires RPC ledger context at least as recent as the result, including for a null receipt. Missing, older or mismatched context stops recovery. Retain the original request and transaction hash. These checks depend on your configured RPC reporting state and ledgers honestly. Later owner, policy or Resolver changes alone do not erase historical receipts. An upgraded or tainted Registrar cannot provide trusted receipt history. A null receipt does not prove failure. The original transaction may still be pending. A successful receipt does not prove current ownership. Read the current holder, generation and destination through Universal Lookup before linking the name to a private app account. Changing a fee, destination, holder, business deadline or epoch creates a different intent and requires a new request ID after reconciliation. Refreshing only the native authorization credential’s nonce/signature for the identical still-valid intent keeps the same business request ID.

Add app approval only when needed

For restricted signup, use a dedicated G eligibility account. Keep it separate from the namespace owner, treasury and claimant. The backend authenticates the app account and wallet link, then signs the exact claim intent using signClaimEligibility. Its trusted configuration, deployment anchors and expiry bounds must come from independent chain reads, not the applicant’s request. A server-side integration can keep account checks separate from contract signing:
The two declared functions are application responsibilities: enforce the current server-authenticated account and wallet binding, then independently read and verify the configured namespace/deployment and current claim policy. Never implement the trusted-context function by returning applicant-supplied configuration. The signer belongs on your server or signing service, not in frontend code. The client calls holder.buildClaim(intent) to obtain the unsigned admission entry without signing or submitting a transaction. After the backend supplies the scoped native authorization entry, pass it as holder.claim(intent, { eligibilityAuthorization, onPrepared }). The user’s wallet still authorizes the transaction. The approver entry contains only the exact Registrar claim intent and no fee-transfer authorization.

Bound the approval service

Configure all four limits explicitly; the SDK does not choose them for you: The SDK also limits approval credentials to 60 future ledgers. A compromised approver can admit attacker-controlled wallets within those budgets. Its admission signature cannot debit another user’s wallet, change an existing holder’s payment route or undo a completed claim. For a stable invitation cohort, use buildClaimAllowlist and publish its complete root through configureClaims. Keep the complete wallet list and distribute each wallet’s proof. Rebuilding a root from a smaller subset replaces the invitation list; it does not append to it. Frontend login alone never restricts an Open contract policy.

Existing holders after signup

Payment updates and permitted transfers do not need the app approval service. acceptNameTransferWithDestination lets a recipient accept and initialize their chosen receiving details in one transaction, preserving the name’s expiry. Finite-term holders can renew their name without a separate username renewal fee. Renewal is independent of claim pause and app approval, but still follows the immutable term, generation and lease limits. Reservation does not prevent renewal of a live name or one still protected by applicable grace. The owner can configure up to 90 days of renewal grace; it defaults to zero and applies only to expiries covered by the current activation cutoff. Grace prevents ordinary reissue but does not keep an expired name resolving. After grace, renewal can still succeed before reissue, except for a now-reissuable label the owner has reserved. See the exact renewal rules. renewalPreview shows the stored destination that renewal will reactivate. Zero-term names cannot renew, and policy-based owner reclaim rights remain. See the native contract API and native claim storage reference for exact types, authorization, receipt and lifecycle boundaries.