Use with curl
Everything the L80 CLI does on launch80 goes through a small HTTP API, so curl and a token are enough to publish, list pages in the Directory and get verified. Nothing to install.
curl or the CLI
Both reach the same pages. What changes is who does the safety work:
| curl | L80 CLI | |
|---|---|---|
| Install | nothing | one binary |
| Template | you write "$template" into the JSON | --template, or "$template" |
| Private fields | you remove env.endpoint and env.host; the server refuses them | stripped for you |
| Check before publishing | GET the template schema | --dry-run |
| Verification checks | the server answers with any that fail | L80 verify check, offline, first |
| Record of what you published | GET /api/v1/artifacts | that, plus a local ledger of source files |
| Run BetterBench, reproduce a run | run the tools yourself | L80 betterbench, L80 verify reproduce |
The guides show both: each command block has an L80 CLI | curl switch, and your choice is remembered on every page. The full endpoint list is in the HTTP API reference.
Set up
A token comes with the Supporter role and is emailed to you. Put it in the environment without leaving it in your shell history, then check it:
$ read -rs L80_TOKEN && export L80_TOKEN # paste the token, then enter$ curl -sS https://www.launch80.com/api/v1/keys/me -H "Authorization: Bearer $L80_TOKEN"The answer is {"ok":true,"data":{"key_id":…,"label":…,"is_verifier":…}}. Examples pipe through jq where it helps; it is optional.
Publish
Pick a layout from Templates (each links to a complete valid payload) and name it in the JSON as "$template". Check the payload against the template's schema if you like, then post the file as is:
$ curl -sS https://www.launch80.com/api/v1/templates | jq '.data[].id'$ curl -sS https://www.launch80.com/api/v1/templates/bench.report.v1/schema | jq .data > schema.json$ curl -sS -X POST https://www.launch80.com/api/v1/artifacts \ -H "Authorization: Bearer $L80_TOKEN" \ -H "Content-Type: application/json" \ --data-binary @report.json | jq .dataThe answer carries url (the page), guid and content_hash (keep the last two if you will ask for verification), and says what happened: message (who can see the page), next (how to list or unlist it, how to request verification, each as the HTTP call) and quota (publishes left today). Use --data-binary, not -d, so the bytes arrive unchanged. 10 publishes per key per day; pages are kept 5 days.
A BetterBench run
Publish a BetterBench results.json as written, with the template added and the two private fields removed (the schema rejects them):
$ jq '. + {"$template": "bench.betterbench.v1"} | del(.env.endpoint, .env.host)' results.json > payload.json$ curl -sS -X POST "https://www.launch80.com/api/v1/artifacts?listed=true" \ -H "Authorization: Bearer $L80_TOKEN" \ -H "Content-Type: application/json" \ --data-binary @payload.json | jq .data.urlWhen BetterBench ran on another machine it records no GPU. Add one as a note so the Directory can filter by it: | .env.notes.gpu = "RTX 4090" in the jq filter. Leave out ?listed=true to keep the page unlisted. The page shows your key id as its author; add &author=false (or ?author=false) to leave it off.
List in the Directory
Pages are unlisted, reachable only by their link, until you list them in the Directory. List one as you publish with ?listed=true, or later, in either direction, with the guid from its URL:
$ curl -sS -X PUT https://www.launch80.com/api/v1/artifacts/<guid>/visibility \ -H "Authorization: Bearer $L80_TOKEN" \ -H "Content-Type: application/json" \ -d '{"listed": true}'{"listed": false} takes it out. Only the publishing key can do either, and only for benchmark and scan templates (not generic.report.v1). A listed page shows up within a minute.
History
$ curl -sS "https://www.launch80.com/api/v1/artifacts?limit=50" -H "Authorization: Bearer $L80_TOKEN" \ | jq -r '.data.artifacts[] | [.created_at, .template_id, .trust_tier, (if .listed then "listed" else "-" end), .url] | @tsv'Newest first, up to 200 per call. When a full page comes back, data.next_before is set: pass it as &before=… for the next. Filter in jq, e.g. select(.listed) or select(.trust_tier == "verified").
Get verified
Verification is open to bench.betterbench.v1 and scan.pwdet.v1 pages. Send the hashes of what you published so the provenance check can match them; without them it warns rather than fails:
$ jq -n --arg note "vLLM 0.9.3, --max-model-len 32768" \ --arg body "$(shasum -a 256 payload.json | cut -d' ' -f1)" \ --arg hash "<content_hash from the publish answer>" \ '{note: $note, body_sha256: $body, content_hash: $hash}' > request.json$ curl -sS -X POST https://www.launch80.com/api/v1/artifacts/<guid>/verification \ -H "Authorization: Bearer $L80_TOKEN" \ -H "Content-Type: application/json" \ --data-binary @request.json | jq .data.status$ curl -sS https://www.launch80.com/api/v1/artifacts/<guid>/verification -H "Authorization: Bearer $L80_TOKEN" | jq .data.status$ curl -sS "https://www.launch80.com/api/v1/verifications?scope=mine" -H "Authorization: Bearer $L80_TOKEN"- 201: the page turns pending and the request joins the queue. A failed server check answers
E_VERIFY_INELIGIBLE(422), onedetailsentry per check: fix the run, publish again, request again. - The checks are listed in Get verified. Ten requests per key per day.
Review others
Any key can read the queue and download a run to reproduce it with BetterBench on its own hardware:
$ curl -sS "https://www.launch80.com/api/v1/verifications?scope=queue" -H "Authorization: Bearer $L80_TOKEN" | jq '.data[] | {id, title, url}'$ curl -sS https://www.launch80.com/api/v1/artifacts/<guid>/payload | jq .data > published-results.jsonVerifier keys A verifier decides; a rejection needs a note, which the publisher sees:
$ curl -sS -X POST https://www.launch80.com/api/v1/verifications/<id>/decision \ -H "Authorization: Bearer $L80_TOKEN" \ -H "Content-Type: application/json" \ -d '{"decision": "verified", "note": "matched on 1x RTX 4090"}'Approving a pwdet scan needs the reproduction pwdet writes. The body is in the API reference.
Responses and errors
Every answer is {"ok": true, "data": …} or {"ok": false, "error": {"code", "message", "remedy", "details"?, "request_id"}}. The remedy says what to change; details points at the exact field. A 429 carries Retry-After. Every code.