Identity
Names

Names

An address can carry several names (a preferred .chain name, a middleware-resolved chain name, an X handle), and the Superhero API resolves one of them into a public_name for display. The name types differ in how much evidence backs them, so know which one you are looking at before you cache it.

Name types

TypeWhere it livesHow it gets thereVerified?
Preferred AENS nameAddressLink, provider prefaenslink_principal, which calls AENSv2.lookup inside the contractYes: ownership is checked on chain
Chain namechain_name on the accountResolved from the middleware, refreshed on an hourly sweep and on a stale readYes, by resolution rather than by claim
X handleAddressLink, provider xlink after an OAuth check the backend performsOff chain only; see X verification
Legacy usernameprofile.usernameRead-only legacy fieldNo

How a public name is resolved

The API walks four steps and returns the first non-empty value:

prefered_aens_name   (links.prefaens — the AENS name you explicitly linked)
chain_name           (resolved from the middleware)
username             (the legacy profile field)
address              (the ak_… itself)

A linked preferred name overrides the middleware-resolved one on purpose: an address can own several AENS names, and the middleware returns the first. Linking one says which.

The resolved value is public_name on GET /api/profile/{address} and GET /api/accounts/{address}. There is no per-account preference that changes the order.

How the chain name stays fresh

chain_name is cached with a chain_name_updated_at timestamp. An hourly sweep refreshes it, and GET /api/accounts/{address} also refreshes on read when the cached value is older than the staleness window, writing the new value back asynchronously. An expired or transferred AENS name therefore drops out of the profile within about an hour.

chain_expires_at in the profile aggregate is a legacy field and is not refreshed. To check whether a name is live, resolve it through AENS.

Reverse resolution

AddressLink provides two reverse lookups, both free dry-run reads backed by the contract's reverse map:

CallAnswers
resolve(provider, value)Which address holds this provider value, for example which address linked the X handle superherocom
resolve_signer(principal)Which address linked this AENS name as a principal

What the app displays

The profile header takes the first non-empty value of:

links.prefered_aens_name   (the preferred AENS name from the account aggregate)
public_name                (the name the API resolved, by the ladder above)
chainName                  (the AENS name looked up for the address)
formatAddress(address)     (a shortened ak_… fallback)

A verified badge is rendered when an x link exists for the account. See Verifying Your X Account for what that link proves.

Normalization

LayerRule
ClientnormalizeName(value) = value.trim().toLowerCase(); normalizeChainNameLabel additionally strips a trailing .chain
Backend, on readlinks.x is trimmed, lowercased and has a leading @ stripped; links.bio, links.site and links.prefaens are trimmed only
Backend, on writeThe value must be at most 200 characters and must not contain a :, because the signed message is colon-delimited

The site provider also strips the scheme (https://example.com/blog is stored as example.com/blog) and rejects a port.

The contract stores the value string verbatim, so whatever the backend normalized is what is on chain.

Worked examples

Link a .chain name. Claim the AENS name first, because the identity layer does not claim it for you. Then run the claim-sign-submit flow at /api/address-links/prefered-aens-name/. The signed message puts the name in the subject slot rather than your address:

link:hero.chain:prefaens:hero.chain:0

The backend calls link_principal, and the contract verifies with AENSv2.lookup that you own the name. PRINCIPAL_NOT_FOUND means the name is not registered; PRINCIPAL_MISMATCH means you do not own it.

Link an X handle. Complete the OAuth leg, sign link:ak_…:x:yourhandle:<nonce>, and submit with the verification token. The handle is stored lowercased.

Change which name shows. Link a different .chain name, or unlink prefaens to fall back to the middleware-resolved name.

Remove a name. unclaim then unclaim/submit on the matching provider path. Only your own signature is required, with no OAuth or AENS check.

Related