Tutorial · Link an agent to an AID
End-to-end walkthrough: bind a Masumi agent to a Veridian-controlled KERI AID and issue it an Agent Verification credential.
This tutorial takes an agent with nothing attached and ends with a cryptographically verifiable identity on the Masumi network. By the end you'll have:
- An AID the user owns (through their Veridian wallet).
- A credential issued to that AID proving the agent is Masumi-verified.
- A service that can retrieve the credential on demand.
You'll use four SDK methods: getIssuerOobi, connectToAid, isAidConnected, and issueCredential.
Before you start
pnpm add @masumi_network/identity-sdkIf you're missing the wallet side, install the Masumi Veridian Wallet or use the full Cardano Foundation reference wallet — both can produce OOBIs and hold ACDCs.
Step 1 · Instantiate the client
import {
MasumiIdentity,
MASUMI_IDENTITY_ENDPOINTS,
} from "@masumi_network/identity-sdk";
const identity = new MasumiIdentity(MASUMI_IDENTITY_ENDPOINTS.production);Step 2 · Share the issuer's OOBI with the wallet
The handshake is two-sided. The credential server has to know how to reach the wallet's AID, and the wallet has to know how to reach the credential server. Start by fetching the credential server's OOBI and surfacing it:
const issuerOobi = await identity.getIssuerOobi();
// Render as a QR code, deep link, or plain text depending on your UX.
console.log("Show this to the wallet:", issuerOobi);A user scanning this in their Veridian wallet resolves the OOBI — now the wallet knows the credential server is a trusted contact.
Step 3 · Receive the wallet's OOBI
The wallet responds by producing an OOBI for the AID it controls. How this arrives is your transport choice — a webhook from Veridian, a pasted string in a form, a deep-link callback. The shape is always the same:
https://keria.masumi-identity.xyz/oobi/EHpH79tPZoSl7VJZ7xMi3JWF4rH9wZ2ntGvKABd9N14zStep 4 · Resolve the wallet's OOBI at the credential server
Tell the credential server about the new AID:
const walletOobi = "https://keria.masumi-identity.xyz/oobi/EHpH79tPZoSl...";
const result = await identity.connectToAid(walletOobi);
if (!result.success) {
throw new Error(`OOBI resolution failed: ${result.data}`);
}After this call succeeds the server knows how to reach the AID and can issue credentials to it.
Step 5 · Confirm the link with an idempotency check
If this flow can replay (users reconnect, retries happen), gate issuance behind an existence check so you never redo the handshake:
const aid = extractAidFromOobi(walletOobi); // your own helper
if (!(await identity.isAidConnected(aid))) {
await identity.connectToAid(walletOobi);
}Step 6 · Issue the credential
Now bind the agent to the AID by issuing a credential. Pick the schema SAID for whatever credential you're issuing (in this example, the Masumi "Agent Verification" schema):
const issuance = await identity.issueCredential({
schemaSaid: process.env.AGENT_VERIFICATION_SCHEMA_SAID!,
aid,
attributes: {
agentId: "trip-planner-v2",
name: "Trip Planner",
url: "https://agents.example.com/trip-planner",
},
});
if (!issuance.success) {
throw new Error(`Issuance failed: ${issuance.data}`);
}The credential is now chained into the status registry and lives in the wallet. It can be presented to any counterparty in the future.
Step 7 · Verify the credential arrived
Fetch credentials for the AID and find the one you just issued:
const creds = await identity.getCredentialsForAid(aid);
const agentVerification = identity.findCredentialBySchema(
creds,
process.env.AGENT_VERIFICATION_SCHEMA_SAID!,
);
if (!agentVerification) {
throw new Error("Credential not yet visible on the AID");
}
const view = identity.formatCredential(agentVerification);
console.log("Issued", view.credentialType, "to", view.issueeAid);Complete example
import {
MasumiIdentity,
MASUMI_IDENTITY_ENDPOINTS,
} from "@masumi_network/identity-sdk";
const identity = new MasumiIdentity(MASUMI_IDENTITY_ENDPOINTS.production);
const AGENT_VERIFICATION_SCHEMA_SAID =
process.env.AGENT_VERIFICATION_SCHEMA_SAID!;
export async function linkAgent(params: {
walletOobi: string;
aid: string;
agentId: string;
agentName: string;
}) {
const { walletOobi, aid, agentId, agentName } = params;
// Idempotent handshake.
if (!(await identity.isAidConnected(aid))) {
const r = await identity.connectToAid(walletOobi);
if (!r.success) throw new Error(`OOBI resolution failed: ${r.data}`);
}
// Avoid re-issuing an existing credential.
const existing = await identity.getCredentialsForAid(aid);
if (identity.findCredentialBySchema(existing, AGENT_VERIFICATION_SCHEMA_SAID)) {
return { status: "already-linked" as const };
}
const issuance = await identity.issueCredential({
schemaSaid: AGENT_VERIFICATION_SCHEMA_SAID,
aid,
attributes: { agentId, name: agentName },
});
if (!issuance.success) throw new Error(`Issuance failed: ${issuance.data}`);
return { status: "linked" as const, credentialSaid: issuance.data };
}