AY Developer docs

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 the jose package.

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