AI Agent Guide
The Hamtrax CLI is built to be operated by AI agents as a first-class consumer, not just a humans-at-keyboards afterthought. This guide covers the three things an agent needs: how to inject credentials without an interactive prompt, how to consume structured output, and how to interpret exit codes deterministically. It ends with two sample LLM prompts you can use as system messages.
Why this matters
When a user asks an LLM to "log my POTA contact for K-1234," the LLM needs to issue real shell commands against a real API on the user's behalf. To do that safely, the integration surface must be:
- Stateless from the agent's perspective -- credentials come in via env var, not an interactive prompt the agent can't drive.
- Machine-readable -- both successful output and errors must be parseable without regex hacks.
- Stable -- exit codes and JSON shapes don't change across patch releases. SemVer applies.
The CLI is small enough (~10 commands) that you can paste hamtrax help --all into the agent's context and it can construct any valid command from that text alone. That's the bar.
Env-var auth
Set HAMTRAX_API_KEY before invoking the CLI. The CLI checks env first, then keychain, then the config file.
export HAMTRAX_API_KEY=htx_live_xxxxxxxxxxxxxxxxxxxxxxxx
hamtrax whoami --json
This is the only auth mechanism that works in headless, non-interactive contexts (CI, Docker, agent runtimes). See Auth and Keys → Resolution order.
In agent runtimes, store the key in your secret manager and inject it into the subprocess environment for each tool invocation -- never write it to disk.
--json output
Every command supports --json. The shape is stable:
- Mutations return a single object.
createreturns{ id, ... };deletereturns{ success: true }. Examples:
{ "id": "qso_92b1f4d2e8a0" }
{ "id": "fld_8a4f0c2b9e1d", "name": "N0CALL@K-1234-20260507", "autoFolderKey": "N0CALL@K-1234-20260507", "created": true }
{ "success": true }
- Reads that return a list use
{ items, cursor }. Thecursorfield is omitted when there's no next page.
{
"items": [
{ "id": "fld_8a4f0c2b9e1d", "name": "N0CALL@K-1234-20260507" },
{ "id": "fld_2c1a3d4e5f60", "name": "N0CALL@K-7777-20260423" }
],
"cursor": "fld_2c1a3d4e5f60"
}
- Errors (any non-zero exit) write a JSON envelope to stderr with the same shape the API returns:
{
"error": "tier_insufficient",
"message": "Requires 'elevated' tier.",
"details": { "required": "elevated" },
"requestId": "f9c0a1d4-..."
}
--ndjson output
For list commands, --ndjson emits one JSON object per line with no wrapping object. This is the right choice when an agent is streaming a long list into a transformation pipeline.
hamtrax contacts list --folder fld_8a4f0c2b9e1d --ndjson
# {"id":"qso_92b1...","callsign":"W1AW","mode":"FT8",...}
# {"id":"qso_aa12...","callsign":"K5ZD","mode":"SSB",...}
NDJSON is list-only; mutations use --json whether or not the agent asked for --ndjson.
Exit-code table
Errors map to deterministic exit codes. An agent should branch on the exit code, not parse the error message string.
| Exit | Server error code | Meaning | Recovery |
|---|---|---|---|
0 | -- | Success | Continue. |
1 | -- | Generic / uncategorized failure | Retry once; surface to the user if it persists. |
2 | -- | Invalid usage (bad flags, missing required args) | Re-read hamtrax help <command>; do NOT retry blindly. |
3 | unauthorized | Missing, malformed, or expired key | Prompt the user for a new key. Do NOT retry. |
3 | key_revoked | Key was revoked server-side | Prompt the user for a fresh key from the web app. Do NOT retry. |
4 | rate_limited | Rate-limit window exceeded | Wait Retry-After seconds (the CLI prints this), then retry. |
5 | tier_insufficient | Operation requires elevated; configured key is basic | Surface to the user; suggest generating an elevated key. |
6 | qso_cap_reached | Free-tier 200-QSO native cap hit | Surface to the user with the upgrade link printed by the CLI. Do NOT retry. |
7 | -- | Network / transport error (DNS, TLS, timeout) | Retry with exponential backoff up to 3 attempts; surface if all fail. |
8 | internal | Server-side 5xx | Retry once after a short delay; surface if it persists. |
Additional server error codes that map to specific exits:
| Server code | Maps to exit | Notes |
|---|---|---|
validation_error | 2 | The CLI usually catches these client-side, but the server is the backstop. |
not_found | 1 | Treat as "doesn't exist or not yours" -- both collapse to 404 by design. |
The CLI also writes errors to stderr in a stable line format, suitable for log scraping when JSON isn't appropriate:
hamtrax: contacts delete: tier_insufficient: Requires 'elevated' tier.
Sample LLM prompts
These two prompts are battle-tested starting points. Drop them into your agent's system message, set HAMTRAX_API_KEY in the agent's environment, and you have a working integration.
Prompt 1: One-shot logger
You are a logging assistant for the Hamtrax CLI. The user will describe a
contact they just made; you will log it.
Tools available:
- shell: run a single command, return stdout, stderr, and exit code.
Rules:
1. Always call commands with --json. Parse the JSON; do NOT guess output shape.
2. If the user does not specify a folder, find the in-progress activation:
hamtrax activations list --in-progress --limit 1 --json
If there is exactly one, use it. If there are zero, ASK the user before
creating an activation. If there are more than one, ASK which one.
3. Required fields for `contacts create`: --folder, --callsign, --frequency
(MHz, decimal), --mode. --time-on is required server-side but the CLI
defaults it to "now" when omitted. The server auto-populates POTA fields
(mySig, mySigInfo) when the folder is an activation; there are no flags
to set them yourself.
4. Exit-code handling:
- 0: success. Confirm the QSO id to the user.
- 3: tell the user the API key is invalid; do NOT retry.
- 5: tell the user this requires an elevated key; do NOT retry.
- 6: tell the user they hit the free-tier 200-QSO cap; do NOT retry.
- 4: parse Retry-After from the error and retry once after waiting.
- any other non-zero: surface the message; do NOT retry blindly.
5. NEVER reveal HAMTRAX_API_KEY in any output to the user.
Prompt 2: Activation runner
You are an activation copilot. The user is portable at a POTA site and will
dictate contacts as they work them. You log each one.
Workflow:
1. At the start of the session, ensure the activation folder exists:
hamtrax activations create --reference $REFERENCE --callsign $MY_CALL --json
This is idempotent -- safe to call again. Capture the .id field.
2. For each contact:
hamtrax contacts create --folder <id> --callsign X --frequency F --mode M --json
Confirm the QSO id back to the user.
3. Every 10 QSOs, run `hamtrax whoami --json` and tell the user their
nativeQsoCount. Free-tier cap is 200 -- warn at 180.
4. If exit 6 (qso_cap_reached) is returned, STOP logging and tell the
user to subscribe at https://hamtrax.com/account.
Reference: hamtrax help --all is available; consult it for any flag you
are unsure about. Do not invent flags that aren't in the help output.
Versioning contract
- The CLI is independently versioned (SemVer) from the API (
/v1/). - A patch release will not change exit codes or JSON shapes.
- A minor release may add new commands, flags, or output fields, but never remove or rename existing ones.
- A breaking change requires a major release AND an
/v2/API. hamtrax --help-jsonincludes bothcli_versionandapi_versionso an agent can pin its understanding to a specific release.
Related pages
- Command Reference -- every flag
- Auth and Keys -- env-var resolution order
- Examples -- worked end-to-end flows
- Troubleshooting -- common failure modes
- Security -- threat model and key hygiene