Quickstart
Register an agent, exchange its credential for a token, and make your first authenticated call.
In this guide you register an agent with the default credential (level 1), get an access token from the identity provider, and call GET /api/v1/agents/me.
Before you begin
- You need an AY account. Sign up at aytwin.ai and finish onboarding. Your account owns the agent.
- You need the AY app paired with your account. Pairing registers your identity (DID) with the key on your phone, and the agent's passport names that DID as its owner. Without it, registration answers
409 IDENTITY_NOT_REGISTERED, and the web app asks you to pair your phone first instead of showing the form. - You need your account's OIDC token. The web app sends it on every request; for scripts, copy it from the browser session or use the web app to register and download the credential file instead of calling the API directly.
- Install
curl, or Node.js 22 with thejosepackage.
Registering through the web app at /agent/register performs step 1 for you and offers the credential as a file download. Steps 2 and 3 are the same.
Register the agent
Send the agent's name and, optionally, the endpoint where it listens for A2A tasks.
curl -X POST https://api.aytwin.ai/api/v1/agents \
-H "Authorization: Bearer $AY_OIDC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Agent",
"service_endpoint": "https://agent.example.com/a2a",
"auth_method": "key_json"
}'The response contains the credential exactly once. Store credentials.key_json in your secret manager now; you cannot fetch it again. If you lose it, rotate the credential.
{
"id": "0a1b2c3d-…",
"did": "did:webvh:QmZP…:api.aytwin.ai:agents:0a1b2c3d-…",
"status": "active",
"key_custody": "platform",
"auth_method": "key_json",
"credentials": {
"type": "key_json",
"key_json": { "type": "serviceaccount", "keyId": "…", "key": "-----BEGIN RSA PRIVATE KEY-----…", "userId": "…" },
"token_endpoint": "https://auth.machine-identity.com/oauth/v2/token",
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"scope": "openid urn:zitadel:iam:org:project:id:392496763820310530:aud"
},
"agent_card": "https://api.aytwin.ai/agents/0a1b2c3d-…/.well-known/agent.json",
"passport_vc": "eyJ…"
}Get an access token
Build a JWT assertion from the key file and exchange it at the token endpoint. The assertion is signed with RS256; iss and sub are the userId, aud is the identity provider, and exp is at most one hour ahead.
import { SignJWT, importPKCS8 } from 'jose';
import { readFile } from 'node:fs/promises';
const cred = JSON.parse(await readFile('ay-agent-key.json', 'utf8'));
const { keyId, key, userId } = cred.key_json;
const issuer = 'https://auth.machine-identity.com';
const assertion = await new SignJWT({})
.setProtectedHeader({ alg: 'RS256', kid: keyId })
.setIssuer(userId)
.setSubject(userId)
.setAudience(issuer)
.setIssuedAt()
.setExpirationTime('1h')
.sign(await importPKCS8(key, 'RS256'));
const res = await fetch(cred.token_endpoint, {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ grant_type: cred.grant_type, scope: cred.scope, assertion }),
});
const { access_token, expires_in } = await res.json();The token is a JWT whose aud contains the AY agents project. Its lifetime is set by the identity provider; read expires_in and refresh before it ends.
Call the API as the agent
curl https://api.aytwin.ai/api/v1/agents/me \
-H "Authorization: Bearer $AY_AGENT_ACCESS_TOKEN"A 200 response returns the agent's profile, including did, key_custody, attestation_status, guardrail configuration, and today's spend. Send exactly one authentication method per request; see Authenticate requests for the rules and error codes.
Next steps
- Publish where your agent listens: set
service_endpointand check the Agent Card. - Accept calls from twins: implement the checks in Verify requests from twins.
- Keep the key yourself: switch to level 3 with Bring your own key.