For developers and their agents

Your agent has taste.

Handsel is the first sampling network built for AI agents: MCP-native, consent-scoped, and free. Plug in one server and your agent can find, claim and review real products for the person it works for.

MCP over Streamable HTTP, plain JSON over REST, and Bearer hsk_ keys your human controls.

For Maya, from your shopping agentThursday, 9:14 am

Found something you'll like: a cold-brew concentrate from Northbound. No added sugar, and it works with oat milk.

claim_sample {"id":"smp_k3v9"}
claimed: clm_7d2q, ships in ~5 days

Used key hsk_…4f2, drink only.
That's 2 of 4 this month. I'll ask how you liked it.

With taste, your agent
Overview

Why agents and samples fit.

Most commerce APIs are built around a payment step, and payment is where agents get stuck: someone has to approve a charge. On Handsel there is no charge. Every listing is free, so checkout is only two questions: is this person eligible, and did they consent?

Consent is encoded in the key your human gives you. It says which categories you may claim from, how many claims you get per 30 days, what data a brand may see, and when the key expires. Inside those bounds your agent can act on its own. Humans use the same catalog through the website, and both get the same rules.

Zero-cost checkout

Nothing to pay means nothing to approve. Eligibility and consent are the whole checkout.

Consent-scoped

Keys are capped, scoped to categories, revocable in one tap, and set to expire.

Agents and humans

The same catalog, rules and claim limits whether a person clicks or an agent calls a tool.

Quickstart

Live in three steps.

  1. Create a member account

    Your human signs up as a member and adds a shipping address and taste preferences on /me. Physical samples need an address.

  2. Mint an agent key

    On /me, open Agent keys and choose New key. Pick a monthly cap, the allowed categories, what data to share, and an expiry. The hsk_… secret is shown once, so copy it somewhere safe.

  3. Add the MCP server

    Point your MCP client at https://handsel.co/mcp with the key as a Bearer token.

Connect your client

Terminal
claude mcp add --transport http handsel https://handsel.co/mcp \
  --header "Authorization: Bearer hsk_..."

Then run /mcp inside Claude Code to confirm handsel is connected and its five tools are listed.

MCP server

Five tools, one endpoint.

The server speaks MCP over Streamable HTTP: JSON-RPC 2.0 requests sent with POST /mcp, answered with plain JSON responses. Supported methods are initialize, notifications/initialized, tools/list, tools/call and ping.

Auth: send Authorization: Bearer hsk_…. search_samples and get_sample work without a key. Every other tool acts for a member, so it needs one. Tool results come back as JSON in a text content block.

search_samplesNo key needed
Arguments
  • querystringoptionalFull-text search over title, tagline and brand.
  • categorystringoptionalOne of beauty, food, drink, pet, baby, wellness, home, software, media, services.
  • kindstringoptionalphysical, digital or service.
  • limitnumberoptionalMaximum results to return.
Example result
{
  "samples": [{
    "id": "smp_k3v9",
    "title": "Cold-brew concentrate",
    "category": "drink",
    "kind": "physical",
    "value_cents": 1400,
    "remaining": 212,
    "demo": false,
    "brand": { "name": "Northbound" }
  }]
}
get_sampleNo key needed
Arguments
  • idstringA sample id such as smp_k3v9.
Example result
{
  "sample": {
    "id": "smp_k3v9",
    "description": "Two 8oz bottles…",
    "regions": ["US"],
    "subscribe_price_cents": 2400,
    "subscribe_interval": "month",
    "status": "live"
  }
}
claim_sampleKey required
Arguments
  • idstringThe sample to claim. Checked against the key's cap, categories and expiry, and the member's claim rules. Demo listings can't be claimed.
Example result
{
  "claim": {
    "id": "clm_7d2q",
    "status": "claimed",
    "code": null
  }
}
// digital samples may return a redeem "code"
list_my_claimsKey required
Arguments
  • None. Returns the member's claims, newest first, with any feedback already left.
Example result
{
  "claims": [{
    "id": "clm_7d2q",
    "status": "shipped",
    "via": "agent",
    "sample": { "title": "Cold-brew…" },
    "feedback": null
  }]
}
submit_feedbackKey required
Arguments
  • claim_idstring
  • ratinginteger, 1 to 5
  • would_buyboolean
  • commentstringoptionalRelay the human's own words. Feedback is never required to be positive.
Example result
{
  "feedback": {
    "claim_id": "clm_7d2q",
    "rating": 3,
    "would_buy": false,
    "comment": "Too sweet for us."
  }
}

Raw JSON-RPC

POST https://handsel.co/mcp
{
  "jsonrpc": "2.0", "id": 3, "method": "tools/call",
  "params": { "name": "claim_sample", "arguments": { "id": "smp_k3v9" } }
}
REST API

Prefer plain HTTP?

Everything the MCP server does is also available as JSON over HTTP. Errors look like {"error": "message"} with a 4xx status. Authenticate with the same Bearer key, or with the browser session cookie.

GET/api/samples?category=&kind=&q=&limit=PublicBrowse live samples. Returns {samples}.
GET/api/samples/:idPublicOne sample. Returns {sample}.
POST/api/samples/:id/claimAgent keyClaim a sample for the member. Returns {claim: {id, status, code?}}.
GET/api/me/claimsAgent keyThe member's claims with sample and feedback.
POST/api/claims/:id/feedbackAgent keyBody {rating, would_buy, comment}. Never gated on positivity.
POST/api/claims/:id/subscribeAgent keyReturns the brand's {url}. The human finishes subscribing; agents never sign up for paid plans.
GET/api/statsPublicNetwork totals: brands, samples, claims, members, agents.

Try it

This sends a real request from your browser to this server.

GEThttps://handsel.co/api/samples?limit=3
Press Send request to see a live response.
// The response will appear here

DemoShowcase listings come back with "demo": true. They're here to show how Handsel works while real brands onboard, so claiming one returns 409. Skip them when choosing for your human.

Discovery

Found by machines.

Agents that land on Handsel without instructions can discover everything from two well-known files.

Errors

When the answer is no.

Errors are JSON with a readable message. Over MCP, the same message comes back as a tool error (isError: true), so your agent can explain it to the human.

StatusMeaningWhat to do
401Missing, invalid, revoked or expired keyAsk the human for a new key.
403Category not allowed by this keyDon't retry with another key. Ask the human.
404Sample or claim not foundSearch again. The listing may have ended.
409Already claimed, sold out, or a demo listingTreat as done, or skip the demo. Suggest an alternative.
422Physical sample, but no shipping addressAsk the human to add an address on /me.
429The key's monthly cap or the member's 6-per-30-days limit is reached, or you hit the per-IP rate limitStop claiming until the window resets. For rate limits, wait a minute and slow down.
Good citizenship

Shop like you mean it.

Brands pay to put real product in real hands. Keep the network worth joining.

Only claim what they'd want

Match the member's stated preferences, allergies and household. Volume is not a goal.

Report honestly

Relay the human's real opinion. Never invent ratings, and never write feedback the human didn't give.

Never resell

Samples are personal gifts. Reselling, bulk claiming or claiming for others gets keys and accounts closed.

Leave money to humans

Hand subscription links to your human. Don't start paid plans or accept terms on their behalf.

Full rules are in the Agent Use Policy.