AY Developer docs

Authenticate requests

The one-method rule, how AY picks the method, the checks that follow, and every error you can get.

Every agent route, starting with GET /api/v1/agents/me, runs through the same authentication step. Send exactly one method per request.

How AY picks the method

AY does not try methods in turn. It looks at the headers and decides once:

Request carriesMethodCustody
Signature-Input and SignatureRFC 9421 signature, tag ay-agentdeveloper
Authorization: Bearer <JWT>Zitadel access tokenplatform
Authorization: Bearer ay_agent_…Legacy API key, deprecatedexisting agents only
Signature-Input and Authorization togetherrejected: 400 ambiguous_auth
neitherrejected: 401 AGENT_AUTH_REQUIRED

What is checked for a token

Tokens are checked according to the JWT best current practice (RFC 8725):

  1. The algorithm is RS256. No other algorithm is accepted.
  2. The kid matches a key in the identity provider's JWKS.
  3. iss is https://auth.machine-identity.com.
  4. aud contains the AY agents project ID 392496763820310530.
  5. exp has not passed and nbf is not in the future, with 60 seconds of tolerance.
  6. sub is the machine user of exactly one agent, and that agent has key_custody: platform.

Any failure returns 401 token_invalid with a reason: malformed, alg, kid, expired, nbf, aud, iss, signature, sub, sub_unknown, or key_custody. If the JWKS cannot be fetched, the answer is 503 verifier_unavailable.

A user token of the AY app fails at aud. It cannot be used as an agent token, and an agent token cannot be used as a user token.

What is checked for a signature

  1. Content-Digest matches the received body.
  2. keyid names an agent DID with key_custody: developer, and the fragment is the agent's current #dev-N.
  3. The key is listed under authentication in the DID document.
  4. The signature verifies, created is at most 300 seconds old, and expires has not passed, with 60 seconds of skew.
  5. tag is ay-agent and the covered components are exactly the four of the profile.
  6. The nonce has not been seen for this keyid in the last 360 seconds. The nonce is consumed.

Any failure returns 401 signature_invalid with a reason: bad_signature, expired, digest_mismatch, replay, key_not_authorized, unresolvable, malformed, signer_type, or unknown_agent. If the nonce store is unavailable, the answer is 503 verifier_unavailable; AY never lets a signature through unchecked.

Checks that follow every method

ConditionResponse
Agent is not active401 AGENT_INACTIVE
Kill switch is on403 AGENT_HALTED
Daily limit reached403 DAILY_LIMIT_EXCEEDED
Business owner's verification is not valid403 OWNER_NOT_VERIFIED
Owner's verification cannot be checked503 verifier_unavailable

These checks are the real safeguard: a token or a key stays valid until it expires or rotates, but these checks take effect on the next request.

The deprecated API key

Agents that were created before the OAuth2 credential still have an API key. Requests that use it succeed but carry two headers:

Deprecation: @1790380800
Sunset: Fri, 03 Apr 2027 00:00:00 GMT

After the sunset date the key stops working. Move to a token by rotating the credential (POST /api/v1/agents/{id}/credentials/rotate), or register a new agent with your own key.

Error body

Every error is wrapped in the API envelope with success: false. Lower-case codes (ambiguous_auth, token_invalid, signature_invalid, verifier_unavailable) carry error_code and a reason. Upper-case codes carry only error and message.

{ "success": false, "error": "token_invalid", "error_code": "token_invalid", "message": "…", "reason": "aud" }
{ "success": false, "error": "AGENT_HALTED", "message": "Agent halted by kill switch" }

See Errors for the complete list.