Hatch REST API Reference
Base URL: https://api.gohatch.fun (production) · http://localhost:3001 (local)
Interactive docs: Swagger UI at
/api/v1/docs. OpenAPI JSON at/api/v1/openapi.json. Schema is stable — breaking changes bump the path version (/v2/…).
Table of contents
Authentication
Two authentication modes:
Anonymous (free tier)
No auth. Rate-limited at 60 req/min per IP. Use this for quick integrations and docs browsing. Invalid bearer still 401s — presenting a bad key never silently downgrades to free tier.
Bearer API key (pro + enterprise)
Authorization: Bearer hk_live_<base64url>
Keys:
- Free (
hk_live_…) — 60 req/min, not currently issued. - Pro — 600 req/min.
- Enterprise — 6,000 req/min.
Keys are sha256-hashed in the DB with an indexed prefix. A leaked DB doesn't grant access. The prefix is visible in the admin panel so leaked keys can be recognized and revoked without seeing plaintext.
Issuance (internal CLI):
pnpm --filter @hatch/api exec tsx src/scripts/issue-api-key.ts \
--tier pro --label acme --owner ops@acme.xyz
See API keys guide for tier comparison and rotation policy.
Rate limits
| Tier | Request limit | Who |
|---|---|---|
| Anonymous | 60/min per IP | Docs browsing, quick curls |
| Free key | 60/min per key | (future) signed-up devs |
| Pro | 600/min per key | Integrators |
| Enterprise | 6,000/min per key | Volume consumers |
Per-route overrides (always lowest wins):
| Route | Limit | Reason |
|---|---|---|
POST /v1/score |
60/min/IP | Expensive LLM call |
POST /v1/score/:id/publish |
10/min/IP | Writes on-chain |
POST /v1/enroll |
20/min/IP | Signature validation cost |
POST /v1/score/:id/rescore |
30/min/IP | Replay cost |
| Catch-all | 300/min/IP | Defense in depth |
Hitting a limit returns 429 with body { error: { code: 'rate_limit' } }.
Errors
All errors return JSON with a typed code:
{ "error": { "code": "not_found", "message": "Score not found" } }
Common codes:
| Code | HTTP | Meaning |
|---|---|---|
not_found |
404 | Resource doesn't exist |
unauthorized |
401 | Missing or invalid bearer |
rate_limit |
429 | Over rate limit |
bad_request |
400 | Validation failed |
upstream |
502 | Downstream dep failed (Anthropic, BSC RPC, etc.) |
not_configured |
503 | Optional feature env not wired |
has_stubs |
409 | Operation refuses preliminary scores |
conflict |
409 | Idempotency conflict (enrollment by different creator) |
Public endpoints (v1)
Base path: /api/v1 (via apiKeyAuth middleware).
GET /api/v1/score/:tokenAddress
Latest score for a token by contract address.
curl https://api.gohatch.fun/api/v1/score/0x1234...
Returns the full ScoreResult (see SDK types):
aggregate, band, per-signal scores, explanation, preliminary flag.
GET /api/v1/launches
List recent enrolled launches, newest first.
Query params:
limit— 1–50, default 25band— filter bygreen | amber | red
curl 'https://api.gohatch.fun/api/v1/launches?limit=10&band=green'
GET /api/v1/leaderboards
Best + worst scores in a time window.
Query params:
window—24h | 7d | all, default24hlimit— 1–50, default 10
Returns { window, limit, best: [...], worst: [...], bestCount, worstCount }.
Excludes rows with hasStubs: true.
GET /api/v1/creator/:address
Public creator profile — every launch address has enrolled, plus
aggregate stats. Doesn't dedupe by X handle or surface wallet clusters.
GET /api/v1/graduates
Graduated tokens (post-Crack). Stable empty shape until E.4 backfills graduation events — clients can build against the structure today.
GET /api/v1/openapi.json
OpenAPI 3.1 spec. Machine-readable source of truth.
GET /api/v1/docs
Swagger UI — browse + try every endpoint interactively.
Private endpoints
Base path: /v1. Most require bearer auth or domain-specific signed
payloads.
Scoring (/v1/score/*)
| Method | Path | Purpose | Auth |
|---|---|---|---|
POST |
/v1/score |
Score a new submission | none (rate-limited) |
GET |
/v1/score/:id |
Fetch score by UUID | none |
POST |
/v1/score/:id/publish |
Publish attestation on-chain | none, refuses stubs |
POST |
/v1/score/:id/rescore |
Replay submission, new UUID | none |
GET |
/v1/score/:id/percentile?window=7d |
Percentile vs cohort | none |
See Scoring guide for the POST /v1/score request
body schema and Attestations guide for publish
flow.
Enrollment (/v1/enroll*)
| Method | Path | Purpose |
|---|---|---|
GET |
/v1/enroll/message?scoreRequestId=…&creatorAddress=… |
Canonical EIP-191 message |
POST |
/v1/enroll |
Submit signed enrollment |
GET |
/v1/enrollment/:scoreId |
Idempotent read |
Launch (/v1/launch/*)
| Method | Path | Purpose |
|---|---|---|
GET |
/v1/launch/:id |
Full launch summary (enrollment + page + notifs + attendance) |
PATCH |
/v1/launch/:id/page |
Edit pre-launch page (creator-signed) |
POST |
/v1/launch/dispatch |
Run notification dispatcher tick (admin) |
Webhooks (/v1/webhooks/*)
| Method | Path | Purpose |
|---|---|---|
POST |
/v1/webhooks |
Subscribe |
GET |
/v1/webhooks/:id |
Get subscription |
DELETE |
/v1/webhooks/:id |
Unsubscribe |
POST |
/v1/webhooks/dispatch |
Dispatcher tick (admin) |
See Webhooks guide.
Admin (/v1/admin/*)
All require Authorization: Bearer <ADMIN_TOKEN>. Fail-closed (503) when
ADMIN_TOKEN isn't configured.
| Path | Returns |
|---|---|
GET /v1/admin/stats |
Scoring + enrollment + key + attestation totals |
GET /v1/admin/scores?limit=25 |
Recent scores |
GET /v1/admin/enrollments?limit=25 |
Recent enrollments |
GET /v1/admin/api-keys |
All issued keys (never plaintext) |
GET /v1/admin/analytics |
24h/7d scores, bands, conversion, key tiers |
GET /v1/admin/status |
DB probe + uptime + commit SHA |
Webhooks
Subscribe to score.created, enrollment.created, attestation.published,
or launch.scheduled.
Payload is signed with HMAC-SHA256 via a shared secret you provide at
subscription time. Verify the signature before trusting the payload.
See Webhooks guide for delivery guarantees (at-least-
once), retries, and the encrypted-at-rest secret handling.
Changelog
API changelog lives at /transparency
(renders apps/web/src/lib/incidents.ts) alongside the product changelog
and incident log.
Breaking changes policy: anything under /api/v1/* is a stable
contract. Breaking changes bump the path version (/api/v2/*) and
announce deprecation with at least 90 days' notice.