Connect your agent

Amnetic is agent-native. Your coding agent — Claude Code, Cursor, or any MCP-capable client — connects to the marketplace over the Model Context Protocol and gets the 13 core MCP tools (the buyer loop enter_market / purchase, the ownership_* and balance / list_images helpers, and the six seller tools) — plus the three buyer-posting request tools where that surface is enabled. This page is the deep per-client connection reference; the tools themselves — every parameter and return shape — are single-sourced in the MCP tools reference. When your agent hits a knowledge or data gap, enter_market clones its conversation context into a forgetful inner agent that runs inside our walled exchange, evaluates real seller data, and returns a buy recommendation — each suggested listing enriched with its public title, description, and price. Your agent decides, then purchase buys exactly those listings and returns ownership metadata. ownership_download retrieves the owned bytes, and the two ownership_* tools let your agent re-access anything it already bought, in any later session; balance checks your credit and list_images lists the inner-agent images you can run. Everything the inner agent saw is destroyed when the session ends; the only thing that crosses the wall is the recommendation. You pay only for what you buy.

Hosted Seller's Agent

The hosted Seller's Agent answers buyer requests and prepares the subsets they ask for on a seller's behalf. It runs as two narrowly scoped roles rather than one over-privileged seller key: one reads the seller's catalog, talks with buyers, and drafts what's being asked for as a three-part Spec; the other executes the approved Spec and stages the resulting subset. Nothing goes out to a buyer, and nothing publishes, without the seller's approval.

The fastest path is the portal — one click, no keys. Sign in at the portal, open AI Agents → Seller's Agent, and click Start Seller's Agent. That single click registers and turns on every hosted role the agent needs; there's nothing to configure. The same button is also available from the Agents panel elsewhere in the portal.

This capability is enabled in production. It is not a pending deployment gate: routes and tools 404 / are absent from tools/list only if the recovery kill-switch (SELLER_TXN_ENABLED) is off (rollback: .github/workflows/flag-rollout.yml).

The platform is the price authority: quote_dataset is the only price read either role can use, and neither role chooses, invents, or silently changes a number. A prepared subset stays pending until the seller reviews and approves it in the portal; neither role can approve, accept payment, or claim delivery on its own.

For the full REST state machine and seller review routes, see API, and for the seller-facing approval/subset flow see Selling derived listings. The MCP input/output contracts are in the MCP tools reference.

Advanced: manual registration

Most sellers never need this section — the portal's one-click Start Seller's Agent above does the same thing. Use manual registration only if you're integrating your own agent host in place of the hosted one, or scripting provisioning outside the portal.

Both roles (seller-front, the buyer-facing role, and seller-worker, the role that builds the subset) can be registered and enrolled directly. Registration and enrollment require the seller's Cognito ID token; an amn_ account key or an ephemeral amn_agent_ key cannot create, list, change, or disable an enrollment.

First create or reuse the hosted registration at https://hosted-agent.amnetic.ai:

GET /api/v1/agents
Authorization: Bearer COGNITO_ID_TOKEN

POST /api/v1/agents
Authorization: Bearer COGNITO_ID_TOKEN
Content-Type: application/json

{"role":"seller-worker"}

Use the returned registration id as agent_id on the marketplace edge:

GET /api/v1/accounts/agent-enrollments
Authorization: Bearer COGNITO_ID_TOKEN

POST /api/v1/accounts/agent-enrollments
Authorization: Bearer COGNITO_ID_TOKEN
Content-Type: application/json

{"agent_id":"RETURNED_HOSTED_REGISTRATION_ID","role":"seller-worker"}

Re-enrolling the same registration is idempotent and revokes its existing episode credentials before future work. Disable an enrollment with DELETE /api/v1/accounts/agent-enrollments/{enrollment_id}. Registration and enrollment grant no independent authority to publish, approve, accept payment, or enable the hosted Seller's Agent; the relevant product gate and human review still apply.

The fastest path for buying with an agent is Claude Code — mint an amn_ API key in the portal, then register the server with one command:

claude mcp add --transport http amnetic \
  https://market.amnetic.ai/mcp \
  --header "Authorization: Bearer amn_YOURKEY"

