Overview

API keys authenticate requests to the Verification API's POST /verify endpoint. Each key is scoped to a single AI agent and requires an EIP-712 signature from the agent owner to create, rotate, or revoke.

Key Format: kya_live_v1_[a-f0-9]{64}

Warning: API keys are shown only once at creation. Store them securely. If lost, you must rotate to generate a new key.

Key Management Endpoints

All mutating key endpoints (generate, rotate, revoke) require EIP-712 signatures and are rate limited to 10 requests per hour per IP + agentId combination.

POST /keys/generate

Create a new API key for an agent.

Request:

POST https://api.kyachain.xyz/keys/generate
Content-Type: application/json

{
  "agentId": "42",
  "signature": "0x<130-hex-chars>",
  "nonce": "1",
  "timestamp": 1707408000,
  "declaredEndpoint": "https://myapp.com"  // optional
}
FieldTypeRequiredDescription
agentIdstringYesAgent ID as numeric string
signaturestringYesEIP-712 signature from agent owner
noncestringYesRandom nonce (prevents replay)
timestampnumberYesCurrent Unix timestamp
declaredEndpointstringNoYour application's endpoint URL (used for mismatch detection in /verify)

Response (201 Created):

{
  "apiKey": "kya_live_v1_abc123...",
  "agentId": "42",
  "message": "API key generated successfully"
}

Errors:

  • 409 KEY_ALREADY_EXISTS: Agent already has an API key (use POST /keys/rotate instead)
  • 401 SIGNATURE_INVALID: Signature doesn't match agent owner
  • 404 AGENT_NOT_FOUND: Agent ID doesn't exist
  • 429 RATE_LIMITED: Exceeded 10 requests/hour limit

POST /keys/rotate

Rotate an existing API key (generates new key, invalidates old one immediately).

Request:

POST https://api.kyachain.xyz/keys/rotate
Content-Type: application/json

{
  "agentId": "42",
  "signature": "0x<130-hex-chars>",
  "nonce": "2",
  "timestamp": 1707408100,
  "declaredEndpoint": "https://myapp.com"  // optional
}

Same fields as POST /keys/generate.

Response (200 OK):

{
  "apiKey": "kya_live_v1_def456...",
  "agentId": "42",
  "message": "API key rotated successfully"
}

Note: The old API key is immediately invalidated. Update your application with the new key before the old one expires.

Errors:

Same as POST /keys/generate (except KEY_ALREADY_EXISTS doesn't apply).

DELETE /keys

Revoke an API key permanently.

Request:

DELETE https://api.kyachain.xyz/keys
Content-Type: application/json

{
  "agentId": "42",
  "signature": "0x<130-hex-chars>",
  "nonce": "3",
  "timestamp": 1707408200
}
FieldTypeRequiredDescription
agentIdstringYesAgent ID as numeric string
signaturestringYesEIP-712 signature from agent owner
noncestringYesRandom nonce
timestampnumberYesCurrent Unix timestamp

Response (200 OK):

{
  "success": true,
  "agentId": "42"
}

Note: This endpoint is idempotent. Revoking a non-existent key returns 200.

Errors:

  • 401 SIGNATURE_INVALID: Signature doesn't match agent owner
  • 404 AGENT_NOT_FOUND: Agent ID doesn't exist
  • 429 RATE_LIMITED: Exceeded 10 requests/hour limit

GET /keys/status

Check if an agent has an active API key (public endpoint, no authentication required).

Request:

GET https://api.kyachain.xyz/keys/status?agentId=42
ParameterTypeRequiredDescription
agentIdstringYesAgent ID as numeric string

Response (200 OK):

{
  "hasKey": true,
  "agentId": "42"
}

EIP-712 Signature Format

All mutating key endpoints use the same EIP-712 signature format as the /verify/signature endpoint. The signature must be created by the wallet that owns the agent NFT.

Generate signature for key management
import { createWalletClient, http } from 'viem';
import { kyaChain } from './chains';

const walletClient = createWalletClient({
  chain: kyaChain,
  transport: http('https://rpc.kyachain.xyz'),
});

const agentId = 42n;
const nonce = BigInt(Math.floor(Math.random() * 1e18));
const timestamp = BigInt(Math.floor(Date.now() / 1000));

const signature = await walletClient.signTypedData({
  domain: {
    name: 'KYA Verification',
    version: '1',
    chainId: 8004,
    verifyingContract: '0xA1393CB409E2fE5573C0840189622aA0e33947b2', // IdentityRegistry
  },
  types: {
    Verification: [
      { name: 'agentId', type: 'uint256' },
      { name: 'nonce', type: 'uint256' },
      { name: 'timestamp', type: 'uint256' },
    ],
  },
  primaryType: 'Verification',
  message: { agentId, nonce, timestamp },
});

// Use signature in key management request
const response = await fetch('https://api.kyachain.xyz/keys/generate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    agentId: agentId.toString(),
    signature,
    nonce: nonce.toString(),
    timestamp: Number(timestamp),
    declaredEndpoint: 'https://myapp.com',
  }),
});

