# `GET /v1/verify`

Returns a signed verification record for a domain — the same data behind
[`verify_merchant`](../mcp/tools/verify-merchant.md), for callers that are not MCP
clients.

```
GET https://api.converg3nce.com/v1/verify?domain=acme.com
Authorization: Bearer <your-api-key>
```

!!!warning This endpoint is key-gated
Open access was considered and rejected. The record is signed and independently
verifiable via [the public key set](jwks.md), but obtaining one requires a key.
!!!

`Cache-Control: public, max-age=60, s-maxage=300`

## Record shape

```json
{
  "schema": "https://converg3nce.com/schema/verification/v1",
  "domain": "acme.com",
  "status": "verified",
  "tier": "silver",
  "score": 63,
  "max_score": 100,
  "sub_scores": {
    "visibility": 83,
    "recommendation_rate": 41,
    "technical_readiness": 75
  },
  "flags": [],
  "evidence": {
    "ok": true,
    "coverage": 1.0,
    "site_coverage": 1.0,
    "engines_ran": ["openai", "anthropic", "gemini", "perplexity"],
    "engines_skipped": [],
    "blockers": []
  },
  "archetype": "ecommerce",
  "first_seen": "2026-06-01T00:00:00.000Z",
  "last_audited_at": "2026-08-01T06:00:00.000Z",
  "next_audit_at": "2026-09-01T06:00:00.000Z",
  "issuer": "converg3nce.com",
  "issued_at": "2026-08-31T09:00:00.000Z",
  "key_id": "c3n-default",
  "signature": "ed25519:…"
}
```

| Field | Notes |
|---|---|
| `status` | `verified` \| `provisional` \| `unverified` \| `flagged` |
| `tier` | `gold` (≥80) \| `silver` (≥50) \| `bronze` (≥1) \| `null` |
| `score` | 0–100, or `null` |
| `max_score` | Always 100 |
| `flags` | e.g. `missing_signal:<name>`, `thin_evidence` |
| `next_audit_at` | Next scheduled monthly re-audit |
| `key_id`, `signature` | Present **only** when the record is signed |

!!!info There is no `expires_at`
Records carry `next_audit_at`, not an expiry. A record does not become invalid on a
date; it becomes stale, and `last_audited_at` tells you how stale.
!!!

## An unknown domain is `200`, never `404`

A valid but never-audited domain returns `200` with `status: "unverified"` and
`score: null`. This is a positive statement — "we have no record of this merchant" —
and is distinct from the domain being malformed.

Thin evidence downgrades `status` to `provisional` and forces `tier` to `null`.

## Errors

| Status | Code | Meaning |
|---|---|---|
| `400` | `INVALID_DOMAIN` | Not a valid hostname |
| `503` | `STORE_UNAVAILABLE` | Verification records unavailable from this deployment |
| `503` | `UNAVAILABLE` | Temporary assembly failure |

## Signature verification

Records are signed with **Ed25519** over the RFC 8785 canonical JSON of the record with
`key_id` and `signature` removed. Signatures are `ed25519:<base64url>`.

!!!danger An unsigned record is possible — check for the field
If signing is not configured, the record is served **without** a `signature` field
rather than with an invalid one. A verifier must treat a missing signature as
unverified, not as a pass. Do not skip the presence check.
!!!

Fetch the public keys from [`/.well-known/converg3nce-keys.json`](jwks.md).