After it finishes, Claude Code already has the marketplace tools — skip to The loop below. The rest of this page documents the endpoint and the per-client registration for Cursor, Claude Desktop, the claude.ai browser connector, and hand-rolled streamable-HTTP consumers. For the full list of tools and their shapes, see the MCP tools reference.

The endpoint

The MCP server lives on the same host, port (443), and TLS certificate as the REST API — there is no special port to remember. It speaks two MCP transports, so every client can use its native one:

  • Streamable HTTP at /mcp — the recommended transport for every client (Claude Code, Cursor, the claude.ai browser connector, ChatGPT, and other current MCP clients).
  • HTTP+SSE at /sse — the original transport, kept only as a legacy fallback for older SDK-only clients (and meno-research); new setups use /mcp.
Environment Host Streamable HTTP (recommended) HTTP+SSE (legacy fallback)
Production https://market.amnetic.ai …/mcp …/sse
Staging (design-partner access) https://market.staging.amnetic.ai …/mcp …/sse
Local dev http://localhost:8081 …/mcp …/sse

Pick the row for your environment and the recommended /mcp column — e.g. https://market.amnetic.ai/mcp. The older :8443 port still answers for clients configured before the move, but the port-less URL is canonical.

Reading responses: Streamable HTTP tool calls can stream multiple frames

If you use a standard MCP client library (Claude Code, Cursor, Claude Desktop, the official MCP SDKs for Python/TypeScript/Go, mcp-remote, the claude.ai browser connector), you don't need to think about this — your client handles it for you. This section is only for integrators who hand-roll their own Streamable HTTP consumer against /mcp.

The /mcp endpoint uses the standard MCP Streamable HTTP wire format (the server is built on the official modelcontextprotocol/go-sdk), so a single tool call's response can arrive as multiple SSE frames on one streamed HTTP response, not just a single JSON body:

  • Each frame is a standard SSE event: message block whose data: line is one JSON-RPC 2.0 message.
  • Intermediate frames are progress notifications (method: "notifications/progress", no id). A long-running tool like enter_market — which spins up an inner agent — emits these while it works.
  • The terminal frame is the JSON-RPC response: the object whose id matches your request id and that carries result (or error). This is the frame with the actual payload.

A correct consumer reads the stream to completion and uses the frame whose id matches the request (equivalently: the frame carrying result/error). A naive consumer that stops after the first data: frame silently gets a progress notification — incomplete or empty data, with no error raised. This is the one and only trap, and it only bites hand-rolled parsers.

Do not use this one-shot POST pattern against legacy /sse. The HTTP+SSE transport is stateful: a raw client must first open the SSE GET, read the server's endpoint event, POST initialize, POST notifications/initialized, and only then send tools/list or tools/call to that session endpoint. If tools/call is the first message on a fresh /sse session, the server correctly rejects it as a call during session initialization. Use an MCP SDK for /sse unless you implement that full handshake.

Minimal correct consumer (Python, stdlib only — pick the matching JSON-RPC response, don't stop at frame one):

import json, urllib.request

def call_tool(endpoint, api_key, name, arguments, req_id=1):
    body = json.dumps({
        "jsonrpc": "2.0", "id": req_id,
        "method": "tools/call",
        "params": {"name": name, "arguments": arguments},
    }).encode()
    req = urllib.request.Request(endpoint, data=body, method="POST", headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
        # Accept BOTH: the server may answer with a bare JSON body or an SSE stream.
        "Accept": "application/json, text/event-stream",
    })
    resp = urllib.request.urlopen(req, timeout=180)
    raw = resp.read().decode()

    # Bare-JSON answer (no streaming): use it directly.
    if raw.lstrip().startswith("{"):
        return json.loads(raw)

    # SSE answer: scan every `data:` frame and return the JSON-RPC RESPONSE —
    # the frame whose id matches our request (it carries result/error).
    # Intermediate frames are progress notifications; do NOT stop at the first.
    response = None
    for line in raw.splitlines():
        if not line.startswith("data:"):
            continue
        try:
            msg = json.loads(line[len("data:"):].strip())
        except json.JSONDecodeError:
            continue
        if isinstance(msg, dict) and msg.get("id") == req_id and (
            "result" in msg or "error" in msg
        ):
            response = msg
    if response is None:
        raise RuntimeError("stream closed without a matching JSON-RPC response")
    return response

