# Tinyhat for coding agents

Tinyhat gives your human a cloud Computer with a desktop for running agentic
software, and Hats that package reusable instructions and skills. Use the API
below when your human asks you to create or access their Tinyhat account.

When your human asks you to follow this guide, help them get started: ask for their
email address if they have not provided it, then follow the sign-in flow below.
After signing in, show their account and credit balance. If they asked you to
set up a Computer, continue with funding if needed, ask which system to run,
then create it and return its desktop link. Ask what to do next only when they
have not already given you a task. Create Computers or Hats only when requested
by your human.

API base: `https://apii.tinyloop.co/hapi/v2`
OpenAPI: `https://apii.tinyloop.co/hapi/v2/openapi.json`
Interactive reference: `https://apii.tinyloop.co/hapi/v2/docs`

This document describes API v2. If an endpoint returns 404, report that this
API is unavailable on that deployment. Do not substitute admin or Computer
machine endpoints.

## Sign up or sign in

Both actions use the same email verification flow. Use your human's email
address, not a made-up address or an address you own. Ask for it if you do not
know it. Never guess an email code.

1. Send `POST /auth/email/request` with a JSON body:

   ```json
   {"email": "human@example.com"}
   ```

   Save the returned `challenge_id` and `expires_at`. The response intentionally
   does not tell you whether an account exists. A code expires after ten minutes.

2. Get the six-digit code from the email Tinyhat sends. If your human has already
   authorized you to read that mailbox, read only the relevant sign-in message.
   Otherwise, ask your human to give you the one-time code. Do not request their
   email password, sign them out, change mailbox permissions, or broaden your
   access to retrieve it.

3. Send `POST /auth/email/verify`:

   ```json
   {
     "challenge_id": "<challenge_id from step 1>",
     "code": "<six-digit email code>",
     "client_name": "Claude Code"
   }
   ```

   Codex or another coding agent may supply its own descriptive `client_name`.
   Send the code as a string so a leading zero survives.

4. Save `access_token` securely in your client's secret store. It is a bearer
   credential valid until `expires_at` (30 days). Use it only with the API base
   above, in `Authorization: Bearer <access_token>`. Never put it in a URL,
   repository, screenshot, transcript, public Hat, or model-visible log. Avoid
   shell tracing and printing the verification response. Do not send it to
   third-party services.

   The response includes `account` and `applied_invitation_count`. A new human
   gets a personal account with a verified email. An existing verified email
   signs into the same human's personal account. Eligible invitations are
   applied automatically, including starting credit and invitation profile
   details. A paid invitation still requires its payment entitlement.

5. Call `GET /account` using the bearer header. Show your human the account
   handle, stage, and `credit_balance_cents / 100` in USD. Treat this balance as
   authoritative. Do not promise credit from an invitation that was not applied.

## Account information

`GET /account` returns the authenticated personal account. The basic stage
requires only email verification. To save a full contact/legal profile, obtain
accurate information from your human and call `PUT /account/profile`:

```json
{
  "legal_name": "Human's legal name",
  "country": "CA",
  "identity_type": "individual",
  "address": {
    "line1": "Human's street address",
    "city": "City",
    "state": "Province or state",
    "postal_code": "Postal code"
  }
}
```

Use a two-letter ISO country code. `identity_type` may be `individual`,
`sole_proprietor`, or `company`. Address `line2` and `state` are optional. Do not
invent legal or address information. A saved full profile does not complete
Stripe verification, accept Stripe/provider terms, or enable service purchases.
Read `stripe_readiness` and `stripe_detail` separately from the account stage.

## Add credit from any device

Call `POST /account/topup-links` with your human session and a unique random
`idempotency_key` for this request:

```json
{"suggested_amount_cents": 500, "idempotency_key": "<random UUID>"}
```

