docs / post /api/verify

POST /api/verify

Verify a raw (addr, msg, sig) tuple directly. Skips Nostr discovery — useful when you already have the envelope in hand (from a QR code, embedded badge, or offline exchange).

POST https://ochk.io/api/verify
Content-Type: application/json

Request body

{
  "addr":   "bc1q...",
  "msg":    "orangecheck\nidentities: ...\naddress: ...\n...",
  "sig":    "AkcwRAIg...",
  "scheme": "bip322",
  "options": {
    "testMode":    false,
    "expectedAud": "https://example.com"
  }
}

Fields

FieldRequired?TypeNotes
addryesstringBitcoin address.
msgyesstringFull canonical message with trailing LF.
sigyesstringBIP-322 or legacy signature. Base64 or hex.
schemeoptional"bip322" / "legacy"Default "bip322". Auto-detected from address if omitted.
options.testModeoptionalboolAccept testnet / signet addresses.
options.expectedAudoptionalstringRequire the aud: extension to equal this origin.

Response (200)

{
  "ok": true,
  "codes": ["sig_ok_bip322", "bond_confirmed"],
  "network": "mainnet",
  "attestation_id": "a3f5b8c2…",
  "identities": [
    { "protocol": "github", "identifier": "alice" }
  ],
  "metrics": {
    "sats_bonded": 125000,
    "days_unspent": 47,
    "score": 18.2
  }
}

Fields

FieldTypeNotes
okbooltrue iff signature valid and no policy failure.
codesstring[]Status codes — see Verification.
networkstringDerived from address + network: extension.
attestation_idstringSHA-256(msg), lowercase hex.
identitiesarrayClaims from the signed message.
metricsobjectsats_bonded, days_unspent, score. Absent if signature invalid.

Status codes emitted inside codes

  • Signaturesig_ok_bip322, sig_ok_legacy, sig_invalid, sig_unsupported_script.
  • Bondbond_confirmed, bond_zero, bond_pending, bond_insufficient.
  • Policyaud_mismatch, expired, network_testmode.
  • Inputbad_request, decode_error, invalid_scheme.

A valid proof returns ok: true with codes like ["sig_ok_bip322", "bond_confirmed"]. A proof below a bond floor returns ok: false with ["sig_ok_bip322", "bond_insufficient"].

Examples

curl

curl -X POST "https://ochk.io/api/verify" \
  -H 'Content-Type: application/json' \
  -d '{
    "addr":   "bc1q...",
    "msg":    "orangecheck\nidentities: \naddress: bc1q...\n...",
    "sig":    "AkcwRAIg...",
    "scheme": "bip322"
  }'

SDK

import { verify } from '@orangecheck/sdk';

const outcome = await verify({
  addr, msg, sig, scheme: 'bip322',
});
if (outcome.ok) {
  console.log('metrics:', outcome.metrics);
}

When to use this vs /api/check

  • Use /api/check when the subject just provided an address and you want the latest attestation + threshold gate.
  • Use /api/verify when you already hold an attestation envelope (from a badge, offline exchange, QR) and need to confirm its authority.

Both end up at the same verification math — /api/check just does the Nostr discovery step for you.

Rate limit

10 req/min per IP on the free tier. Lower than /api/check because every request incurs chain lookups without caching.

Status codes

HTTPMeaning
200Verification ran; body carries result (valid or not).
400bad_request — missing/malformed body.
429Rate limited.
500Server error.