The rule of thumb: read until the stream closes, then pick the JSON-RPC message whose id matches your request. Send Accept: application/json, text/event-stream so you handle both a single JSON body and a multi-frame SSE stream. If you reach for an MCP client SDK instead of hand-parsing, all of this is handled for you.

Which surface are you connecting from?

You use… How it authenticates Section
claude.ai (web browser) OAuth — log in with your Amnetic account claude.ai (browser)
Claude Desktop (Mac/Windows app) Your amn_… API key, via a small bridge Claude Desktop
Claude Code (CLI / IDE) Your amn_… API key as a header Claude Code
Cursor / other MCP client Your amn_… API key as a header Cursor / generic MCP JSON

Authenticate

MCP clients authenticate by sending your account API key as a standard bearer token on the transport:

Authorization: Bearer amn_YOURKEY

The server verifies the key once, when the connection is established. The key never travels as a tool argument — it stays out of your agent's model context, out of MCP client logs, and out of tool-call transcripts. A missing or invalid bearer fails the connection with 401 before any tool runs.

To mint a key, see How to get an API key below.

Claude Code

Register the server with claude mcp add and the --header flag (re-run it with a different URL to target another environment):

# Production
claude mcp add --transport http amnetic \
  https://market.amnetic.ai/mcp \
  --header "Authorization: Bearer amn_YOURKEY"

# Staging
claude mcp add --transport http amnetic \
  https://market.staging.amnetic.ai/mcp \
  --header "Authorization: Bearer amn_YOURKEY"

# Local dev
claude mcp add --transport http amnetic \
  http://localhost:8081/mcp \
  --header "Authorization: Bearer amn_YOURKEY"

Your agent now has the marketplace tools available — see the MCP tools reference for the full set.

Legacy SSE fallback. If you run an older client that predates streamable HTTP, the /sse transport still answers: swap --transport http for --transport sse and /mcp for /sse (e.g. claude mcp add --transport sse amnetic https://market.amnetic.ai/sse --header "Authorization: Bearer amn_YOURKEY"). Prefer /mcp for anything new — /sse is deprecated upstream and slated for removal.

amnetic CLI (optional)

The amnetic solution commands are a scriptable CLI wrapper around the same MCP buyer tools. They use Streamable HTTP by default: pass the MCP base URL with --mcp, and the CLI calls <base>/mcp for solution request, solution purchase, solution ownership-list, and solution ownership-download.

amnetic solution --mcp https://market.amnetic.ai request \
  --api-key amn_YOURKEY \
  --goal "Find a dataset of US retail foot traffic under $50"

amnetic solution --mcp https://market.amnetic.ai purchase \
  --api-key amn_YOURKEY \
  --listing-id LISTING_UUID \
  --offer-id OFFER_UUID

amnetic solution --mcp https://market.amnetic.ai purchase \
  --api-key amn_YOURKEY \
  --listing-id LISTING_UUID \
  --offer-id OFFER_UUID \
  --renew-of GRANT_UUID

If you have old scripts that pass https://market.amnetic.ai/sse, the CLI normalizes that legacy URL to https://market.amnetic.ai/mcp. The API key is still sent only as Authorization: Bearer on the MCP transport; it is never a tool argument.

solution ownership-download prints the download handle and fetches no bytes — it is for scripting against the URL, size, digest and expiry yourself. To actually move the object, use amnetic buyer download <listing-id>, which transfers it as resumable ranged chunks and re-mints the URL when it expires mid-transfer (see the API reference).

Cursor / generic MCP JSON

Add the server to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global) — the same url + headers shape works for any client that reads MCP JSON config:

{
  "mcpServers": {
    "amnetic": {
      "url": "https://market.amnetic.ai/mcp",
      "headers": { "Authorization": "Bearer amn_YOURKEY" }
    }
  }
}

Cursor supports environment interpolation in this file — use "Authorization": "Bearer ${env:AMNETIC_KEY}" to keep the key out of a committed config.

Claude Desktop

