Documentation

From a Tap to a Profile

Turn a card uid or a tapped link into a holder you can display, with no token.

This guide turns what a card tap gives you, a uid or a URL, into something you can render. Every call is unauthenticated: no API key, no account, no user consent. That makes it usable from a browser, a kiosk, or a link preview.

1. Work out what you have

What you haveDo this
A card uidFetch the document directly at /c/{uid}/json
A card or profile URLFetch the page and follow its discovery link
A URL on some other hostThe same: discovery works the same way

The direct fetch is one hop instead of two. Use it whenever you have the uid.

curl -H "Accept: application/vnd.davi.card+json" \
  "https://davi.social/c/CARD_UID/json"

2. Discover from a URL

When you have only a URL, fetch it and look for the alternate link:

const ACCEPT = "text/html, application/vnd.davi.card+json, application/json";
const res = await fetch(url, { headers: { Accept: ACCEPT } });

// A card document may come straight back — check before parsing as HTML.
const type = res.headers.get("content-type") ?? "";
if (type.includes("+json") || type.includes("application/json")) {
  return await res.json();
}

const html = await res.text();
const tag = html.match(
  /<link[^>]+type=["']application\/vnd\.davi\.card\+json["'][^>]*>/i,
);
const href = tag?.[0].match(/href=["']([^"']+)["']/i)?.[1];
if (!href) return null;                    // nothing to display
return await (await fetch(new URL(href, res.url))).json();

A profile URL advertises the owner's primary card. If they have none, the link is absent and there is no card document to reach. That is a valid state, not an error.

3. Check the version before the fields

const m = /^(\d+)\.(\d+)$/.exec(card?.version ?? "");
if (!m) return null;                       // not a card document
if (Number(m[1]) !== 1) return null;       // a major you don't read

Accept any minor version. Code written against 1.0 can read a 1.7 document.

4. Decide what to show

Branch on status, not on which fields are present:

statusWhat it meansReasonable UI
claimedSomebody holds this cardShow the holder
unclaimedNobody holds it yetOffer to claim it
frozenHeld, but withheld as a credentialShow the holder, don't act on the card

A frozen card still carries its holder's details, so owner_name being present does not mean the card is live.

Render owner_name as the display name. It is the one name field that applies to every holder. owner_given_name and owner_family_name refine it for people and are absent for organizations, so a UI built only on those breaks on an organization-held card.

5. Handle the empty cases

404 + application/problem+json   → no card to show
no discovery link on a page      → no card to show
version major you don't read     → no card to show

A 404 carries an RFC 9457 problem document. The same body comes back for a card that does not exist and for one that exists but has nothing to show. Treat both as "nothing to display".

Next

The document never carries an email address or a phone number, whoever reads it. If your product signs people in, authenticated calls add what it leaves out: