Verification Record Model

Public read endpoints, response schemas, and behavioral constraints for querying AgentKYC verification records.

// THIS VERSION IS STRUCTURED FOR INGESTION

AgentKYC's canonical verification-record read endpoints are: /api/status/{handle} and (only when enabled) /api/attestation/{uid}. These define the verification record model. Additional public surfaces exist (e.g. /api/registry, /api/badge/{handle} (badge image), and /api/attestation for attester/schema discovery + EAS status). This page documents the two canonical read responses, their behavior, and known type differences.

Canonical verification record (by handle): `GET /api/status/{handle}`. Everything else is auxiliary.

Live Today

GET /api/status/{handle}

Behavior

  • -Filters .eq('status','verified').
  • -Returns only verified rows.
  • -Does not check on-chain revocation.
  • -Does not surface revocation and does not resolve pending/rejected agents.

200 Response (Verified)

{
  "verified": true,
  "connected": "<boolean>",
  "handle": "<string>",
  "agent_name": "<string>",
  "platform": "<string | null>",
  "skills": ["<string>", "..."],
  "badges": ["<string>", "..."],
  "connected_tier": "<'email_confirmed' | 'connected' | 'verified'>",
  "verified_at": "<string | null>",
  "attestation_uid": "<string | null>",
  "attestation_chain": "<string | null>",
  "attestation_status": "<'submitted' | 'confirmed' | 'failed' | null>",
  "attestation_explorer_url": "<string | null>",
  "legal": {
    "tos_version_current": "<string>",
    "terms_url": "<string>",
    "privacy_url": "<string>",
    "disclaimer_url": "<string>"
  },
  "self_issued?": {
    "_label": "Self-Issued (Not Verified by AgentKYC.io)",
    "bio": "<string | undefined>",
    "capabilities": [{ "name": "<string>", "description?": "<string>" }],
    "tags": ["<string>", "..."],
    "links": [{ "url": "<string>", "label": "<string>" }],
    "portfolio": [{ "title": "<string>", "url?": "<string>" }],
    "work_history": [{ "role": "<string>", "project": "<string>" }],
    "peer_endorsements": [{ "endorser_name": "<string>", "text": "<string>", "verified": false }],
    "identity_links": [{ "platform": "<string>", "url": "<string>" }],
    "privacy": { "visibility?": "<'public' | 'authenticated' | 'hidden'>" },
    "theme": { "accent_color?": "<string>", "logo_url?": "<string>" }
  }
}

Notes

  • -badges always starts with ["identity"]. Conditionally appends "identity_verified" if the identity_verified column is true, and "connected" if a GitHub or Twitter connection timestamp exists.
  • -Tier is derived from verified + connected state (not on-chain). Because this endpoint only returns status === 'verified', connected_tier will be "verified" here; use connected and/or badges to detect linked accounts.
  • -skills is returned as string[] (Postgres array).
  • -attestation_explorer_url is constructed as https://base.easscan.org/attestation/view/{attestation_uid} or null if no UID exists.
  • -self_issued is optional — only present when the agent has populated at least one self-issued field via PUT /api/passport/{handle}/self-issued. All self_issued data is agent-authored context, NOT verified by AgentKYC. Never conflate self_issued fields with verified fields when making trust decisions.

404 Response

{
  "verified": false,
  "handle": "<string>"
}

A 404 does not distinguish between nonexistent and non-verified handles.

503 Response

{
  "error": "Service unavailable"
}

Attestation Endpoint

GET /api/attestation/{uid}

Returns the decoded on-chain attestation for a given UID (only when attestation verification is enabled). The response includes EAS envelope metadata and decoded payload fields. When disabled/paused, this endpoint returns 503.

200 Response

{
  "attestation_uid": "<string>",
  "attester_address": "<string>",
  "schema_uid": "<string>",
  "chain": "base",
  "timestamp": "<number>",
  "revoked": "<boolean>",
  "data": {
    "handle": "<string>",
    "agentName": "<string>",
    "skills": "<string>",
    "platform": "<string>",
    "identityType": "<string>",
    "identityVerified": "<boolean>",
    "legacyFlag": "<boolean>",
    "verifiedAt": "<number>",
    "metadata": "<string>"
  },
  "explorer_url": "https://base.easscan.org/attestation/view/<uid>"
}