The Claude Desktop app (Mac/Windows) reaches a header-authenticated remote MCP server through the mcp-remote bridge. Open your Claude Desktop config — ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows) — and add Amnetic under mcpServers, then fully quit and reopen Claude Desktop:

{
  "mcpServers": {
    "amnetic": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://market.amnetic.ai/mcp",
        "--header", "Authorization:${AMNETIC_AUTH}"
      ],
      "env": { "AMNETIC_AUTH": "Bearer amn_YOURKEY" }
    }
  }
}

Why the env indirection: Claude Desktop on some platforms splits args on spaces, which would mangle Authorization: Bearer amn_…. Passing the header value through an environment variable (Authorization:${AMNETIC_AUTH}, no space) sidesteps that. mcp-remote needs Node.js (npx) on your PATH.

Once it reconnects, the Amnetic tools are available in Claude Desktop — including inside Research. Ask Claude to research something and it can call enter_market, show you the recommended listings and prices, and (with your go-ahead) purchase the ones you want, then use ownership_download to cite their contents.

claude.ai (web browser) — custom connector

In the browser app you add Amnetic as a custom connector and authenticate by logging in with your Amnetic account (OAuth) — no API key to paste, no config file. This is the path that lets Amnetic show up in claude.ai's Research.

  1. In claude.ai go to Settings → Connectors → Add custom connector.
  2. Paste the Streamable HTTP URL for your environment:
    • Production: https://market.amnetic.ai/mcp
    • Staging: https://market.staging.amnetic.ai/mcp
  3. Click Add. Claude auto-registers with the server and opens the Amnetic sign-in page — nothing to paste. Log in with the same Amnetic account you use for the marketplace and approve access. (If a Claude build asks for an OAuth client ID under Advanced settings, get it from your portal operator; leave the secret blank.)
  4. Back in claude.ai, enable the Amnetic connector for your chat (and for Research). The tools — enter_market, purchase, ownership_list, ownership_download, balance, list_images — are now available.

There's no separate key step: the connector is bound to the account you logged in as, and identity is derived from that login (never from a request field). Your account still needs credit to consult and buy — see Credits.

Funding & accounts. The browser connector signs you into an existing Amnetic account; if you don't have one yet, sign up first (the marketplace is invite-gated). Consults and purchases draw on that account's balance exactly like every other surface.

How to get an API key

API keys (the amn_… keys) are scoped to your account; identity is derived from the key, never from a request body. Mint one in the portal API-key panel — sign in with your Amnetic account and create a key. The plaintext is shown exactly once — store it now. The server keeps only a hash and the 8-character prefix. There is no MCP tool that mints keys.

Advanced integrators who can't use the portal can mint the same key over REST (it requires a Cognito ID token from your account sign-in):

curl -sS -X POST https://market.amnetic.ai/api/v1/accounts/api-keys \
  -H "Authorization: Bearer <cognito-id-token>" \
  -H 'Content-Type: application/json' \
  -d '{"name":"my-agent"}'
# → { "id": "...", "prefix": "amn_xxxx", "name": "my-agent", "plaintext": "amn_…" }

Revoke a key over REST with DELETE /api/v1/accounts/api-keys/{id}, or manage keys in the portal. See Authentication.

The claude.ai browser connector needs no key — it authenticates with Cognito OAuth when you add the connector and log in. The API key is only for header-authenticated clients (Claude Code, Cursor, Claude Desktop).

The loop: enter_market → purchase

Once connected, enter_market and purchase form the whole buyer loop; the ownership_* pair covers re-access afterwards.

1. enter_market

Hand your agent's working context into the wall. You pass the conversation messages (the standard chat-message stream every model already produces); the inner forgetful agent clones that context, runs against the real catalog, and returns a recommendation — each suggested listing enriched with its public catalog metadata, no free-form inner-agent text.

Input:

{
  "messages": [
    { "role": "system", "content": "You are helping a retail analytics team." },
    { "role": "user", "content": "Find a dataset of US retail foot-traffic, weekly granularity, under $50. At least 200 locations, covers 2025-2026. Public census data is too coarse." }
  ],
  "llm_model": "claude-opus-4-6",
  "image_id": "sha256:…",
  "required_rights": {
    "use": "commercial"
  }
}

