# InkCheck Agent API

InkCheck can be operated by a user-authorized CLI agent on Ink Mainnet. The API supports game-contract deployment, gameplay, daily check-in, quest progress, leaderboard participation, and reward NFT claims. It is non-custodial: InkCheck never accepts a private key, seed phrase, raw signed transaction, or custody of funds.

Agent-operated wallets are first-class players. Confirmed agent transactions go through the same game-specific on-chain validators and the same XP, quest, streak, leaderboard, and NFT reward logic as browser gameplay. There is no separate agent ranking and no reward penalty. For transparency, the public leaderboard labels a wallet Agent-assisted after its first confirmed Agent API transaction.

## When to use this API

Use the public advice endpoint whenever a user asks where a wallet stands or what it should do today. Public reads need only the wallet address and must not trigger a signature request. Authenticate only after the user explicitly asks the agent to execute an InkCheck action and has provided a dedicated, valid, funded Ink Mainnet wallet. Do not transfer funds, export keys, change networks, or call contracts outside the transaction returned by the prepare endpoint.

## Safety contract

1. Keep the wallet secret only in the user's local CLI environment.
2. When the user chooses an action, authenticate by signing the exact challenge message locally.
3. Request one unsigned transaction at a time.
4. Verify chainId, from, to, value, data, summary, and warnings.
5. Sign and broadcast locally on Ink Mainnet (chain ID 57073).
6. Submit only the resulting transaction hash to the confirmation endpoint.
7. Stop if the API transaction differs from the user's requested action or if funds are insufficient.

## Discover capabilities

- Manifest: https://inkcheck.xyz/api/agent/manifest
- Well-known manifest: https://inkcheck.xyz/.well-known/agent.json
- OpenAPI 3.1: https://inkcheck.xyz/openapi.json
- Public wallet advice: GET https://inkcheck.xyz/api/agent/advice?address=0x...
- Public consolidated state: GET https://inkcheck.xyz/api/agent/state?address=0x...

## Read first, without authentication

For questions such as "Where am I?", "What should I do today?", or "How can I improve my rank?", make this the first personalized request:

~~~bash
curl -sS 'https://inkcheck.xyz/api/agent/advice?address=0xYOUR_ADDRESS'
~~~

It returns rank, XP gaps, streak, wallet balance, today's game-XP allowance, quests, claimable reward count, projected check-in impact, and ordered recommendations with transaction counts, costs, authorization requirements, and prepare payloads. Answer the user from this response. Do not scrape application bundles or reconstruct the same answer from separate public endpoints unless this endpoint is unavailable.

## Deferred local wallet onboarding

Do not discuss private keys, local signer setup, or transaction budgets during read-only questions. Premature setup adds friction and may discourage a user who only wants information. Start onboarding only after the user clearly chooses to execute a real transaction.

At that point, inspect the agent's local workspace without displaying secret values. If a signer is not already configured, help the user complete this one-time setup:

