Royall Connect — connect a person's vault to your product and ask permission before you use their likeness. Read the partner integration guide →
The authorization API for human likeness
Ask Royall whether your platform may use a person's voice or face — right before you
generate — and get back ALLOW or BLOCK plus a signed, offline-verifiable clearance.
One call: POST /v1/authorize.
RYL-…). They set which uses they'll allow. Your platform calls
/v1/authorize with that Asset ID + your intended use; Herald reads the standing
consent and answers without running a model or calling anything else, so it is fast enough to sit in front of a generation. On ALLOW you also get a
grant — a portable proof-of-clearance you can embed in your output and anyone can verify
against Royall's public key. There is no biometric matching on this path, so it never inherits
model-inference latency.
Products
Five partner surfaces, one platform. Herald is documented in full on this page; Royall Connect, the detections feed, voice matching and the song catalogue are specified in the partner integration guide.
Royall Herald live
The authorization API. One call — POST /v1/authorize with an
Asset ID and your intended use; you get back ALLOW or BLOCK plus an Ed25519-signed grant
anyone can verify offline.
Royall Connect early access
A hosted consent handoff: you send your user to Royall, they consent and record their likeness there, and you get a durable connection you can check on every use.
Detections feed early access
Powered by Sentinel and delivered through Connect: a feed of things we spotted that look
like the person — each carrying a link, a platform, a date, a confidence band, and,
once you have registered a catalogue, a work block naming which song we
think it renders. No verdict on the person: matches, never accusations.
Voice matching by agreement
Submit a recording and ask whether it is the voice of the person behind an Asset ID.
It uses your ordinary rh_live_ Herald key on POST /v1/match —
not your Connect keys — but it is enabled per agreement and only for assets your
company licenses. It has its own, much tighter allowance than the rest of the API — ask us for
your number — and is never available to a sandbox key.
Song catalogue early access
Register the songs you administer, with a reference recording for each. We then match a detection by sound rather than by the uploader's title, and tell you which of your songs it renders — which is what lets you file rather than only demand removal.
Sentinel
Sentinel is Royall's monitoring layer. A creator captures their likeness — voice, face, body — in their Royall vault, and Sentinel watches platforms for it from there. Nothing outward happens on a match by default: detections land in the creator's own inbox. Nothing is ever filed on a match alone — a filing is either made by the creator, or sent automatically only where that creator has signed a standing sworn authorization in advance.
What a partner sees of Sentinel. One surface: the
detections feed, delivered through Royall Connect
and specified in §8 of the integration guide. It is a
list of things we spotted that look like the person — it is not a list of things done
without their permission. Each detection carries a link, a platform, a date and a
confidence band, and no verdict; a status field appears only if the creator
chose to share it. You may be shown your own authorized output.
The feed is in early access and off by default: enabling it is a deliberate act on your partner account, and the person must separately agree to it, per connection, on the consent screen. An empty feed is therefore not a clean bill of health — read §8 before you build on it.
Quickstart
The same call in every language. Pick your language once (left) and every snippet on this page follows.
# set your key once — never inline a secret in a shell history export ROYALL_HERALD_KEY="rh_live_..." curl -sX POST https://vault.royall.ai/v1/authorize \ -H "Authorization: Bearer $ROYALL_HERALD_KEY" \ -H "Content-Type: application/json" \ -d '{"asset_code":"RYL-V-2026-DEM0V01C-8","use_case":"ads","depth":"synthesis"}' # -> {"status":"ALLOW","tier":"commercial_synthesis","grant_id":"grant_…","signed_token":"…", ...} # -> or {"status":"BLOCK","reason":"block:no_offer:commercial_synthesis/ads"} (BLOCK is HTTP 200, not an error)
# Royall Python SDK — not on a package registry yet; we send you the source (see "SDKs" below). The API is plain HTTP; an SDK is optional. from royall_herald import HeraldClient herald = HeraldClient(api_key="rh_live_...") # base_url defaults to vault.royall.ai d = herald.authorize("RYL-V-2026-DEM0V01C-8", use_case="ads", depth="synthesis") if d.allowed: # BLOCK is a normal result, not an exception proof = d.signed_token else: print("blocked:", d.reason) assert d.verify(herald.fetch_public_key()) # offline Ed25519 — zero callbacks
// Royall Node SDK — not on a package registry yet; we send you the source (see "SDKs" below). Zero dependencies; Web Crypto for verify. import { HeraldClient } from "royall-herald"; const herald = new HeraldClient({ apiKey: "rh_live_..." }); const d = await herald.authorize("RYL-V-2026-DEM0V01C-8", "ads", { depth: "synthesis" }); if (d.allowed) { const proof = d.signedToken; } else { console.log("blocked:", d.reason); } const ok = await d.verify(await herald.fetchPublicKeyRaw()); // offline
// Royall Go SDK — not on a package registry yet; we send you the source (see "SDKs" below). Package: herald. Standard library only. c, _ := herald.NewClient(herald.Options{APIKey: "rh_live_..."}) d, err := c.Authorize(ctx, "RYL-V-2026-DEM0V01C-8", "ads", herald.AuthorizeOptions{Depth: "synthesis"}) if err != nil { /* auth(401) / rate-limit(429) / server(5xx) / network */ } if d.Allowed() { // BLOCK is not an error proof := d.SignedToken } else { fmt.Println("blocked:", d.Reason) } pub, _ := c.FetchPublicKeyRaw(ctx) ok := d.Verify(pub) // offline crypto/ed25519
No key yet? Everything below works against the sandbox with an
rh_test_ key — no live data required.
Authentication
Every request carries your Herald key as a bearer token:
Two key kinds
rh_live_…— production. Decides against real registered assets + licenses.rh_test_…— sandbox. Runs the real decision logic against a synthetic, fully-licensed asset. Never touches live data.
Keys are issued by your Royall admin. The secret is shown once — store it server-side.
Base URL
| All requests | https://vault.royall.ai |
One base URL for everything. To test without touching live data, use an
rh_test_ sandbox key against this same URL — same endpoint,
synthetic asset.
A missing/malformed/invalid key returns
401 with a detail of missing_bearer_token,
invalid_key_format, or invalid_or_revoked_key.
Sandbox
Ask your Royall contact for an rh_test_ key to exercise the endpoint and the SDKs
without a live asset, creator, or license. A sandbox key runs the real decision against a
synthetic, fully-licensed asset, so your ALLOW/BLOCK handling exercises the real decision logic.
Every response carries "sandbox": true. The asset_code you send is
ignored — the synthetic menu is fixed:
A sandbox grant is deliberately NOT interchangeable with a production one. This is the one place the sandbox is not a faithful mirror, and it is intentional:
- The signed payload carries
"env": "sandbox". It is inside the signature, so it survives stripping the HTTP envelope. - It is signed with a separate key (its
kidstartssbx-), published atGET /api/royall-sandbox-pubkey. A verifier holding only/api/royall-pubkeycannot verify it — not "will not", cannot. - Every official SDK refuses sandbox grants by default. Accepting one takes an explicit
opt-in and the sandbox key:
allow_sandbox/allowSandbox/AllowSandbox.
So: do not treat a sandbox grant as clearance, and never add the sandbox key to a production verifier. A test key must not be able to produce something a production system accepts as consent — which is exactly what this separation prevents.
| use_case | depth | result |
|---|---|---|
ads | synthesis | ALLOW commercial_synthesis + signed grant |
ads / audiobook / games | inference | ALLOW commercial_inference |
games | synthesis | BLOCK block:no_offer:commercial_synthesis/games |
any, with abuse_category set to one of the five floor categories | any | BLOCK block:universal_floor:…Any other abuse_category value is not a floor hit — the synthetic asset carries no creator exclusions, so it will ALLOW. |
# the demo Asset ID is optional — the decision always uses the synthetic menu export ROYALL_HERALD_KEY="rh_test_..." curl -sX POST https://vault.royall.ai/v1/authorize \ -H "Authorization: Bearer $ROYALL_HERALD_KEY" -H "Content-Type: application/json" \ -d '{"asset_code":"RYL-V-2026-SANDB0X0-N","use_case":"ads","depth":"synthesis"}' # -> {"status":"ALLOW","tier":"commercial_synthesis","sandbox":true,"signed_token":"…", ...}
Consent tiers
What a creator can authorize, from most-protected to most-permissive. Your use_case +
depth resolve to exactly one tier; if the creator offered it (and your company holds any
required license) you get ALLOW.
| tier | depth | meaning | |
|---|---|---|---|
protected | T0 | — | The default. Registry + enforcement only — nothing is authorized. |
fan_noncommercial | T1 | inference | Free, non-commercial use of the creator’s recorded likeness samples, with attribution. Revocable. No license required. |
commercial_inference | T2 | inference | Paid commercial use of the creator’s recorded likeness samples or models trained on them. No new content generated. Requires a license. |
commercial_synthesis | T3 | synthesis | Paid commercial generation of new content in the likeness (TTS / image). Requires a license. |
commercial_training | T4 gated | training | Persistent model training/fine-tune. Per-deal admin approval only — a creator can never self-offer it. |
Creators may also attach exclusions — categories they won't allow regardless of tier:
alcohol, gambling, tobacco, cannabis, adult,
political, religious, competitors. A hit returns
block:creator_exclusion:<category>.
The universal floor
Five categories are never authorizable — no tier, payment, or creator
setting overrides them. They're enforced in code, checked against both fields of your request that can carry a
category — abuse_category and use_case.
Declaring one via abuse_category (or a matching use-case) returns
block:universal_floor:<category>.
illegal | Any unlawful use. |
ncii | Non-consensual intimate / sexual imagery. |
fraud_impersonation | Scams, fake authority, deception. |
defamation_harassment | Defamation or targeted harassment. |
political_manipulation | Falsely-attributed endorsements / deepfake misinformation. |
The signed grant
On ALLOW, signed_token is a portable, Ed25519-signed proof-of-clearance binding the
asset, tier, use, your company, a grant_id, and a short expiry (~15 min, ≤ 1 hour). Embed it
in your output (e.g. as a C2PA assertion). Anyone can verify it offline against Royall's public key —
every SDK's verify() does this with zero calls back to Royall, using a canonical-bytes
derivation that's byte-identical across Python, Node, and Go.
Returns {alg:"ed25519", kid, public_key_pem, public_key_b64, signed_endpoints, c2pa_cert_chain_url}.
Re-authorize at generation time — a short TTL means a withdrawn grant flips to BLOCK by architecture, not by polling.
The separate key for sandbox grants. Returns
{alg:"ed25519", kid, public_key_b64, env:"sandbox", warning}; the kid is
prefixed sbx-. For integration testing only — never load this key into a production
verifier. Its whole purpose is that production cannot accept what it signs.
What verify() checks
In this order, failing closed at every step: signature present → the signed
env selects which key must have signed it (an unrecognised or missing
env is rejected, not assumed) → signature → principal parses → version == 4 (exact — reject anything else) → identity_verified is a real boolean → expiry → status == "ALLOW" →
and finally, a sandbox grant is refused unless you explicitly opted in. The signature is checked
before the environment is acted on, so a forged env cannot route around it.
What verify() does not check
A passing verify() proves Royall issued the grant. It does not prove the grant
was issued to you, for this asset, or for this operation. Royall signs with one production
key, so a valid grant belonging to another platform, another Asset ID or another use case passes
every check above. verify() takes no caller expectation — after it passes, compare
the signed claims to the values you expected:
| claim | compare against |
|---|---|
principal | your own account's identity — a flat string whose shape
depends on your credential (company:<id> for Herald keys,
partner:prt_… for Connect partners). It belongs in your configuration; deriving
it from the token you are checking asserts nothing. |
asset_code | the canonical Asset ID of the job you are about to run. |
tier and use_case | the operation you are actually performing. |
identity_verified | your own policy, if you require verified identity. |
Skipping this comparison makes the token a proof that somebody was authorized for something.
Resolver & badge
A Royall Asset ID is a public, printable handle (like a phone number). The code is public; what it resolves to is consent-gated.
Machine JSON. 422 on a mistyped code; otherwise HTTP 200 with a status of
active, revoked, deleted, or unknown. Anonymous
responses carry no PII — id, status, kind, issued, verified_human,
assertion for an active asset, plus demo on the documentation
demo asset; a revoked tombstone carries since); a valid
Herald key additionally sees its own rights + authorized_for for that asset.
A shareable “Verified Human by Royall” badge page — a human-readable view of the same resolution. See it cleared → or Unverified →.
Asset IDs look like RYL-V-2026-7K3F9Q2X-N — namespace · kind (V/F/B) · year ·
random designation · a Luhn-mod-32 check character. Case-insensitive and hyphen-tolerant.
Embed the badge
Drop a live “Verified Human by Royall” badge into your own product — it reflects the current
consent status and links to the full /verify/{code} page. The badge is a
cue, not proof: the authoritative check is the resolver + the signed grant, so the badge
always links back to /verify.
Plain HTML — no JavaScript
Works anywhere an image does: a web page, a README, an email.
<a href="https://vault.royall.ai/verify/RYL-V-2026-7K3F9Q2X-N">
<img src="https://vault.royall.ai/badge/RYL-V-2026-7K3F9Q2X-N.svg"
alt="Verify with Royall" height="28">
</a>Web component — live, one tag
A custom element that renders the badge (Shadow-DOM isolated) and links to
/verify. Load the script once, then use <royall-badge> anywhere.
<script src="https://vault.royall.ai/badge.js" async></script> <royall-badge code="RYL-V-2026-7K3F9Q2X-N"></royall-badge>
The SVG is short-cached (status is live). Endpoints:
GET /badge/{code}.svg (the image) and GET /badge.js (the component).
Both are public and need no key. Embed them as an image and a script tag — they do not
send CORS headers, so a cross-origin fetch() is blocked (and is not how you
would use them).
Errors & rate limits
- ALLOW / BLOCK are both
200. A BLOCK is a decision, not a failure — branch onstatus. 401— bad/missing key (missing_bearer_token/invalid_key_format/invalid_or_revoked_key).422— a mistyped Asset ID at/id/{code}({"error":"invalid_checksum"}).429— over quota (rate_limit_exceeded)./v1/authorizeis limited per key over a rolling 24-hour window. Allowances are agreed per partner rather than published, so ask us for yours. The/v1/connect/*endpoints are charged to a per-partner daily budget, not to an IP. Respect theRetry-Afterheader rather than hard-coding a delay.5xx— transient; retry with backoff. The decision path holds no ML, so it stays fast.
SDKs
No SDK is required. Herald is one JSON endpoint over HTTPS — the curl tab above is the whole integration, and grant verification is standard Ed25519 you can do with any crypto library.
If you'd rather not write it yourself, we maintain three SDKs. Each gives you a typed
authorize() returning a Decision (BLOCK is not an error) plus offline grant
verify(). Canonical grant bytes are byte-identical across all three, so a grant signed for
one verifies in the others.
| language | package | notes |
|---|---|---|
| Python | royall_herald | 3.9+. Offline verify needs the cryptography extra. |
| Node / TypeScript | royall-herald | Zero dependencies. Node 18+, browsers, Deno, Bun, Workers. |
| Go | herald | Standard library only — no external modules. |
These are not yet on public package registries. Ask your Royall contact and we'll send you the SDK for your language, or integrate directly against the HTTP endpoint above — both are fully supported.
Machine-readable source:
/developers/connect.md.
Questions: support@royall.ai.