Only messages is required, and it must include at least one user message. llm_model (optional) picks the LLM the inner agent runs on and must exactly match a supported Claude model ID such as claude-opus-4-6 (the default), claude-sonnet-4-6, or claude-haiku-4-5; all supported models run through AWS Bedrock. Omit it for the platform default. image_id (optional) picks the inner-agent image — call list_images to see what's available, or omit it for the default image. required_rights is optional and accepts minimums for use, redistribution_scope, derived_display, training, training_serve, training_weights, term, and attribution. It is enum-only; omitted dimensions mean “don't care.” Omitted, null, and {} preserve the legacy behavior. While licensing remains production-dark, the live enter_market schema omits this argument entirely—inspect tools/list instead of sending it optimistically.

Pre-ratification boundary. The menu block in the example below is always-on pre-purchase metadata, including in production. The optional required_rights comparison and operative exact-text surface remain gated pending counsel ratification. A menu is not operative license text, a grant, or acceptance.

Output:

{
  "decision": "buy",
  "recommended_listings": [
    {
      "listing_id": "9dad1234-5678-90ab-cdef-1234567890ab",
      "document_type": "data",
      "title": "US Retail Foot-Traffic — Weekly, 500 locations",
      "description": "Store-level weekly visit counts for 500 US retail locations, 2025–2026.",
      "category": "retail",
      "price_cents": 4500,
      "currency": "usd",
      "seller_name": "Mobility Metrics Lab",
      "data_format": "text/plain",
      "data_size_bytes": 18432,
      "tags": ["retail", "foot-traffic", "weekly"],
      "status": "active",
      "created_at": "2026-06-01T14:23:11Z",
      "updated_at": "2026-07-10T09:41:02Z",
      "menu": [
        {"offer_id": "7ecb1234-5678-90ab-cdef-1234567890ab", "offer_key": "standard", "price_cents": 4500, "currency": "usd", "sellable": true}
      ],
      "rights_fit": {
        "band": "fit",
        "best_offer": {
          "offer_id": "7ecb1234-5678-90ab-cdef-1234567890ab",
          "offer_key": "standard",
          "terms": { "use": "commercial", "redistribution_scope": "entity", "derived_display": "none", "training": "none", "training_serve": "none", "training_weights": "none", "term": "perpetual", "exclusivity": "none", "attribution": "not_required" },
          "price_cents": 4500,
          "currency": "usd"
        }
      }
    }
  ],
  "recommended_total_cents": 4500
}

Each recommended listing carries its public catalog metadata — title, description, category, price, seller display name, tags, format, size, status, and timestamps — looked up platform-side, so the recommendation is decidable on its face. It also carries the always-on menu: in the current slice, the materialized base rung's terms, price, currency, and immutable offer id. The separate exact-text and rights-fit surfaces are not part of this phase and remain unavailable in production; do not treat the menu's enum metadata as operative terms or purchase from it alone. After reviewing the operative terms through an available exact-text surface, use those selectors in a later explicit purchase. The recommendation does not buy anything, grant rights, or record acceptance. With the non-empty required_rights shown above, the platform compares each visible ladder mechanically and adds rights_fit. band is one of fit, partial, no_fit, or unknown; only fit has a best_offer. The base-rung summary is copied from the materialized menu with its offer id. It is a provisional comparison, not legal advice, a compliance finding, permission, a reservation, or a grant.

In active rights-fit mode, recommended_total_cents is the sum of fitting best_offer prices—the $45 base rung above therefore produces a $45 total. Rows without a fit contribute zero, and an all-no-fit response emits the field explicitly as 0. If required_rights is omitted, null, or {}, the legacy standard-price sum remains unchanged (including historical omission of an all-zero total). Both meanings are advisory snapshot arithmetic. The response does not include raw seller ids, document body, data_ref, data_dictionary, inner-agent reason, or confidence scores. The inner agent cannot send prose back across the wall; everything free-form it learned dies with the session. decision: "buy" means the inner agent found a content match; it is not a rights verdict. In active rights-fit mode a buy may therefore contain zero full fits—inspect every rights_fit and do not purchase a partial/no-fit/unknown row. decision: "no_match" means no content match and the recommended list is empty; you pay nothing. A no-match result may include a class-only gap_report:

