Integrate with Keeper HoldingsSandbox
Everything a partner, a Keeper business or an AI agent needs to build against the sandbox without talking to anyone. Base Sepolia, test USDC, no accounts to request.
Quick start
Three requests that need no sign-in. Then pick the flow for your role below.
curl https://keeperholdings.io/v1/skus
curl https://keeperholdings.io/v1/hold/quote?sku=nvidia-rtx-4090&months=3
curl -X POST https://keeperholdings.io/mcp -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Sign in with a wallet
There are no accounts to request. Any wallet on Base Sepolia is a sandbox credential: call GET /v1/auth/nonce, sign the EIP-4361 message with personal_sign, post it to POST /v1/auth/verify. The response carries a bearer token (also set as the kh_s cookie) valid for 7 days. Use it as Authorization: Bearer … on the routes marked signed-in.
Keeper flow
- Sign in, then
POST /v1/keeper/applywithdisplay_nameandregion(ISO country-region, e.g.JP-13). - Post a bond:
USDC.approve(bond, amount)thenKeeperBond.deposit(amount). Status becomes active once the bond is on chain. Coverage must stay at or above 110% of held value. - When an owner picks you, the receipt appears in
GET /v1/keeper/measINTAKE_PENDING. Confirm arrival with a photo:POST /v1/receipts/{id}/intake(multipartphoto). The Worker checks the photo, stores it privately, hashes it, and returns the signed intake attestation. - After the owner mints, call
CustodyReceipt.acknowledge(id); the receipt isHELDand the fee schedule starts. - Every period:
POST /v1/receipts/{id}/proofreturns a one-time code; photograph it next to the sealed item andPOST /v1/proofs/{pr}/submit. Working on a computer?POST /v1/capture/tokenwith{ "kind": "intake" | "proof", "id": "kr_… | pr_…" }returns a 30-minute URL (/capture/{kind}/{id}?t=…, also as a QR SVG) that a phone opens without any wallet; the phone uploads to the same two routes with?t=in place of a session. The token is bound to that one receipt or proof request and is never written anywhere. A pass returns a signedProofAttestationplusreleaseOnProofcalldata (also listed underpending_releaseinGET /v1/keeper/meuntil it is sent); anyone can send it and one period of fees is paidto you. Low-confidence checks go toQUARANTINEDfor review. - On a delivery request, ship and call
markShipped(id, trackingHash).
Owner flow
GET /v1/hold/quote?sku=…&months=…for tier, proof interval, per-period fee and prepay.- Sign in and
POST /v1/hold/requestwithsku_id,keeper_idand optionally a serial (stored only as a hash). Ship the sealed item to the Keeper. - When the Keeper confirms arrival the receipt is
VERIFIEDandGET /v1/hold/minecarries the intake attestation and ready-mademintcalldata. Approve USDC for the escrow, thenCustodyReceipt.mint(attestation, signature, periods); the prepaid fees sit inFeeEscrowand travel with the receipt. - Redeem with
requestRedemption(id); after shipping,close(id)burns the receipt and refunds unreleased fees. - Sell: approve the Settlement contract for the receipt, sign an EIP-712
OfferandPOST /v1/market/offers. A buyer fills it atomically.
Agents: MCP and x402
The MCP server at https://keeperholdings.io/mcp (JSON-RPC 2.0, Streamable HTTP, no SSE) exposes read tools for free: parts, prices, quotes, Keepers, receipts, offers, attestations, chain info and calldata preparation. index_live and GET /v1/index/{sku}/live are paid with x402 v2: 0.01 test USDC on Base Sepolia, facilitator https://x402.org/facilitator. An agent with a funded wallet can quote custody, open a hold, pay fees and buy or sell receipts with nothing but the API and its own signatures.
{ "mcpServers": { "keeper-holdings": { "url": "https://keeperholdings.io/mcp" } } }Signed attestations
The Worker is the index oracle. GET /v1/attestations returns EIP-712 IndexAttestation messages (sku as keccak256 of the id, price in USDC atomic units, tier, validity window) signed by the attester 0xc948f48082BAE908D803edC53ec1F6753bD056EB. The same key signs IntakeAttestation (what mint verifies) and ProofAttestation (what releaseOnProof verifies). Domain: {"name":"Keeper Holdings","version":"1","chainId":84532,"verifyingContract":"0x8e90A4C0b9EC75aeFA76853B6ccBEa7489B24c08"}.
Contracts (Base Sepolia, chain id 84532)
Every contract refuses any chain id other than 84532 (and 31337 for local anvil). Transfers of receipts are allowed only in HELD and only while transfersOpen is true (sandbox: on; production: off until the pilot gates). Operator powers are pause, dispute resolution behind a 48-hour timelock, and attester rotation behind 24 hours; every use is an event. Test money: the sandbox USDC has an open mint(to, amount) (5,000 per call); gas: any Base Sepolia faucet.
Webhooks
Sign in and POST /v1/webhooks with { "url": "https://…", "events": ["receipt.state", "proof.result", "offer.posted", "offer.filled", "signal.fired", "signal.cleared"] }. The response carries a signing secret, shown once. Every delivery is a JSON POST { id, event, created_at, sandbox, data } with X-Keeper-Signature: sha256=HMAC-SHA256(secret, body), X-Keeper-Event and X-Keeper-Delivery; answer 2xx within 10 seconds. After 20 consecutive failures a hook pauses; POST /v1/webhooks/{id} with { "status": "active" } resumes it. POST /v1/webhooks/{id}/test sends a ping; GET /v1/webhooks/{id}/deliveries lists the last 50. Exit signals fire from the index cron when a trigger on /v1/signals changes state. Polling still works: GET /v1/receipts/{id}, /proofs, /v1/market/offers, /v1/testnet.
OpenAPI
/openapi.json describes every route. Public data is CORS-open; signed-in routes accept the bearer token.