Wallet API Reference

Every PoH miner exposes an HTTP API on walletApiPort (default 3456). Balance responses are in μPOH (raw) — 1 POH = 1 000 000 000 μPOH. Input amounts for /api/wallet/send and staking endpoints are in POH (the server converts internally).

Base URL
http://127.0.0.1:3456

Public miners (e.g. miner.proofofhuman.ge) expose the same routes over HTTPS. CORS is open for browser clients.

Generate Wallet

A PoH wallet is an ed25519 keypair. The address is derived from the signing public key — no seed phrases, no custodians. Follow these four steps to create a wallet and start submitting jobs.

STEP 1 Generate the keypair

Run this Node.js script (requires tweetnaclnpm i tweetnacl). It deterministically derives an ed25519 signing keypair from 32 random bytes, then computes the canonical PoH address as 'poh' + sha256(signingPublicKey).slice(0,40).

// generate-wallet.js const crypto = require('crypto'); const nacl = require('tweetnacl'); function hexToUint8(hex) { const a = new Uint8Array(hex.length / 2); for (let i = 0; i < hex.length; i += 2) a[i/2] = parseInt(hex.slice(i, i+2), 16); return a; } const b64 = (u8) => Buffer.from(u8).toString('base64'); const sha256hex = (s) => crypto.createHash('sha256').update(s).digest('hex'); // 1. 32 random bytes → private key const privateKeyHex = crypto.randomBytes(32).toString('hex'); // 2. Deterministic ed25519 signing keypair // (same derivation as poh/wallet/src/services/signing.js) const seedHex = sha256hex(privateKeyHex + ':poh-ed25519-signing-v1'); const { publicKey, secretKey } = nacl.sign.keyPair.fromSeed(hexToUint8(seedHex)); const signingPublicKey = b64(publicKey); // 32-byte raw ed25519 pubkey in base64 const signingSecretKeyB64 = b64(secretKey); // 64-byte secretKey — keep secret // 3. Canonical address const address = 'poh' + sha256hex(signingPublicKey).slice(0, 40); // 4. Registration proof — ed25519 signature over the address string. // Required by /api/wallet/register-key (Step 2). const proof = b64(nacl.sign.detached(new TextEncoder().encode(address), secretKey)); const wallet = { address, privateKeyHex, signingPublicKey, signingSecretKeyB64, proof }; require('fs').writeFileSync('poh-wallet.json', JSON.stringify(wallet, null, 2)); console.log('Address:', address);
node generate-wallet.js # Address: pohaf4c36bfb8ee60d019251b613d1e5cd18a241d76

Keep poh-wallet.json secret. The signingSecretKeyB64 is the private signing key — never share or commit it.

STEP 2 Register the signing key on-chain

Post your address, signingPublicKey, and the proof from Step 1 to any miner. The proof is an ed25519 signature over your address — it proves you hold the private key. This binds your key to your address so the network can verify your job payment signatures.

curl -X POST https://miner.proofofhuman.ge/api/wallet/register-key \ -H 'Content-Type: application/json' \ -d '{ "address": "poh...", "signingPublicKey": "<signingPublicKey from poh-wallet.json>", "proof": "<proof from poh-wallet.json>" }' # Response: { "ok": true }

Missing the proof returns 400 { "error": "address, signingPublicKey, and proof required" }. An invalid proof returns 403 { "error": "invalid proof" }. To rotate to a new key later, include a rotationProof signed by your existing key.

Registration is free and only needs to happen once per keypair. It does not require any POH balance.

STEP 3 Fund the address

Compute and skill jobs require a signed paymentTx with sufficient budget. Fund your address via the P2P exchange, a transfer from another wallet, or a faucet. Check your balance at any time:

curl "https://miner.proofofhuman.ge/api/wallet/balance?address=poh..." # Response: { "balance": 5000000000 } # 5 000 000 000 μPOH = 5 POH (1 POH = 1 000 000 000 μPOH)
STEP 4 Submit your first compute job

Sign a payment proof and post to /job. The snippet below uses the wallet file from Step 1.