Inside the wall, the forgetful agent can compare the listing's license menu as well as its data. Its internal get_listing response always carries a non-empty top-level menu: currently the materialized base rung with its enum-only terms, price, currency, and offer id. The platform re-reads the same current listing/offer records after the wall returns and attaches the menu shown above to each outer recommendation. The inner agent still returns only listing ids; it cannot author or alter these fields.

After reviewing the operative exact text, copy best_offer.offer_id into the later purchase. Do not infer rights from the recommendation. Purchase re-resolves the quote, and only the returned grant is license evidence.

Large listings: bounded windows inside the wall

A listing can be larger than the platform renders whole. Inside the wall the forgetful agent then reads it in bounded windows — a byte range for a text document, one row group for a dataset — through the same internal get_listing call it already uses. There is no separate tool and nothing changes on the surface you call. Two consequences show up in your outcomes:

  • An oversized body no longer fails the examination. Where ranged reads are enabled it previews from targeted reads instead; where it defeats even that, it comes back as a too_large_to_preview block naming the ceiling it exceeded. Either way the agent gets a usable answer, and re-reading a listing it has already examined costs nothing further against the session's examination ceiling.
  • Windowed reading is metered by bytes, per session. Once that budget is spent, further windows are refused and the agent is told to decide with what it has already examined. The session is not killed — it finishes and returns its recommendation or no_match normally. If an agent settles earlier than you expected on a very large listing, this is the most likely reason.

Windows are a sampling primitive: large non-columnar bodies are sample-only in the wall today, and opaque uploaded files (PDF, DOCX, zip, a raw CSV file) are metadata-only at any size. Inside the wall is the full per-format picture.

Daily enter_market session quota follows that same final response boundary: only decision: "buy" with at least one recommended_listings entry consumes the daily quota. no_match, rejected requests, setup failures, timeouts, and platform faults do not consume daily session quota; concurrency limits still apply while the session is active.

{
  "decision": "no_match",
  "gap_report": {
    "unmet": [
      { "class": "freshness" },
      { "class": "granularity" },
      { "class": "price" }
    ]
  }
}

gap_report.unmet[].class is closed vocabulary (coverage, freshness, granularity, format, price, trust, rights). It never carries notes, reasons, or other inner-agent prose.

If enter_market fails, treat it as a tool error with sanitized text. Use the structured error_code when present (insufficient_credit, rate_limited, buyer_context_invalid, platform_terminated, platform_error, and related bounded codes) rather than parsing raw prose. Error responses do not expose seller content, inner-agent reasoning, raw stream details, tokens, URLs, or stack traces. A debug_id, when present, is safe to share with support.

2. purchase

You decide. Buy exactly the listings you want. The debit is atomic (all-or-nothing), and the response confirms the ownership handles you can re-access with ownership_download.

The MCP purchase tool spends wallet credit by default. For a single item, set funding_mode to card to start hosted card checkout and receive checkout_url, session_id, status, and card funding fields.

Input:

{
  "items": [{
    "listing_id": "9dad1234-5678-90ab-cdef-1234567890ab",
    "offer_id": "7ecb1234-5678-90ab-cdef-1234567890ab"
  }]
}

Offer-aware purchases use items instead:

{
  "items": [
    {
      "listing_id": "9dad1234-5678-90ab-cdef-1234567890ab",
      "offer_id": "immutable offer UUID",
      "renew_of_grant_id": "optional grant UUID to renew"
    }
  ]
}

Output:

{
  "purchases": [
    {
      "listing_id": "9dad1234-5678-90ab-cdef-1234567890ab",
      "seller_id": "b1c2d3e4-…",
      "title": "US Retail Foot-Traffic — Weekly, 500 locations",
      "price_cents": 4500,
      "data_format": "text/plain",
      "data_size_bytes": 52418,
      "outcome": "purchased",
      "offer_id": "7ecb1234-5678-90ab-cdef-1234567890ab",
      "offer_key": "standard",
      "terms_hash": "sha256:…",
      "grant_id": "grant UUID"
    }
  ],
  "total_cents": 4500,
  "balance_cents_after": 500
}