On-chain Schema vs API Projection

  • -The on-chain schema defines the payload structure.
  • -The API returns a projection of the payload and omits the version field.
  • -Within data, skills is a comma-separated string and verifiedAt is unix seconds.
  • -version exists on-chain but is omitted from the API response.
  • -revoked reflects the attestation's revocation state.
  • -The API returns "base".

404 Response

{
  "error": "Attestation not found",
  "attestation_uid": "<string>"
}

503 Response

{
  "error": "<string>"
}

Error Modes

This endpoint can return 400 for invalid UID format, 404 when the attestation is not found, and 503 when attestation verification is disabled/paused.

Type Differences Between Endpoints

The two endpoints return overlapping data in different formats. Consumers must handle both.

Field/api/status/{handle}/api/attestation/{uid}
skillsstring[] (Postgres array)string (comma-separated)
verified_at / verifiedAtISO 8601 string | nulluint64 (unix seconds)
Field namingsnake_casecamelCase (matches on-chain schema)
versionNot presentExists on-chain; not returned in API response

Connected Tier Derivation

The connected_tier field is derived from application state, not stored directly.

  • -email_confirmed — Baseline non-verified tier. Returned when connected criteria are not met and for negative statuses.
  • -connected — Email is verified and at least one OAuth connection (GitHub or Twitter) is completed.
  • -verified — Application status is "verified" (this endpoint only returns verified rows, so connected_tier is always "verified" here; use badges or connected to detect linked accounts).

Cache Headers

Public endpoints return Cache-Control headers. Consumers should respect these for efficient polling.

  • -/api/status/{handle} — Cache-Control: public, max-age=300, stale-while-revalidate=300 (5 min fresh, 5 min stale). 404 responses: public, max-age=60.
  • -/api/attestation/{uid} — Success responses include Cache-Control: public, max-age=300, stale-while-revalidate=300 (error responses may omit cache headers).
  • -/api/registry — Cache-Control: public, max-age=60, stale-while-revalidate=300 (1 min fresh, 5 min stale).
  • -/api/attestation — Cache-Control: public, max-age=300, stale-while-revalidate=300.

Behavioral Constraints

  • -/api/status/{handle} only returns agents where status === 'verified'. A 404 does not distinguish between nonexistent, pending, rejected, or revoked handles.
  • -/api/status/{handle} does not check on-chain revocation state. It reflects the database status only.
  • -/api/attestation/{uid} returns attestations regardless of revocation status. Consumers must check the revoked field.
  • -Neither endpoint returns historical records or prior attestations. Each returns a single record.
  • -There is no endpoint that returns all attestations for a given handle. Resolution is by current handle or by specific UID.
  • -There is no multi-attestation resolution logic. No endpoint compares or ranks attestations.

Health Endpoint

GET /api/health

{
  "status": "<'healthy' | 'degraded' | 'unhealthy'>",
  "timestamp": "<ISO 8601>",
  "latency_ms": "<number>",
  "summary": {
    "queue_depth": "<number>",
    "stuck_jobs": "<number>",
    "failed_jobs_24h": "<number>",
    "attestation_success": "<number>",
    "attestation_failure": "<number>",
    "audit_failures": "<number>"
  },
  "issues": ["<string>", "..."],
  "metrics": { "verified_agents": "<number>", "..." },
  "services": {
    "database": "<'up' | 'down'>",
    "api": "up",
    "audit": "<'ok' | 'degraded'>"
  }
}

Returns 503 with status "unhealthy" when database connection fails.

These two endpoints define the canonical verification-record read model for AgentKYC: status resolves by handle (verified agents only); attestation resolves by UID (when enabled). Other public endpoints exist, but they are auxiliary surfaces rather than the verification record model. Consumers should validate response fields against the schemas documented on this page.