Overview
The Verification API returns structured error responses with consistent formatting. All error responses include an HTTP status code, error type, human-readable message, optional details, and timestamp.
Error Response Format
{
"error": "Human-readable error message",
"errorType": "ERROR_TYPE_CODE",
"details": {},
"timestamp": 1707408000000
}| Field | Type | Description |
|---|---|---|
error | string | Human-readable error message |
errorType | string | Machine-readable error type from the table below |
details | object | Optional additional context (varies by error type) |
timestamp | number | Unix timestamp in milliseconds |
Note: The
detailsfield varies by error type and may contain helpful debugging information (e.g., SIGNATURE_INVALID includesagentIdandowneraddress).
Error Types
VALIDATION_ERROR
HTTP Code: 400
When It Occurs: Request body fails schema validation (missing required fields, wrong data types, invalid format).
Common Causes:
- Missing required fields in request
agentIdis not a numeric stringsignatureis not in format0x+ 130 hex charactersapiKeydoesn't match formatkya_live_v1_[a-f0-9]{64}timestampis not a number or is negative
Resolution: Check that your request matches the documented schema. Common fixes:
- Ensure
agentIdandnonceare strings, not numbers - Verify signature is exactly 65 bytes (0x + 130 hex chars)
- Use current Unix timestamp in seconds for
timestampfield
Example:
{
"error": "Invalid request body: agentId must be a numeric string",
"errorType": "VALIDATION_ERROR",
"details": {
"field": "agentId",
"received": 42
},
"timestamp": 1707408000000
}SIGNATURE_INVALID
HTTP Code: 401
When It Occurs: The provided EIP-712 signature doesn't match the registered owner of the agent NFT.
Common Causes:
- Signing with the wrong private key
- Agent ownership transferred but old owner is still signing
- Incorrect EIP-712 domain parameters (wrong chainId or verifyingContract)
- Malformed signature
Resolution: Ensure you're signing with the private key of the wallet that owns the agent NFT. Verify:
- Domain
chainIdis 8004 - Domain
verifyingContractis0xA1393CB409E2fE5573C0840189622aA0e33947b2(IdentityRegistry) - Signer address matches the result of
IdentityRegistry.ownerOf(agentId)
Example:
{
"error": "Signature does not match agent owner",
"errorType": "SIGNATURE_INVALID",
"details": {
"agentId": "42",
"owner": "0x3CDA80d87cDde4612C9b66B657d45055Fa03Ce99",
"recoveredSigner": "0x1234567890123456789012345678901234567890"
},
"timestamp": 1707408000000
}REPLAY_DETECTED
HTTP Code: 400
When It Occurs: The same nonce was reused for the same agent, or the timestamp is outside the acceptable window (too old or too far in the future).
Common Causes:
- Reusing a nonce from a previous request
- System clock is incorrect (timestamp too old or in the future)
- Retrying a failed request with the same nonce
Resolution: Generate a fresh random nonce for each request. Use the current Unix timestamp (in seconds). Nonces are tracked per-agent to prevent replay attacks.
// Generate fresh nonce
const nonce = BigInt(Math.floor(Math.random() * 1e18));
const timestamp = BigInt(Math.floor(Date.now() / 1000));Example:
{
"error": "Nonce has already been used for this agent",
"errorType": "REPLAY_DETECTED",
"details": {
"agentId": "42",
"nonce": "123456789"
},
"timestamp": 1707408000000
}AGENT_NOT_FOUND
HTTP Code: 404
When It Occurs:
The specified agentId doesn't correspond to a minted NFT in the IdentityRegistry contract.
Common Causes:
- Agent ID was never registered
- Typo in agent ID
- Agent ID is 0 (reserved value)
Resolution:
Verify the agent was successfully registered on-chain. Token IDs start at 1 (0 is reserved). Check on KYAScan or query IdentityRegistry.ownerOf(agentId) directly.
Example:
{
"error": "Agent ID 9999 not found in IdentityRegistry",
"errorType": "AGENT_NOT_FOUND",
"details": {
"agentId": "9999"
},
"timestamp": 1707408000000
}RPC_ERROR
HTTP Code: 503
When It Occurs: KYA Chain RPC node is unreachable, timed out, or returned an error when querying on-chain data.
Common Causes:
- RPC node is down or overloaded
- Network connectivity issues
- RPC rate limiting
Resolution: Retry the request after a short delay (exponential backoff recommended). If the issue persists, check KYA Chain network status at https://status.kyachain.xyz.
Example:
{
"error": "Failed to fetch on-chain data: RPC request timed out",
"errorType": "RPC_ERROR",
"details": {
"rpcUrl": "https://rpc.kyachain.xyz",
"method": "eth_call"
},
"timestamp": 1707408000000
}INTERNAL_ERROR
HTTP Code: 500
When It Occurs: Unexpected server error that doesn't fit other categories.
Common Causes:
- Database connection failure
- Unhandled exception in API code
- Redis cache unavailable
Resolution:
Retry the request. If the issue persists, report it with the timestamp value for debugging.
Example:
{
"error": "An unexpected error occurred",
"errorType": "INTERNAL_ERROR",
"timestamp": 1707408000000
}INVALID_API_KEY
HTTP Code: 401
When It Occurs:
API key is not found in the key store, has been revoked, or doesn't match the requested agentId.
Common Causes:
- API key was revoked via DELETE /keys
- API key was rotated and the old key is being used
- API key doesn't belong to the requested agent
- Typo in API key
Resolution: Generate a new key via POST /keys/generate (if no key exists) or POST /keys/rotate (if key was lost). Ensure the key matches the agent you're verifying.
Example:
{
"error": "API key not found or has been revoked",
"errorType": "INVALID_API_KEY",
"details": {
"agentId": "42"
},
"timestamp": 1707408000000
}KEY_ALREADY_EXISTS
HTTP Code: 409
When It Occurs: Attempted to generate an API key for an agent that already has one.
Common Causes:
- Calling POST /keys/generate for an agent that already has a key
- Forgot to check key status before generating
Resolution: Use POST /keys/rotate to get a new key (invalidates the old one), or DELETE /keys first then generate.
Example:
{
"error": "Agent 42 already has an API key. Use /keys/rotate to get a new one.",
"errorType": "KEY_ALREADY_EXISTS",
"details": {
"agentId": "42"
},
"timestamp": 1707408000000
}RATE_LIMITED
HTTP Code: 429
When It Occurs: Exceeded the rate limit for the agent's trust tier.
Common Causes:
- Making too many requests within the rate limit window
- Agent is low tier (Bronze = 1 req/min)
- Burst of traffic exceeds sliding window limit
Resolution:
Wait for the duration specified in the Retry-After header before retrying. Upgrade trust tier by staking more LABS in the TrustVault contract.
Rate Limits by Tier:
- Tier 1 (Bronze): 1 request/minute
- Tier 2 (Silver): 16 requests/minute
- Tier 3 (Gold): 166 requests/minute
- Tier 4 (Diamond): 2700 requests/minute
Example:
{
"error": "Rate limit exceeded for agent tier",
"errorType": "RATE_LIMITED",
"details": {
"agentId": "42",
"tier": 1,
"limit": 1,
"retryAfter": 45
},
"timestamp": 1707408000000
}Response Headers:
X-RateLimit-Limit: 1X-RateLimit-Remaining: 0Retry-After: 45
TIER_BLOCKED
HTTP Code: 403
When It Occurs: Agent is Tier 0 (no LABS staked) and cannot use the Verification API.
Common Causes:
- Agent has never staked LABS
- Agent unstaked all LABS and fell back to Tier 0
- TrustVault stake fell below Tier 1 threshold
Resolution: Stake at least 1,000 LABS in the TrustVault contract to reach Bronze tier (Tier 1). See the TrustVault documentation for staking instructions.
Example:
{
"error": "Agent is Tier 0 (unverified) and cannot use the API. Stake at least 1,000 LABS to reach Bronze tier.",
"errorType": "TIER_BLOCKED",
"details": {
"agentId": "42",
"currentTier": 0,
"requiredTier": 1,
"requiredStake": "1000000000000000000000"
},
"timestamp": 1707408000000
}INSUFFICIENT_DEPOSIT
HTTP Code: 400
When It Occurs: FeedbackEscrow doesn't have enough LABS deposited for the agent to cover the feedback relay fee.
Common Causes:
- Agent's FeedbackEscrow balance is too low
- Feedback relay requires a deposit that hasn't been made
Resolution: Deposit more LABS into the FeedbackEscrow contract for the agent. Check the agent's escrow balance and ensure it covers the feedback fee.
Example:
{
"error": "Insufficient LABS deposited in FeedbackEscrow to relay feedback",
"errorType": "INSUFFICIENT_DEPOSIT",
"details": {
"agentId": "42",
"currentBalance": "100000000000000000",
"requiredAmount": "1000000000000000000"
},
"timestamp": 1707408000000
}RELAY_FAILED
HTTP Code: 500
When It Occurs: Feedback relay transaction failed on-chain (transaction reverted or was rejected).
Common Causes:
- Agent's escrow balance is insufficient
- Gas estimation failed
- On-chain validation failed (e.g., agent doesn't exist)
Resolution: Check the agent's FeedbackEscrow balance. Verify relay parameters are correct. If the issue persists, check the transaction hash in the details for more information.
Example:
{
"error": "Failed to relay feedback transaction on-chain",
"errorType": "RELAY_FAILED",
"details": {
"agentId": "42",
"reason": "Transaction reverted",
"txHash": "0x..."
},
"timestamp": 1707408000000
}Rate Limit Headers
All successful responses (200, 201) and rate limit errors (429) include these headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per window for the agent's tier |
X-RateLimit-Remaining | Requests remaining in the current window |
Retry-After | Seconds to wait before retrying (only on 429 errors) |
Example:
X-RateLimit-Limit: 16
X-RateLimit-Remaining: 12On rate limit exceeded:
X-RateLimit-Limit: 16
X-RateLimit-Remaining: 0
Retry-After: 37Error Handling Best Practices
-
Check error types, not just status codes: Multiple errors can share the same HTTP code (e.g., 400 for VALIDATION_ERROR and REPLAY_DETECTED).
-
Parse
detailsfor context: The details object often contains actionable information like field names, current values, or expected formats. -
Implement exponential backoff: For RPC_ERROR and INTERNAL_ERROR, retry with exponential backoff (e.g., 1s, 2s, 4s, 8s).
-
Respect Retry-After headers: For RATE_LIMITED errors, wait the specified duration before retrying.
-
Log timestamps: The
timestampfield helps correlate errors with server logs for debugging. -
Handle TIER_BLOCKED gracefully: Show users a clear message about needing to stake LABS, with a link to the TrustVault.
const response = await fetch('https://api.kyachain.xyz/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey, agentId }),
});
if (!response.ok) {
const error = await response.json();
switch (error.errorType) {
case 'VALIDATION_ERROR':
console.error('Invalid request:', error.details);
break;
case 'SIGNATURE_INVALID':
console.error('Signature mismatch. Expected owner:', error.details.owner);
break;
case 'RATE_LIMITED':
const retryAfter = error.details.retryAfter || 60;
console.log(`Rate limited. Retry in ${retryAfter} seconds.`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
// Retry request
break;
case 'TIER_BLOCKED':
console.error('Agent is Tier 0. Stake LABS to unlock API access.');
// Redirect user to staking flow
break;
case 'RPC_ERROR':
case 'INTERNAL_ERROR':
console.error('Server error. Retrying...');
// Implement exponential backoff
break;
default:
console.error('Unknown error:', error);
}
throw new Error(error.error);
}
const data = await response.json();
console.log('Verification successful:', data);