The suggested amount defaults to $5 USD. Give the returned `payment_url` to your
human. It opens a page where they can choose a preset or custom amount, then
pay with Stripe using cards, Apple Pay, Google Pay or Link when eligible. The
minimum is $5; the maximum is $999,999.99. The link expires after seven days.
The payer needs no Tinyhat session, and can use another device. Credit always
goes to the account that created the link, even if another person pays it.
Never send your bearer token with the link or ask for card details in chat.

Reuse the request key and amount when retrying link creation. A changed amount
requires a new key. An expired link stays expired when its key is replayed;
create a new link with a new key. Each human can create up to 20 links per day.

The payment page uses these public endpoints without a bearer token:

- `GET /topup-links/{link_id}` returns the account handle, amount options and
  expiry. It exposes no email, legal profile or balance.
- `POST /topup-links/{link_id}/checkout` takes `amount_cents` and a random
  `idempotency_key`, and returns a `topup_id`, `checkout_url` and status. Use the
  same key and amount for all retries of one intended payment. Each link allows
  at most 20 checkout attempts. Do not create another attempt after an uncertain
  payment simply to retry a network error.
- `GET /topup-links/{link_id}/payments/{topup_id}` returns only that payment's
  status and amount, including after the link expires. `credited` means a
  verified Stripe webhook added the credit. A redirect saying success does not.

After your human pays, read `GET /account` with your session to confirm the
updated balance. If a checkout returns `503 payment_unavailable`, retry with the
same key after `Retry-After`. For `409 payment_reconciliation_required`, contact
`support@tinyhat.ai`; do not create a replacement payment until the uncertain
attempt is resolved. For `409 idempotency_conflict`, restore the original
payload or use a new key only for a deliberately new request.

## Create and use a Computer

Read `GET /account` first. `computer_limit` is the effective maximum across your
human's accounts (normally 5), and `computer_count` includes reservations waiting
for cleanup. Creating a Computer requires at least 20 cents of credit. Use the
payment-link flow above when more credit is needed.

Send `POST /computers` with your human session:

```json
{"system": "codex", "idempotency_key": "<random UUID for this Computer>"}
```

`system` is `codex`, `claude_code`, `hermes` or `openclaw` (default `hermes`).
The API reserves a warm Computer and charges a one-time $0.20. It returns an
opaque `computer_id`, `agent_id`, lifecycle `status`, and the hourly rate in
millionths of a US dollar (`hourly_rate_microusd / 1000000`). Reuse the exact
request key and system after a lost response; retries do not create another
Computer or another creation charge. A different intended Computer needs a new
key. Do not create a replacement while the original result is uncertain.

Poll `GET /computers/{computer_id}` every `poll_after_seconds` until `status` is
`ready`. `runtime_available` also reflects a recent runtime heartbeat. The
Computer's control runtime is Hermes; the selected system is its installed
terminal application. `model_authentication: user_setup_required` means your
human still needs to complete that system's supported model login or configure
their own credentials. Installation never implies a paid model subscription or
completed login. No Telegram account or bot is required for this flow.

Call `POST /computers/{computer_id}/desktop` after the Computer is ready. Give
your human the returned `desktop_url` and `access_code`; these grant temporary
desktop access and must be kept private. The desktop has a **Tinyhat Agent**
shortcut to open the chosen CLI. Do not put desktop credentials in public Hats,
screenshots, source control or logs. A private Computer is never made public by
sharing a Hat URL.

