source
CUVETSMO · verified vet knowledge

Public API — v0.0.1

API documentation

Free public read of every canonical entry, ontology cross-reference, public key, and signature event. CORS-enabled. Edge-cached. No API key required.

Inverted economics

Public read is free forever. Hospital EHRs, AI agents, research workflows can integrate without paying anything. Institutional write (POST contributions back, bulk dataset export, dataset DOI minting) is paid — revenue flows back to the contributing department per their reviewed entries. Phase 0 ships the read surface; institutional write comes online Phase 1.

Endpoints

GET/api/drugspublic

List all canonical entries (faculty-reviewed AND signed). Pending entries are filtered out.

Example

curl https://source.cuvetsmo.com/api/drugs

Response shape

{
  "apiVersion": "0.0.1",
  "lastUpdated": "<iso>",
  "count": 1,
  "data": [ { "slug": "meloxicam", ... } ]
}
GET/api/drugs/{slug}public

Single drug entry by slug. Includes pending entries (with amber status visible in response).

Example

curl https://source.cuvetsmo.com/api/drugs/meloxicam

Response shape

{
  "apiVersion": "0.0.1",
  "lastUpdated": "<iso>",
  "data": { "slug": "meloxicam", ... }
}
GET/api/by-codepublic

Filter by ontology code. Supports ATC prefix-match and RxNorm exact match.

Rate limit: 1000 / IP / day (Phase 1 enforced)

Example

curl 'https://source.cuvetsmo.com/api/by-code?system=atc&code=M01AC06'

Response shape

{
  "query": { "system": "atc", "code": "M01AC06" },
  "count": 1,
  "data": [ { "slug": "meloxicam", "codes": {...} } ]
}
GET/api/keys/{kid}public

Public signing key as JSON Web Key. Use for client-side signature verification.

Example

curl https://source.cuvetsmo.com/api/keys/cuvetsmo-board

Response shape

{
  "apiVersion": "0.0.1",
  "data": {
    "kty": "OKP", "crv": "Ed25519", "x": "<base64url>",
    "fingerprint": "ed25519:<hex>",
    "displayName": "CUVETSMO Editorial Board"
  }
}
GET/api/logpublic

Append-only transparency log of every signature event. Audit trail for the curious.

Rate limit: 100 / IP / day (Phase 1, large payload)

Example

curl https://source.cuvetsmo.com/api/log

Response shape

{
  "apiVersion": "0.0.1",
  "count": N,
  "entries": [ { "entryType": "drug-signature", "drugSlug": "meloxicam", ... } ]
}
GET/api/healthpublic

Citation-probe health summary — coverage stats, dead URLs, source breakdown. Lets external monitoring detect citation rot.

Example

curl https://source.cuvetsmo.com/api/health

Response shape

{
  "apiVersion": "0.0.1",
  "summary": {
    "totalCitations": 132, "probed": 119, "healthy": 118,
    "unhealthy": 1, "pctProbed": 90, "pctHealthy": 99
  },
  "bySource": { "dailymed": {...}, "atc": {...}, ... },
  "unhealthyEntries": [ { "cid": "...", "url": "...", "latestStatus": 404 } ]
}
GET/api/datasetinstitutional

Bulk dataset export. Phase 1+. Today: returns 402 + contact instructions.

Example

curl -i https://source.cuvetsmo.com/api/dataset

Response shape

HTTP/2 402
{
  "error": { "code": "institutional-tier-required", ... },
  "contact": { "email": "palm@cuvetsmo.com", ... }
}

Rate limits + response headers

Phase 0 ships rate-limit headers but does not enforce them yet (so you can build integrations today and not break when enforcement flips on in Phase 1):

X-Source-Tier: public
X-Source-Limit-RPD: 1000
X-Source-Cost: 1
X-Source-Enforcement: phase-0-soft

Tier public = anonymous. Tier institutional = paid. Tier admin= maintainers only. When enforcement flips on, exceeding RPD returns HTTP 429 with the same shape as the 402 dataset response.

CORS

All GET endpoints set Access-Control-Allow-Origin: *. You can fetch from any origin (browser, curl, Node, Python, Go). POST is locked — Phase 1 enables it for institutional keys only.

Sample integration

// Browser — fetch + verify a single entry
const drug = await fetch('https://source.cuvetsmo.com/api/drugs/meloxicam').then(r => r.json())
const key = await fetch(`https://source.cuvetsmo.com/api/keys/${drug.data.signatures[0].signerKeyId.split(':')[1].slice(0,99)}`)
// For real verification, fetch by signerId not by fingerprint slice
// see /verify/[slug] for the proper pattern