// submit-job.js (requires tweetnacl) const crypto = require('crypto'); const nacl = require('tweetnacl'); const wallet = JSON.parse(require('fs').readFileSync('poh-wallet.json')); const secretKey = new Uint8Array(Buffer.from(wallet.signingSecretKeyB64, 'base64')); const MINER = 'https://miner.proofofhuman.ge'; const sha256hex = (s) => crypto.createHash('sha256').update(s).digest('hex'); const sign = (data, sk) => { const msg = new TextEncoder().encode(typeof data === 'string' ? data : JSON.stringify(data)); return Buffer.from(nacl.sign.detached(msg, sk)).toString('base64'); }; async function run() { // 1. Get current nonce const { nonce } = await fetch(`${MINER}/api/wallet/nonce?address=${wallet.address}`) .then(r => r.json()); // 2. Build payment proof const maxBudget = 500_000_000; // 0.5 POH in μPOH const timestamp = Date.now(); const payload = JSON.stringify({ from: wallet.address, to: 'miner', amount: maxBudget, fee: 0, nonce: nonce + 1, timestamp, memo: 'compute:llama3.1:8b' }); const txHash = sha256hex(payload); const signature = sign(txHash, secretKey); // 3. Submit job const res = await fetch(`${MINER}/job`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'compute', model: 'llama3.1:8b', payload: { prompt: 'Summarise the PoH network in one sentence.' }, maxBudget, requesterAddress: wallet.address, paymentTx: { txHash, signature, nonce: nonce + 1, timestamp } }) }).then(r => r.json()); console.log('Job submitted:', res.jobId); // 4. Poll for result while (true) { await new Promise(r => setTimeout(r, 2000)); const { status } = await fetch(`${MINER}/job/${res.jobId}/status`).then(r => r.json()); if (status === 'done') { const result = await fetch(`${MINER}/job/${res.jobId}/result`).then(r => r.json()); console.log('Result:', result.output); break; } if (status === 'error') { console.error('Job failed'); break; } console.log('Status:', status); } } run();
Quick reference
1Generate keypair & address locally — no network needed
2POST /api/wallet/register-key — once, free, any miner
3Fund address — GET /api/wallet/balance to verify
4POST /job with signed paymentTx — poll /job/:id/status

Chain & Wallet

GET /status

Node status — chain height, sync state, reputation.

curl http://127.0.0.1:3456/status
GET /api/miner/info

Version, uptime, peer count, wallet address.

GET /api/wallet/balance?address=<addr>

Balance in μPOH for a PoH wallet address.

curl "http://127.0.0.1:3456/api/wallet/balance?address=poh..."
GET /api/wallet/nonce?address=<addr>

Current nonce for transaction / payment signing.

GET /api/wallet/transactions?address=<addr>

POH transfer history.

GET /api/wallet/history?address=<addr>

Full history with block confirmations.

GET /api/wallet/jobs?address=<addr>&limit=20

Public job history — joins job-submitted transitions with mined scanResults. Returns { jobs, latestSkillMemory, chatTurns }.

POST /api/wallet/register-key

Register an ed25519 signing key for an address. Required before signed job payments. See Generate Wallet for how to produce the proof. proof = ed25519 signature over the address string.

{ "address": "poh...", "signingPublicKey": "base64...", "proof": "base64...", "rotationProof": "base64 (only when changing an existing key)" }
POST /api/wallet/send

Transfer POH between wallets. The sender wallet must be stored on this node (loaded via walletManager). Amount is in POH (not μPOH). Optional fee and memo fields.

{ "from": "poh...", "to": "poh...", "amount": 1, "memo": "optional" }
POST /api/tx/submit

Submit a pre-signed PoHTransaction to the mempool.

GET /api/tx/pending

Inspect pending mempool transactions.

GET /api/chain/supply-audit

Chain supply audit — total issued vs. accounted balances.

Jobs

Submit compute work to the network. Types: verdict (identity scan, free), skill, compute. skill and compute jobs require a signed payment proof (maxBudget in μPOH).

Pricing & fees

Paid jobs are metered per AI compute token at 1 μPOH per token (the default gasPrice; μPOH is the smallest unit, so this is the floor). Since 1 POH = 1 000 000 000 μPOH, one POH buys up to 1 billion tokens.

The fee is tokensUsed × gasPrice and is never less than the tokens actually used (1 000 tokens ⇒ ≥ 1 000 μPOH). Unused budget is refunded. A maxBudget too small to cover the job's token cost is rejected with 402 FEE_TOO_LOW.

Block reward. On top of the fee, each block's ~1 POH subsidy is split among the workers who completed jobs, weighted by delivered compute (a deterministic token estimate — splitting into many nodes earns nothing extra), minus a small (~10%) proposer cut. Blocks with no real work mint only a small keepalive, so emission tracks demand.

POH has no fixed price — the market defines it via the best open P2P order (see GET /api/p2p/price).

POST /job

Submit a job. Returns { jobId, statusUrl, resultUrl }. paymentTx.txHash is a SHA-256 of { jobId, requesterAddress, minerAddress, amount, nonce } — not a blockchain transaction hash.

{ "type": "compute", "model": "llama3.1:8b", "payload": { "prompt": "Summarize the dataset" }, "maxBudget": 500000000, "requesterAddress": "poh...", "paymentTx": { "txHash": "<computeJobPaymentHash>", "signature": "base64..." } }
GET /job/:id/status

Poll job state: queued, computing, done, error.

GET /job/:id/result

Full result when the job is done.

GET /jobs

List active jobs on this node.

POST /gossip

Receive P2P gossip envelopes from peer miners.

Chat & Routing

POST /chat/route

Route a message to the best matching skill.