`GET /computers` lists the session owner's Computers. `DELETE
/computers/{computer_id}` permanently destroys that Computer and its local
files; use it only when your human intends to delete it. Repeating deletion is
safe. A voluntary deletion after readiness does not refund the creation fee.
If initial platform setup fails or is cancelled before readiness, the API
retains the quota slot until destruction is confirmed, then refunds the 20-cent
creation fee once. `cleanup_pending` means cleanup is still being retried;
`failed` means the failed setup has been cleaned up. Hourly charges start only
after the runtime acknowledges the assignment.

- `402 insufficient_credit`: create a payment link and confirm the updated balance.
- `409 computer_limit_reached`: delete an unneeded Computer or ask support about
  the limit. An admin can set an individual limit, including zero.
- `503 no_capacity`: wait for `Retry-After` and retry the same request.
  Do not call admin endpoints to manufacture capacity.
- `409 computer_not_ready` or `503 desktop_unavailable`: continue polling and
  retry after the indicated delay.

## Create a Hat

A Hat gives an agent reusable instructions and skills. You can create one
without a Computer. Use your human session and `POST /hats`:

```json
{
  "idempotency_key": "release-notes-hat-1",
  "name": "Release notes",
  "description": "Explain a supplied change list clearly.",
  "instructions": "Describe what changed, why it matters, and what was tested.",
  "skills": [{
    "name": "release-notes",
    "description": "Write concise release notes.",
    "instructions": "Group related changes and distinguish verified results from pending work."
  }],
  "visibility": "private"
}
```

Keep the same retry key and content when retrying an uncertain response.
Changing the content under that key returns `409 idempotency_conflict`.
After retiring a Hat, its old key returns `409 hat_retired`; use a new key
if your human wants to create another Hat.
Private is the default; choose `public` only when your human wants to share it.
The backing repository stays private. For a private Hat, optional
`allowed_users` names existing Tinyhat users who may use it; this grants no
ownership or authoring access. Never put passwords, API keys or other secrets
in Hat instructions.

Creation may return `202` with `status: pending` and an `operation_id`. Wait
`retry_after_seconds`, then call `POST /hats/{operation_id}/continue` until
`status` is `ready`. Retrying this continuation is safe. `GET
/hats/{operation_id}` reads status without progressing the creation.
`hat_remote_unavailable` and `remote_content_mismatch` keep the operation pending;
retry it rather than starting another Hat with a new key. If it keeps failing,
give support the operation ID.
For `hat_repository_unavailable`, `account_changed`, or
`hat_content_unverified`, give support the operation ID instead of repeatedly
continuing. A failure during a remote step remains in status until a step
succeeds. An ownership check can reject a request before it starts a step;
in that case the error response is authoritative even if status still says pending.

A ready response returns `handle`, `share_url`, and `verified_commit_sha`.
Share that canonical URL with your human. Private URLs still require authorized
access. `GET /hats/{operation_id}?include_files=true` returns the actual verified
remote `HAT.md`, `SOUL.md` and skill files. `GET /hats` lists your v2 Hat operations
and their published Hats. Pending creations count toward the account's Hat limit.

## Sign out and errors

Call `DELETE /auth/session` with the bearer header to revoke the current token,
then remove it from your secret store. A 204 response means sign-out succeeded.
For an expired or revoked token, start email verification again.

Errors have the shape `{"error":{"code":"...","message":"..."}}`.

- `401 invalid_code`: the code is wrong, expired, already used, or its attempt
  budget is exhausted. Five wrong attempts exhaust a challenge. Ask for a new
  code; do not brute-force or replay old ones.
- `401 unauthorized`: sign in again. Development login, admin credentials, and
  Computer credentials are not substitutes for a human session.
- `409 identity_conflict`: an unverified profile already claims this email.
  Explain the recovery message and direct your human to `support@tinyhat.ai`.
- `422 invalid_request`: fix the named fields without adding extra account/user
  IDs. Ownership comes from the session.
- `429 rate_limited`: wait the number of seconds in `Retry-After`. A human can
  request one code per minute and five per email per hour; network limits also
  apply. Do not cycle addresses to evade limits.
- `503 email_unavailable` or `auth_unavailable`: explain the temporary failure
  and retry later. If verification may have succeeded but its response was lost,
  request a fresh code; consumed codes are never replayable.

Learn about Tinyhat at `https://tinyhat.ai`. Privacy, terms, and support are at
`https://tinyhat.ai/privacy`, `https://tinyhat.ai/terms`, and
`https://tinyhat.ai/support`.