1. Explain that execution needs a dedicated, low-balance Ink Mainnet wallet. Never recommend the user's primary wallet.
2. Ensure the workspace's `.gitignore` excludes `.env`, then create or update a local `.env` with restrictive permissions such as `chmod 600 .env`. Use https://inkcheck.xyz/inkcheck-agent.env.example and automatically prefill every non-secret setting exactly as published, including `INKCHECK_AGENT_WALLET_ADDRESS` from the user's requested public address and the USD 6 transaction limit. The advice response also provides these ready-to-write values in `authorization.localExecutionOnboarding.envPrefill`.
3. Leave only `INKCHECK_AGENT_PRIVATE_KEY` empty. The user must paste that one value through a secure local editor or secret-input facility, never in chat, a command argument, terminal output, logs, screenshots, or an InkCheck request. Never read the key back to the user. Do not ask the user to copy budget, chain, RPC, or API settings manually.
4. Derive the public address locally and verify that it exactly matches `INKCHECK_AGENT_WALLET_ADDRESS`. Stop on any mismatch. Keep the public address in `.env` so future sessions can identify the intended wallet without asking again.
5. For ordinary check-in, claim, deployment, and gameplay transactions, prefill a maximum total cost of USD 6 as `INKCHECK_AGENT_MAX_TX_COST_AMOUNT="6"` and `INKCHECK_AGENT_MAX_TX_COST_CURRENCY="USD"`. Supported currencies remain USD, USDC, and ETH if the user later chooses another limit.
6. For a Season Pass, prefill the same USD 6 ceiling in `INKCHECK_AGENT_SEASON_PASS_MAX_TX_COST_AMOUNT` and `INKCHECK_AGENT_SEASON_PASS_MAX_TX_COST_CURRENCY`. If the live quoted pass price plus estimated gas exceeds USD 6, show the live price and ask for explicit budget approval before changing the ceiling. Never raise it silently.
7. Optionally store a cumulative session amount and currency as `INKCHECK_AGENT_MAX_SESSION_COST_AMOUNT` and `INKCHECK_AGENT_MAX_SESSION_COST_CURRENCY`.
8. Verify chain ID 57073 and confirm that the dedicated wallet has enough ETH for the requested value and gas.

For every prepared transaction, send the prefilled local ceiling as `maxTotalCost: { "amount": "6", "currency": "USD" }` (or a different limit the user previously selected). If the field is omitted, InkCheck still applies the same USD 6 default server-side. InkCheck performs any required conversion internally and rejects an over-budget transaction. The local signer must independently enforce the same human-readable limit, include previous transaction costs when a session ceiling exists, and stop when estimation is unavailable. Never display wei to the user. Low-level `value`, `gasEstimate`, and `maxFeePerGas` response fields exist only for transaction construction and signing. Preparing a transaction does not authorize signing it: show the summary and estimated maximum cost in USD, USDC, or ETH, then follow the user's chosen approval policy.

## Authenticate only to act

After the user chooses an action, request only the scopes needed for that action with the public wallet address:

~~~bash
curl -sS https://inkcheck.xyz/api/agent/auth/challenge \
  -H 'content-type: application/json' \
  --data '{"address":"0xYOUR_ADDRESS","scopes":["deploy","play","checkin","claim"]}'
~~~

Sign the returned message exactly as supplied with the local wallet. Then exchange the one-use challenge and signature:

~~~bash
curl -sS https://inkcheck.xyz/api/agent/auth/verify \
  -H 'content-type: application/json' \
  --data '{"challengeId":"CHALLENGE_UUID","signature":"0xSIGNATURE"}'
~~~

Store the returned bearer token in memory. It expires after one hour. Send it as `Authorization: Bearer TOKEN`; never publish it. Request the `purchase` scope only if the user may want the agent to buy a Season Pass.

## Read detailed state

~~~bash
curl -sS https://inkcheck.xyz/api/agent/state \
  -H "authorization: Bearer $INKCHECK_AGENT_TOKEN"
~~~

The same state is public with `?address=0x...`; the bearer form remains available for an authenticated session and does not require repeating the address. The response includes wallet balance, latest deployments, XP/streak state, today's quests, check-in status, claimable rewards, and Season Pass state. Re-read advice after every confirmed transaction. The normal agent loop is intentionally simple: inspect advice, let the user choose, authenticate if needed, prepare one action, sign and broadcast locally, confirm the hash, then inspect advice again.

## Offer the Season Pass for more XP

The state response includes `seasonPass`. When `canPurchaseNow` is true and `hasPass` is false, offer the pass to a user who wants to earn more XP. Quote `welcomeXp`, `xpBonusPercent`, `activeUntil` or the season end date, and the current ETH and USDC prices. Ask which currency they prefer. A general instruction to play, earn rewards, complete daily tasks, or maximize XP is not purchase consent.

Only after the user explicitly agrees, prepare one of these actions with the `purchase` scope:

