The h3 command line
Your Human 3.0 progress, Maturity Model position, badges and library from a terminal, and a stable JSON API any agent can drive.
Install
curl -fsSL https://human3.ai/cli/install.sh | sh
The installer downloads https://human3.ai/cli/h3.js, checks its SHA-256 against the digest built into the installer, and refuses to install on a mismatch. It needs Node.js 20 or newer, or Bun. It puts h3 in ~/.local/bin (set H3_INSTALL_DIR to change that). The digest is also published at https://human3.ai/cli/h3.js.sha256.
Or run it without installing: curl -fsSLO https://human3.ai/cli/h3.js && node h3.js help.
Log in
$ h3 login
Your one-time code: BCDF-GHJK
Open https://human3.ai/cli/authorize while signed in, enter the code, and approve.
Waiting for approval (the code expires in 10 minutes)…
✓ Logged in as you@example.com (key sk_h3_AbCd1234…, read + write, expires 2026-12-25).
Open the page while signed in to Human 3.0, type the code, check where the request came from, the access and the expiry, and approve. Codes expire after 10 minutes and work once. The CLI then stores a new API key in ~/.config/h3/credentials.json, readable only by you (mode 0600). h3 logout revokes that key on the server and deletes the file.
A key from h3 login expires after 90 days; pass --expires 30 for a shorter one or --expires never for one that lasts until you revoke it. The device name is whatever the terminal sent, so the approval page also shows the country the request came from, and warns you when that country or network differs from yours. Never approve a code someone else gave you.
For CI or an agent, create a key on your account page and set H3_API_KEY; it takes precedence over the stored login. Test identities cannot hold keys.
Using it from an agent
Every command prints only JSON on stdout when stdout is not a terminal, or with --json. Errors go to stderr, as JSON under --json: {"error": {"code": "…", "message": "…", "exit_code": 2}}. h3 help --json prints the whole catalog: commands, arguments, flags, exit codes, environment and endpoints. During h3 login --json the approval code is written to stderr as one JSON line so an agent can show it to you.
Global flags
--json- Print only JSON on stdout (the default whenever stdout is not a terminal).
--help- Show help for the command.
--no-color- Disable colour (the NO_COLOR environment variable does the same).
Exit codes
| Code | Name | Meaning |
|---|---|---|
0 | ok | The command did what it said. |
1 | usage | Bad arguments, an unknown command, or an id that does not exist. |
2 | auth | Not logged in, the key is invalid, revoked or expired, lacks the scope, or API access is suspended. |
3 | subscription | The command needs an active Human 3.0 membership. |
4 | rate_limited | A rate limit was hit; stderr says how many seconds until it resets. |
5 | server | Network failure, timeout, or a server error. |
Commands
| Command | What it does |
|---|---|
h3 login | Log in from this machine with a device code. |
h3 logout | Revoke this machine's key and delete the stored credentials. |
h3 whoami | Show who the key belongs to and what it can do. |
h3 progress | Your curriculum progress, overall and per track. |
h3 score | Your H3 Maturity Model position and scores. |
h3 badges | Badges you have earned and the ones still open. |
h3 library | List the library, optionally one track or type. |
h3 item | One library item in full. |
h3 next | The item to do next. |
h3 complete | Mark an item complete. |
h3 courses | The individual courses for sale. |
h3 usage | How much of your rate limits you have used. |
h3 completion | Print a shell completion script. |
h3 version | Print the CLI version. |
h3 help | Show help for all commands or one. |
h3 login
Log in from this machine with a device code.
Starts a device login. The CLI prints a short code and https://human3.ai/cli/authorize; open that page signed in to Human 3.0, enter the code, check where the request came from, the scopes and the expiry, and approve. The CLI then stores a new API key in ~/.config/h3/credentials.json (mode 0600). The key expires after 90 days unless you pass --expires. Under --json the code is printed to stderr as one JSON line so an agent can pass it to you.
h3 login [--name NAME] [--read-only] [--expires never|DAYS] [--no-browser]
--name NAME- Name for the key on your account page (default: this machine's hostname).
--read-only- Ask for a read-only key; `h3 complete` will then be refused.
--expires never|DAYS- Key lifetime: 1–365 days, or never. Default: 90 days.
--no-browser- Do not try to open the approval page in a browser.
Log in with a read and write key named after this machine.
$ h3 loginA 30-day read-only key for an agent.
$ h3 login --read-only --name claude-agent --expires 30
Output with --json:
{
"logged_in": true,
"email": "you@example.com",
"key": { "id": "…", "name": "laptop", "prefix": "sk_h3_AbCd1234", "scopes": ["read", "write"], "expires_at": "2026-12-25T10:00:00.000Z" },
"credentials_path": "/home/you/.config/h3/credentials.json"
}
h3 logout
Revoke this machine's key and delete the stored credentials.
Revokes the stored key on the server, then deletes ~/.config/h3/credentials.json. A key supplied through H3_API_KEY is not touched; revoke it on your account page.
h3 logout
Revoke and forget this machine's key.
$ h3 logout
Output with --json:
{ "logged_out": true, "revoked": true, "credentials_removed": true }
h3 whoami
Show who the key belongs to and what it can do.
Your account, membership status, and the key in use (name, prefix, scopes, expiry).
h3 whoami
Check which account this machine is logged in as.
$ h3 whoamiJust the email.
$ h3 whoami --json | jq -r .user.email
Output with --json:
{
"user": { "id": "…", "email": "you@example.com", "name": "You", "member_since": "2026-03-01T00:00:00.000Z" },
"subscription": { "status": "active", "plan": "core", "active": true },
"auth": { "type": "api_key", "key": { "id": "…", "name": "laptop", "prefix": "sk_h3_AbCd1234", "scopes": ["read", "write"], "expires_at": null } }
}
h3 progress
Your curriculum progress, overall and per track.
How many library items you have completed, overall and per track, plus the ids you have completed and viewed.
h3 progress
A per-track table.
$ h3 progressOverall completion percentage.
$ h3 progress --json | jq .percent
Output with --json:
{
"completed": 12,
"total": 64,
"percent": 19,
"viewed": 20,
"tracks": [{ "track": "SO1", "name": "Purpose & Meaning", "completed": 3, "total": 5 }],
"completed_ids": ["SO1-V1"],
"viewed_ids": ["SO1-V1", "SO1-E1"]
}
h3 score
Your H3 Maturity Model position and scores.
Where you are on the Human 3.0 Maturity Model: the current stage, how many stages are complete, the markers you have answered, and an overall position from 0 to 1. Also returns your saved H3MM self-scores when you have them.
h3 score
Current stage and per-stage markers.
$ h3 scoreJust the stage name.
$ h3 score --json | jq .position.stage.name
Output with --json:
{
"position": {
"stage": { "order": 2, "name": "Activated" },
"stages_complete": 1,
"stages_total": 5,
"markers_answered": 7,
"markers_total": 20,
"position": 0.28
},
"stages": [{ "order": 1, "name": "Aware", "answered": 4, "total": 4, "complete": true }],
"scores": { "SO1": 3 }
}
h3 badges
Badges you have earned and the ones still open.
Every badge with your level in it: 0 unearned, 1 Practicing, 2 Embodying.
h3 badges [--earned]
--earned- Only badges at level 1 or above.
What you have earned so far.
$ h3 badges --earnedNames of badges at Embodying.
$ h3 badges --json | jq '.badges[] | select(.level == 2) | .name'
Output with --json:
{
"earned": 5,
"total": 39,
"badges": [{ "id": "navigator", "name": "Navigator", "description": "You know where you're going", "domain": "SO", "level": 1, "level_name": "Practicing", "earned_at": "2026-09-01 10:00:00" }]
}
h3 library
List the library, optionally one track or type.
Every active item in the library in curriculum order, with whether you have viewed or completed it. Video items link to their page on human3.ai; the API never hands out a playable video URL.
h3 library [--track TRACK] [--type TYPE] [--todo]
--track TRACK- A track (SO1…CA4) or a whole domain (SO, SH, CO, CA).
--type TYPE- Only items of this type (video, exercise, book, essay…).
--todo- Only items you have not completed.
The Purpose & Meaning track.
$ h3 library --track SO1Everything left in the Capabilities domain, as JSON.
$ h3 library --track CA --todo --json
Output with --json:
{
"count": 1,
"items": [
{
"id": "SO1-V1",
"track": "SO1",
"track_name": "Purpose & Meaning",
"type": "video",
"title": "Finding Your TELOS",
"description": "…",
"duration_seconds": 1260,
"completed": false,
"viewed": true,
"url": "https://human3.ai/content/SO1-V1",
"video_page": "https://human3.ai/v/<stream-uid>/iframe"
}
]
}
h3 item
One library item in full.
Everything about one item: description, why it matters, its steps, and links.
h3 item <id>
<id>- The item id, as shown by `h3 library`.
Read the steps for an exercise.
$ h3 item SO1-E1
Output with --json:
{
"item": {
"id": "SO1-E1",
"track": "SO1",
"track_name": "Purpose & Meaning",
"type": "exercise",
"title": "Write your draft mission statement",
"description": "…",
"importance": "…",
"steps": ["…"],
"duration_seconds": null,
"completed": false,
"viewed": false,
"has_file": false,
"essay_url": null,
"url": "https://human3.ai/content/SO1-E1",
"video_page": null
}
}
h3 next
The item to do next.
The item you started but have not finished, if there is one; otherwise the first item you have not completed, in curriculum order.
h3 next
What to work on now.
$ h3 nextMark the next item done (needs a write key).
$ h3 complete "$(h3 next --json | jq -r .item.id)"
Output with --json:
{
"reason": "in_progress",
"item": {
"id": "SO1-V1",
"track": "SO1",
"track_name": "Purpose & Meaning",
"type": "video",
"title": "Finding Your TELOS",
"description": "…",
"duration_seconds": 1260,
"completed": false,
"viewed": true,
"url": "https://human3.ai/content/SO1-V1",
"video_page": "https://human3.ai/v/<stream-uid>/iframe"
}
}
h3 complete
Mark an item complete.
Marks the item complete, re-evaluates your badges exactly as the web does, and reports any badge that changed. Needs a key with the write scope.
h3 complete <id>
<id>- The item id.
Mark the mission-statement exercise done.
$ h3 complete SO1-E1
Output with --json:
{
"item_id": "SO1-E1",
"completed": true,
"already_completed": false,
"badges_changed": [{ "id": "navigator", "name": "Navigator", "level": 1, "level_name": "Practicing" }],
"progress": { "completed": 13, "total": 64, "percent": 20 }
}
h3 courses
The individual courses for sale.
The à-la-carte course catalog, newest first. Every one is already included in the membership.
h3 courses
List the courses and prices.
$ h3 courses
Output with --json:
{
"courses": [{ "id": "…", "title": "…", "kind": "video", "tier": "video-tier-1", "description": "…", "price_cents": 4995, "price": "$49.95", "duration_minutes": 83, "published_at": "2026-07-21", "url": "https://human3.ai/courses/…" }]
}
h3 usage
How much of your rate limits you have used.
Your key's per-minute budget, your per-minute budget across all your keys, and your per-day quota: limit, used, remaining, and seconds until each resets.
h3 usage
Check headroom before a batch job.
$ h3 usage
Output with --json:
{
"key_minute": { "limit": 120, "used": 3, "remaining": 117, "reset_seconds": 41, "window_seconds": 60 },
"member_minute": { "limit": 240, "used": 3, "remaining": 237, "reset_seconds": 41, "window_seconds": 60 },
"member_day": { "limit": 10000, "used": 210, "remaining": 9790, "reset_seconds": 51000, "window_seconds": 86400 }
}
h3 completion
Print a shell completion script.
Prints a completion script for bash, zsh or fish. Add it to your shell's startup file.
h3 completion <bash|zsh|fish>
<shell>- bash, zsh or fish.
Install zsh completion (with ~/.zfunc on your fpath).
$ h3 completion zsh > ~/.zfunc/_h3Install bash completion.
$ echo 'eval "$(h3 completion bash)"' >> ~/.bashrc
Output with --json:
{ "shell": "zsh", "script": "#compdef h3 …" }
h3 version
Print the CLI version.
The CLI version and the runtime it is running under.
h3 version
Which version is installed.
$ h3 version
Output with --json:
{ "version": "1.0.0", "runtime": "node", "runtime_version": "22.9.0" }
h3 help
Show help for all commands or one.
Lists every command. With --json, prints this whole catalog (commands, flags, exit codes, environment, endpoints) for an agent to read.
h3 help [command]
<command>- Show help for this command only. Optional.
Help for one command.
$ h3 help libraryThe machine-readable catalog.
$ h3 help --json
Output with --json:
{ "name": "h3", "version": "1.0.0", "commands": [ … ], "global_flags": [ … ], "exit_codes": [ … ], "env": [ … ], "endpoints": [ … ] }
Environment
| Variable | Effect |
|---|---|
H3_API_KEY | Use this key instead of the stored credentials. Takes precedence over h3 login. |
H3_BASE_URL | API origin (default https://human3.ai). Must be https, except for localhost. |
NO_COLOR | Any value turns colour off. |
XDG_CONFIG_HOME | Where h3/credentials.json lives (default ~/.config). |
API
The CLI is a thin client over https://human3.ai/api/v1. Send Authorization: Bearer sk_h3_…. Keys are 256-bit random strings; we store only their SHA-256 hash, show a short prefix so you can tell them apart, and let you revoke them any time. A key is read or read + write, may expire after 1 to 365 days, and a member can hold at most 20 active keys. Member-only endpoints use the same membership check as the website.
curl -s https://human3.ai/api/v1/progress -H "Authorization: Bearer $H3_API_KEY"
| Method | Path | Auth | Returns |
|---|---|---|---|
GET | /api/v1/me | key | Your account, membership and the key in use. |
GET | /api/v1/subscription | key | Membership status, plan and period end. |
GET | /api/v1/usage | key | Rate-limit usage for the key and the member. |
GET | /api/v1/courses | key | The individual course catalog. |
GET | /api/v1/progress | key, member | Curriculum progress overall and per track. |
GET | /api/v1/h3mm | key, member | Maturity Model position, stages and self-scores. |
GET | /api/v1/badges | key, member | Every badge with your level. |
GET | /api/v1/library | key, member | Active library items; ?track= (track or domain), ?type=. |
GET | /api/v1/library/:id | key, member | One active item in full. |
GET | /api/v1/next | key, member | The recommended next item. |
POST | /api/v1/progress/:id/complete | key (write), member | Mark an item complete and re-evaluate badges. |
DELETE | /api/v1/keys/current | key | Revoke the key making the request (logout). |
POST | /api/v1/device/code | none | Start a device login: returns a device code, a user code and the approval URL. |
POST | /api/v1/device/token | none | Poll a device login; returns the new key once, after approval. |
Video items link to their page on human3.ai, which checks your membership before it plays anything; the API never returns a playable or signed video URL, a storage URL, or an unpublished item.
Errors
Every error is JSON: {"error": "<code>", "message": "…"}. A bad, revoked or expired key always gets the same 401 body.
| Status | Error | Meaning |
|---|---|---|
400 | invalid_request | A parameter or body field is missing or malformed. |
400 | authorization_pending | Device login: not approved yet; poll again after interval seconds. |
400 | slow_down | Device login: polling too fast; the response carries the new interval. |
400 | expired_token | Device login: the code expired (codes live 10 minutes). |
400 | access_denied | Device login: the member denied the request. |
400 | invalid_grant | Device login: unknown or already-used device code. |
401 | unauthorized | No valid credentials. The body is identical for every cause. |
403 | insufficient_scope | The key lacks the scope this call needs (write). |
403 | subscription_required | This call needs an active membership. |
403 | api_access_suspended | API access for this account is suspended. |
403 | forbidden_origin | A cross-site browser request was refused. |
403 | key_refused | Device login: this account cannot hold another key (test identity, suspended, or 20 active keys). |
404 | not_found | No such endpoint, or no active item with that id. |
429 | rate_limited | A rate limit was hit; see Retry-After. |
500 | internal_error | Something failed on our side. |
Rate limits
Every /api/v1 response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds), describing whichever budget you are closest to running out of, plus RateLimit-Policy listing all of them. A 429 also carries Retry-After, the seconds until that window ends. Windows are fixed and aligned to the clock.
| Policy | Limit | Window | Counted per | Notes |
|---|---|---|---|---|
key-minute | 120 | per minute | key | Requests per API key (or per signed-in browser session) per minute. |
member-minute | 240 | per minute | member | Requests per member per minute, across all of that member's keys and sessions, so more keys never mean more burst. |
member-day | 10,000 | per UTC day | member | Requests per member per UTC day, across all of that member's keys and sessions. |
auth-fail-ip | 30 | per 10 minutes | ip | Wrong or unknown API keys per IP address per 10 minutes. Past it, wrong keys from that IP get 429 until the window ends; valid keys and requests with no key are not affected. |
device-start-ip | 10 | per 10 minutes | ip | h3 login starts per IP address per 10 minutes. |
device-poll-ip | 300 | per 10 minutes | ip | Device-token polls per IP address per 10 minutes. |
key-create-member | 30 | per hour | member | API keys created per member per hour, on the account page or by h3 login together. |
device-approve-member | 20 | per 10 minutes | member | Code look-ups and approvals on /cli/authorize per member per 10 minutes. |
Device login polls faster than every 5 seconds answer slow_down, and each one adds 5 seconds to the interval.
Manage keys on your account page. Questions: support@unsupervised-learning.com.