const { apiKey } = await response.json();
console.log('New API key:', apiKey);

Complete Key Lifecycle Example

Full API key lifecycle workflow
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { kyaChain } from './chains';

const account = privateKeyToAccount('0x...');
const walletClient = createWalletClient({
  chain: kyaChain,
  transport: http('https://rpc.kyachain.xyz'),
  account,
});

const agentId = 42n;
const API_BASE = 'https://api.kyachain.xyz';

// Helper to create signature
async function createSignature() {
  const nonce = BigInt(Math.floor(Math.random() * 1e18));
  const timestamp = BigInt(Math.floor(Date.now() / 1000));

  const signature = await walletClient.signTypedData({
    domain: {
      name: 'KYA Verification',
      version: '1',
      chainId: 8004,
      verifyingContract: '0xA1393CB409E2fE5573C0840189622aA0e33947b2',
    },
    types: {
      Verification: [
        { name: 'agentId', type: 'uint256' },
        { name: 'nonce', type: 'uint256' },
        { name: 'timestamp', type: 'uint256' },
      ],
    },
    primaryType: 'Verification',
    message: { agentId, nonce, timestamp },
  });

  return { signature, nonce, timestamp };
}

// 1. Check if agent has a key
const statusRes = await fetch(`${API_BASE}/keys/status?agentId=${agentId}`);
const { hasKey } = await statusRes.json();

if (!hasKey) {
  // 2. Generate new key
  const { signature, nonce, timestamp } = await createSignature();
  const generateRes = await fetch(`${API_BASE}/keys/generate`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      agentId: agentId.toString(),
      signature,
      nonce: nonce.toString(),
      timestamp: Number(timestamp),
      declaredEndpoint: 'https://myapp.com',
    }),
  });

  const { apiKey } = await generateRes.json();
  console.log('Generated API key:', apiKey);

  // Store securely (e.g., environment variable, secret manager)
  process.env.KYA_API_KEY = apiKey;
}

// 3. Use the key for verification
const verifyRes = await fetch(`${API_BASE}/verify`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    apiKey: process.env.KYA_API_KEY,
    agentId: agentId.toString(),
  }),
});

const verifyData = await verifyRes.json();
console.log('Agent verified:', verifyData.valid);

// 4. Rotate key (e.g., for security incident or scheduled rotation)
const { signature: rotateSig, nonce: rotateNonce, timestamp: rotateTs } = await createSignature();
const rotateRes = await fetch(`${API_BASE}/keys/rotate`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    agentId: agentId.toString(),
    signature: rotateSig,
    nonce: rotateNonce.toString(),
    timestamp: Number(rotateTs),
  }),
});

const { apiKey: newKey } = await rotateRes.json();
console.log('Rotated to new key:', newKey);
process.env.KYA_API_KEY = newKey;

// 5. Revoke key (e.g., decommissioning agent)
const { signature: revokeSig, nonce: revokeNonce, timestamp: revokeTs } = await createSignature();
const revokeRes = await fetch(`${API_BASE}/keys`, {
  method: 'DELETE',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    agentId: agentId.toString(),
    signature: revokeSig,
    nonce: revokeNonce.toString(),
    timestamp: Number(revokeTs),
  }),
});

const { success } = await revokeRes.json();
console.log('Key revoked:', success);

Security Best Practices

  1. Store keys securely: Never commit API keys to version control. Use environment variables or secret management services.

  2. Rotate regularly: Implement periodic key rotation (e.g., every 90 days) to limit exposure window.

  3. Revoke on compromise: If a key is leaked, immediately call POST /keys/rotate to invalidate it.

  4. Use declaredEndpoint: Set declaredEndpoint during key generation to enable endpoint mismatch detection in /verify responses.

  5. Monitor rate limits: Track X-RateLimit-Remaining headers and implement backoff strategies.

  6. Validate signatures offline: For critical applications, verify EIP-712 signatures client-side before sending to the API.

On this page