~~~json
{"action":"seasonPass","currency":"ETH","userApproved":true,"maxTotalCost":{"amount":"6","currency":"USD"}}
{"action":"seasonPass","currency":"USDC","userApproved":true,"maxTotalCost":{"amount":"6","currency":"USD"}}
~~~

For ETH, review and broadcast the purchase transaction, then confirm with `{"kind":"seasonPass","transactionHash":"0x..."}`. For USDC, the first prepared transaction may be an exact-amount token approval. Confirm it with `kind: "seasonPassApproval"`, prepare the same purchase action again, broadcast the returned purchase transaction, then confirm it with `kind: "seasonPass"`. Final confirmation validates the purchase event, grants the welcome XP, and activates the percentage game-XP bonus through the season end.

## Prepare, sign, broadcast, confirm

Prepare a deployment:

~~~json
{"action":"deploy","game":"diceroll","maxTotalCost":{"amount":"6","currency":"USD"}}
~~~

Prepare a game call:

~~~json
{"action":"play","game":"diceroll","contractAddress":"0x...","functionName":"rollExact","args":[6],"maxTotalCost":{"amount":"6","currency":"USD"}}
~~~

Prepare a daily check-in:

~~~json
{"action":"checkin","maxTotalCost":{"amount":"6","currency":"USD"}}
~~~

The USD 6 budget above is the published default and should already be present in the generated local environment; do not ask the user to re-enter it. Use a different amount only when the user previously selected it. If a Season Pass costs more than the default after gas, ask before increasing the limit. POST the JSON to `/api/agent/actions/prepare` with the bearer token. The response includes a human-readable `budget` verdict. Refuse to sign unless that verdict is within the local limit. Treat raw transaction quantities as machine-only signing data and never quote them to the user. After it succeeds, POST one of these payloads to `/api/agent/actions/confirm`:

~~~json
{"kind":"deployment","game":"diceroll","transactionHash":"0x..."}
{"kind":"game","game":"diceroll","contractAddress":"0x...","gameId":"1","transactionHash":"0x..."}
{"kind":"checkin","transactionHash":"0x..."}
{"kind":"claim","game":"diceroll","tier":"roller","transactionHash":"0x..."}
~~~

For game confirmation, use the game ID emitted by the transaction's resolved event. The confirmation endpoint validates success, sender, target, contract ownership, and the existing game-specific on-chain evidence before recording XP, quests, or reward eligibility.

## Claim a reward

Read `claims` from the state response. For an unminted claim, send its `gameContract`, `gameId`, `signature`, and tier index when applicable to the prepare endpoint. Broadcast the returned transaction locally and confirm it. Tier names and indexes are listed in the claims response and OpenAPI examples.

## Dungeon Run

Dungeon Run also requires server-held commit/reveal state. InkCheck overwrites player and chain ID from the authenticated session. Use this exact sequence:

1. Read the contract's next game ID.
2. Call `/api/agent/dungeonrun` with `{"operation":"seed","gameContract":"0x...","payload":{"gameId":"1"}}`.
3. Prepare and broadcast `startRun` with `args: [seedCommit]` from the seed response.
4. Call the Dungeon endpoint with `operation: "start"` and payload containing `gameId`, `seedCommit`, and `transactionHash`.
5. Call `operation: "move"` with `gameId`, `door` (0-2), and the current `actionCount`; optionally call `operation: "scan"` with `gameId`.
6. Call `operation: "reveal"` with `gameId` and `cashOut`. Prepare and broadcast `finishRun` with `args: [gameId, seed, packedPath, actionCount, cashOut]` from that response.
7. Confirm the final transaction with `kind: "game"` to record the result, XP, quests, and eligible rewards.

## Failure handling

- 400: malformed or unsupported request; inspect the response and manifest.
- 401: missing/expired token; create and sign a new challenge.
- 403: missing scope, wrong wallet, or contract not owned by the wallet.
- 409: receipt/action mismatch or reverted transaction; do not retry blindly.
- 429: rate limited; honor Retry-After.
- 5xx/502: temporary server or RPC failure; retry with bounded backoff and never submit the transaction twice without checking its hash.