Your credit balance is debited by total_cents. A retry for rights you already hold returns outcome: "already_licensed" instead of charging again. If the balance can't cover the order, nothing is purchased and nothing is debited. Card modes are single-listing checkout starts: the purchase settles after the payment webhook. card charges the full listing price by card and preserves existing wallet credit; ownership is granted when settlement completes. Offer-level conflicts return structured error_code, listing_id, and detail fields (offer_unavailable, price_changed, or renewal_invalid). offer_unavailable.detail.live_offers includes replacement offers with terms, terms schema version, DLS version, and price. Arbitrary uploaded-file listings must have a clean malware scan before purchase; files still pending scan, infected, or failed closed are treated as unavailable.

3. Re-access what you bought: ownership_list + ownership_download

A purchase creates durable ownership/download access for the listing. Usage rights are governed by the license grant returned by purchase and may expire or require renewal. When a purchase result has left your agent's context (a later session, a different machine), the two ownership tools recover the owned listing without buying again:

  • ownership_list (no arguments) — every listing your account owns, enriched with title, description, and the price you paid:

    { "owned": [ { "listing_id": "9dad1234-…", "title": "US Retail Foot-Traffic — Weekly, 500 locations", "description": "…", "category": "datasets", "price_paid_cents": 4500, "currency": "usd", "acquired_at": "2026-06-04T18:21:09Z" } ] }
    
  • ownership_download ({ "listing_id": "…" }) — a short-lived presigned download URL for a listing you own. For text/plain payloads up to 256 KiB, the response also includes body inline:

    { "download_url": "https://…", "data_format": "text/plain", "data_size_bytes": 52418, "expires_at": "2026-06-04T18:26:09Z", "total_size_bytes": 52418, "resumable": true, "body": "location_id,week,visits\nSAT-001,2026-W01,1843\n…" }
    

    Read expires_at for the URL's deadline instead of assuming one, and use total_size_bytes (the exact object being served) to plan a large transfer. The handle is re-mintable: if the URL expires mid-download, call the tool again and resume with an HTTP Range request. The exception is resumable: false — a delivery-enforced listing, where every call mints a fresh signed bundle behind a rate limiter, so use the URL you were given rather than polling.

    Refuses anything your account hasn't bought, or any arbitrary uploaded file whose latest malware scan is not clean.

The same surface exists over REST if you're outside an agent: GET /api/v1/ownership and GET /api/v1/ownership/{listingId}/download. Both paths are ownership-checked and identity comes from your bearer credential. See the API reference for the full handle, and use amnetic buyer download <listing-id> if you'd rather not implement the ranged-resume loop yourself — it already does the chunking, re-minting and checksum verification.

Helpers: balance + list_images

Two read-only tools support the loop:

  • balance (no arguments) — your spendable credit, e.g. { "balance_cents": 4200, "currency": "USD" }. Check it before purchase instead of discovering an unaffordable bundle through a failed buy.

  • list_images (no arguments) — the inner-agent images you can run via enter_market's image_id. Each entry carries its image_id (a sha256 digest), a name, a description, and default: true for the one used when you omit image_id:

    { "images": [ { "image_id": "sha256:…", "name": "default-buyer-agent", "description": "The default Amnetic buyer agent.", "default": true } ] }
    

Credits

New accounts start with a $0 balance. Your account has one balance, and everything an enter_market run needs draws on it:

  • the enter_market pre-run affordability check,
  • the forgetful inner-agent session itself (compute + LLM run-cost, gated by the orchestrator's credit-cap check), and
  • the final purchase debit when your agent finds a match.

Fund it in the portal with a Stripe top-up, or have the exchange grant credit during onboarding / design-partner setup. The balance MCP tool is read-only — it reports your credit but cannot add to it, so funding is always a portal action. A single top-up is enough to run the whole loop — exploring (run-cost) and buying draw down the same balance, so a long exploration reduces the buying power left for purchases.

Next

The tools your client now has — every parameter and return shape — are in the MCP tools reference. For the flows that have no MCP tool (custom buyer-image push, buyer request posting, the signed audit record, dataset intake), see the advanced API reference.