{ "message": "audit this skill code", "budget": 0, "wallet": "poh..." }
POST /chat/ask

Full AI reply — routes to a skill or free chat. May return { fromChainHistory: true } for cached on-chain replies.

{ "message": "What is my on-chain reputation?", "wallet": "poh...", "requesterAddress": "poh...", "history": [] }
Private vs public context
Private chat uses in-memory session history. Public (paid) jobs also pull on-chain history via /api/wallet/jobs so follow-ups work across devices.

Blockchain Explorer

GET /api/explorer/blocks?page=0&limit=20

Recent blocks (newest first). Each entry includes jobCount when the block contains mined results.

GET /api/explorer/block/:height

Full block JSON — scanResults, stateTransitions, hash, miner, reward.

GET /api/explorer/search?q=<query>

Lookup by block height, tx/block hash, or wallet address. Address search returns balance, transfers, and completed jobs.

Skills

On-demand agent modules. Deploy a public skill for 100 POH; community staking reaches 1,000 POH to graduate a skill network-wide.

GET /api/skills

List skills with staking totals and economics: { proposeFeePoh, graduationThresholdPoh }.

curl https://miner.proofofhuman.ge/api/skills
GET /api/skills/prefs?address=<addr>

Skill enable/disable and staking preferences for a wallet.

POST /api/skills/propose

Propose a new skill. Public proposals cost 100 POH (escrowed for code_audit).

{ "manifest": { ... }, "code": "...", "private": false }
POST /api/skills/:id/stake

Stake POH toward skill graduation. Amount is in POH (not μPOH). Uses the node's own wallet.

{ "amount": 1000 }
POST /api/skills/:id/unstake

Withdraw staked POH from a proposed skill.

POST /api/skills/:id/enable  |  /disable

Toggle whether this node runs a skill.

HF Datasets

Hugging Face datasets installed on this node. Compute jobs can reference them by ID. Chat will suggest installs when a dataset is missing. Datasets are stored under ~/.poh-miner/brain-data/hf-datasets/.

GET /api/hf-dataset

List installed datasets. Returns { datasets: [{ id, files, rowCount, source }] }.

POST /api/hf-dataset/:id/download

Install a dataset by Hugging Face ID. Falls back to pulling from a peer miner if HF is unreachable. Returns { ok: true, manifest } on success.

curl -X POST http://127.0.0.1:3456/api/hf-dataset/imdb/download
DELETE /api/hf-dataset/:id

Remove an installed dataset from this node.

LLM & Brain

Inference runs in-process via QVAC. Brain state stores human feedback and signal weights.

POST /api/chat

Streaming chat via local QVAC (Ollama-compatible body).

POST /api/generate

QVAC generate endpoint (Ollama-compatible body).

GET /api/models

Models available on this node (built-in + loaded + registry).

GET /api/network-models

Aggregated model list from connected peers.

GET /api/brain/state

Weights count, feedback count, model info.

GET /api/brain/weights

Full weights.json for the identity brain.

POST /api/brain/feedback

Submit a human correction to improve signal weights.

{ "address": "poh...", "aiVerdict": "human", "correction": "bot", "comment": "obvious bot pattern", "signals": [{ "methodId": "...", "result": true }] }
POST /api/brain/vote

Vote on signal weight adjustments.

P2P Exchange

Peer-to-peer POH trading with on-chain escrow.

GET /api/p2p/currencies

Supported quote currencies.

GET /api/p2p/price?quoteCurrency=<cur>

Reference POH price — the market price derived only from the best open P2P order(s): { price (mid), bestBid, bestAsk, source }. Omit quoteCurrency for a per-currency map. POH has no oracle/fixed price.

GET /api/p2p/orders

Open order book. Filters: side, quoteCurrency.

GET /api/p2p/orders/my?address=<addr>

Orders created by this address.

POST /api/p2p/orders

Create a buy or sell order.

POST /api/p2p/orders/:id/cancel

Cancel an open order.

POST /api/p2p/orders/:id/select

Taker selects an order to begin a trade.

GET /api/p2p/trades/my?address=<addr>

Active and completed trades for an address.

POST /api/p2p/trades/:id/:action

Advance trade: payment-sent, release, cancel, dispute.

Push Notifications

POST /api/push/register

Register an Expo push token for a wallet address.

{ "address": "poh...", "token": "ExponentPushToken[...]" }
POST /api/push/send

Send a push notification. Optional address filter.

Errors & Auth

Responses are JSON. Errors return { "error": "message" } with an appropriate HTTP status (400, 401, 402, 404, 500).

Payment-required (402): skill/compute jobs without a valid signed paymentTx are rejected before execution.

Signing: register your ed25519 key via /api/wallet/register-key, then sign payment proofs binding jobId + minerWallet + maxBudget + nonce.

SDKs: use runCompute() / submitJob() in poh-miner SDK to handle signing automatically.