Skip to content

For the complete documentation index, see llms.txt. The full corpus is available at llms-full.txt. This page is also available as Markdown by appending .md to the URL.

Build a /verify page

A /verify page is a page on your own site that lists every account that is really you, so people can tell you from an impersonator. It is a slash page, like /now and /uses. Your RealHandles profile already holds that list, signed by your key, so your page can read it from there instead of keeping a second copy.

  • Every account as a link with rel="me". That attribute is how Mastodon, RealHandles, and other verifiers recognize a link as yours. Put the links in the page’s HTML: those tools read the HTML and do not run JavaScript, so a list that a script adds after the page loads is invisible to them.
  • A link to your RealHandles profile, also with rel="me", so anyone can check the list without trusting your site.

That is the whole convention. Everything below is optional.

Your profile is public at this address, with no key or sign-in, from a browser or a build:

https://realhandles.com/api/identity/<handle>

The fields a /verify page uses:

FieldWhat it holds
accounts.verifiedAccounts RealHandles has verified.
accounts.claimedAccounts you listed that are not verified.
disavowedAccounts, and whole platforms, you have signed as not you.
profileThe address of your RealHandles profile.

Each account has platform, handle, url, label, and kind. label is the platform’s display name, such as Product Hunt, and kind is account, domain, or wallet. Each disavowal has kind (account or platform), platform, and label, plus handle and url when it names one account.

Fetch your profile when your site builds, and write the links into the HTML. In Astro, for example:

---
const res = await fetch('https://realhandles.com/api/identity/yourhandle');
if (!res.ok) throw new Error(`RealHandles returned ${res.status}`);
const { accounts, profile } = await res.json();
const all = [...accounts.verified, ...accounts.claimed].filter((a) => a.kind !== 'wallet' && a.url);
---
<ul>
{all.map((a) => <li><a rel="me" href={a.url}>{a.label}</a></li>)}
<li><a rel="me" href={profile}>RealHandles</a></li>
</ul>

Any static site generator works the same way. Fail the build when the request fails, as above, rather than publish an empty list: your host keeps serving the last good deploy. An account you add on RealHandles appears on your page the next time the site builds.

To mark the accounts RealHandles has verified, ask again in the browser on every visit, and mark only links whose address is in accounts.verified. A mark written in at build time can outlive the verification it shows. If the request fails, draw no marks.

const key = (u) => {
const x = new URL(u, location.href);
return (x.hostname.replace(/^www\./, '') + x.pathname.replace(/\/+$/, '')).toLowerCase();
};
const res = await fetch('https://realhandles.com/api/identity/yourhandle').catch(() => null);
const verified = res?.ok ? (await res.json()).accounts.verified : [];
const urls = new Set(verified.map((a) => key(a.url)));
for (const link of document.querySelectorAll('a[rel="me"]')) {
if (urls.has(key(link.href))) link.classList.add('verified');
}

Responses are cached for up to five minutes, so checking on every visit is fine on a busy page.

  • Wallets are the accounts with kind set to wallet. Show the address from handle as text rather than as a rel="me" link, because a block explorer page is not you.
  • Disavowals are in disavowed. You can link to the account so people can recognize it, but never with rel="me", which would claim the opposite.

davidvkimball.com/verify reads its wallets and disavowals from RealHandles when the site builds, and marks verified accounts in the browser on every visit.

If you already have a /verify page, import it: paste its address into Import your profiles, on the Claim tab of the dashboard or in the app. RealHandles reads the rel="me" links and adds each account as Claimed, and you verify them from there. See Verifying an account. Once the accounts are on your profile, you can build the page from it as above.