Skip to main content
Automatic claiming is deployed in the 6 September 2026 native-claim contracts. The examples target Owner 0.7.0 and Holder 0.5.0; their publication and hosted console cutover are tracked independently in release status. Older namespaces remain owner-only unless they use a compatible native-claim deployment.
You build username registration inside your own application using the SDKs. Your users can choose a username during signup or later in account settings. You do not need to embed a Soran interface or send every user to soran.domains. The Owner SDK configures the namespace. The Holder SDK lets each user authorize their own claim and manage their name. The Lookup SDK reads the resulting records through Universal Lookup. An optional open-source claim application can provide a separate public interface under your control.

Configure registration

Claiming starts disabled. The namespace owner signs an on-chain configuration transaction before users can claim. The updated console provides these controls after its release cutover. A console update does not activate native claims on older contracts. Choose the following settings: The receiving wallet may differ from the wallet that owns the namespace. Only the current on-chain namespace owner can publish these settings. A console team role alone does not grant contract signing authority.

Where username fees go

The namespace’s Registrar stores the configured fee and receiving wallet on chain. When someone claims a name, the app reads those settings and shows the exact amount before requesting authorization. The claim transaction transfers the username fee directly from the claimant to the configured treasury. It also issues the name and initializes the user’s complete receiving details. These effects either succeed together or roll back together. There is no separate treasury withdrawal step for this fee. 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. Free registration means a zero username fee, not a free network transaction. Contract execution and storage rent still cost XLM. Initial contract storage rent can be substantially higher than later actions, and storage upkeep can incur additional costs. Always review the current transaction estimate. Owner 0.7.0 and Holder 0.5.0 default to a 5 XLM network-fee limit for native claim operations. 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 maxNativeFeeStroops after their user reviews the estimate. The updated owner console also exposes this separate limit. Do not automatically raise it 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. This username fee is separate from Soran’s 5,000 testnet XLM namespace application fee. The namespace application’s objection, withdrawal and refund rules do not become the username claim’s pricing or refund policy. See namespace claiming.

Open or restricted registration

Open registration allows any wallet satisfying the on-chain rules to claim. It needs no backend eligibility key. Charging a fee does not change that: a paid namespace can still offer open registration. An invitation list suits a known set of eligible wallets. An organization that needs to admit newly eligible app accounts can choose optional app-approved registration. Its account service provides a scoped approval, and the user still authorizes the claim. This is automatic backend approval, not a request for the human namespace owner to sign each signup. App-approved registration requires an explicitly configured approval account, approval lifetime and namespace-wide admission allowances. The total allowance is a cumulative ceiling. Raising it requires an owner-authorized change; key rotation, mode changes and ownership transfer do not reset usage. The rate limit also retains its active window across these changes. A wallet quota is not proof of 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

Reserve labels before opening registration, or reserve additional available labels later. A reserved, unassigned label has no holder and does not resolve. Use the dedicated owner-authorized assignment action to issue a reserved username. You do not need to briefly release it and expose it to other claimants. Reserved assignment is the declared administrative exception to ordinary username fees and claim quotas; network costs still apply. Releasing a reservation makes the label subject to normal claim availability. It does not revoke an already issued name. Reservations also do not disable an existing holder’s permitted payment updates, transfer or renewal.

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.
Receiving details can be a classic G address without a memo, G with a supported memo, a muxed M address, or a C contract destination. A required memo or muxed routing ID stays part of the complete on-chain destination. Supporting a receiving address does not mean every wallet type can sign claims. The initial signer flow uses a supported G transaction-source account. See payment destination examples. 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

These examples target Owner 0.7.0 and Holder 0.5.0. Check their publication status before installing them. Supply the verified native-claim Registry explicitly; an older deployment cannot provide this interface. The acme namespace below is illustrative.
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. It does not revoke issued names. In Public mode, ordinary legacy issue and issueBatch calls are blocked; reserved assignment is the explicit exception. Switching back to Manual mode is an owner-authorized policy change, not an immutable fairness guarantee. Reservation batches contain 1–23 distinct canonical labels and succeed or fail as a group. owner.assignReserved(namespace, label, holderG) assigns a reserved name using the holder’s default receiving address. Its reservation remains until releaseReservations is called. A custom initial destination needs the holder’s consent: the raw contract has an atomic co-authorized method, while this SDK’s simple assignment method leaves a later setPayment to that holder.

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. The name holder and fee payer remain the user’s G account even when the receiving destination differs.

Recover before replacing a request

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. Configure all four approval limits explicitly: cumulative allowance, claims per window, window length and approval lifetime. No budget is selected for you. The contract’s window maximum is one year, and its approval lifetime maximum is one hour; the SDK additionally bounds approval credentials to at most 60 future ledgers. A compromised approver can admit attacker-controlled wallets within those limits. It cannot use that admission signature to debit another user’s wallet, alter an existing holder’s payment route or undo a completed permanent 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 use renewName with their exact generation, existing expiry, fixed policy term and reviewed new-expiry bounds. There is no separate username renewal fee. Renewal is independent of new-claim pause, reservations and app approval. The earliest renewal window is the shorter of 30 days or one policy term before expiry. The resulting lease must also remain within 100 years of the execution time, so that limit can postpone renewal for very long terms. An expired name may be renewed by its recorded holder only until someone reissues it. There is no guaranteed grace period. Renewal preserves the generation and reactivates its previous receiving instructions; renewalPreview(name) shows that exact-generation destination before authorization. A zero-expiry or permanent name has no finite term to renew. Existing policy-based owner reclaim rights remain. See the native contract API and native claim storage reference for exact types, authorization, receipt and lifecycle boundaries.