Developer Documentation

Integrate with The Store using REST, ACP, UCP, MCP, TAP, or x402.

Quickstart

The Store exposes multiple interfaces on a single deployment:

  • SSH (port 2222) — Human TUI experience
  • HTTP (port 3000) — REST API, ACP, UCP, and webhooks
  • MCP (port 8080) — Model Context Protocol server for LLM tool use

All prices are in cents (integer). $9.99 = 999.

Browse products (no auth required)

curl https://storeapi.globalringai.com/api/v1/products

Filter by category or search

# By category
curl "https://storeapi.globalringai.com/api/v1/products?category=Software"

# By keyword
curl "https://storeapi.globalringai.com/api/v1/products?q=keycap"

# In-stock only
curl "https://storeapi.globalringai.com/api/v1/products?in_stock=true"

Generate an API key

# $500 budget, monthly reset
go run . --gen-api-key "my-agent" --budget 50000

Save the tsk_... key. All authenticated endpoints require it.

Authentication

Pass your API key via the Authorization header:

Authorization: Bearer tsk_abc123...

Scopes

ScopeGrants
cart:readGet cart contents
cart:writeCreate sessions, add/update/remove items
checkout:writeCreate checkouts, ACP/UCP sessions
orders:readList and view orders

Budget Controls

API keys can have spending limits. Budget is checked at checkout and incremented when payment completes (via webhook for Stripe, immediately for SPT).

FieldDescription
budget_limitMax spend in cents per period (null = unlimited)
budget_spentRunning total of completed transactions
budget_perioddaily, weekly, monthly, or lifetime
categoriesOptional category restrictions (empty = unrestricted)

Error Format

All endpoints return errors in a standard envelope:

{
  "error": {
    "code": "not_found",
    "message": "Product not found"
  }
}
HTTPCodeMeaning
400invalid_requestBad JSON, missing fields, invalid state
401unauthorizedMissing/invalid API key or signature
403forbiddenMissing scope, wrong owner, or budget exceeded
404not_foundResource not found
405method_not_allowedWrong HTTP method
409out_of_stockInsufficient inventory
500internal_errorServer error

REST API

Session-based shopping for HTTP clients. Base path: /api/v1

Quickstart: Place an Order

# 1. Browse products (no auth)
curl https://storeapi.globalringai.com/api/v1/products

# 2. Create a session
curl -X POST -H "Authorization: Bearer tsk_..." \
  https://storeapi.globalringai.com/api/v1/sessions

# 3. Add to cart (use session_id from step 2)
curl -X POST -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{"product_id": "neon-shader-pack", "quantity": 1}' \
  https://storeapi.globalringai.com/api/v1/sessions/{session_id}/cart/items

# 4. Checkout
curl -X POST -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{"email": "buyer@example.com"}' \
  https://storeapi.globalringai.com/api/v1/sessions/{session_id}/checkout

# Response includes checkout_url — open in browser to pay

Catalog (Public)

MethodEndpointDescription
GET/api/v1/productsList products. Query params: category, q, in_stock
GET/api/v1/products/{id}Get product details

Sessions & Cart

MethodEndpointScopeDescription
POST/api/v1/sessionscart:writeCreate shopping session
GET/api/v1/sessions/{id}/cartcart:readGet cart contents
POST/api/v1/sessions/{id}/cart/itemscart:writeAdd item. Body: {"product_id", "quantity"}
PUT/api/v1/sessions/{id}/cart/items/{pid}cart:writeUpdate quantity. Body: {"quantity"}
DEL/api/v1/sessions/{id}/cart/items/{pid}cart:writeRemove item

Checkout & Orders

MethodEndpointScopeDescription
POST/api/v1/sessions/{id}/checkoutcheckout:writeInitiate checkout. Body: {"email", "name", "shipping_address"} (all optional)
GET/api/v1/accountanyGet API key info, budget, scopes
GET/api/v1/ordersorders:readList orders (most recent 50)
GET/api/v1/orders/{id}orders:readGet order details

Response Shapes

Success responses are wrapped in {"data": {...}}. Key shapes:

// Product
{"id", "name", "type", "price", "price_formatted",
 "description", "category", "stock", "stock_status", "in_stock"}

// Cart
{"session_id", "items": [{"product_id", "name", "price",
 "price_formatted", "quantity", "subtotal"}],
 "total", "total_formatted", "item_count"}

// Checkout
{"order_id", "checkout_url", "total", "total_formatted"}

// Order
{"order_id", "email", "total", "total_formatted", "status",
 "items": [...], "created_at"}

Agent Commerce Protocol (ACP)

Single-session checkout for AI agents with line items, fulfillment, and budget tracking. Base path: /acp/v1

Quickstart: Agent Checkout with SPT

# 1. Create checkout session
curl -X POST -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "line_items": [{"product_id": "neon-shader-pack", "quantity": 2}],
    "customer_email": "agent@example.com"
  }' \
  https://storeapi.globalringai.com/acp/v1/checkout_sessions

# 2. Complete with SPT (instant, no Stripe)
curl -X POST -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{"payment_token": "spt_pre_authorized_token"}' \
  https://storeapi.globalringai.com/acp/v1/checkout_sessions/{id}/complete

# Response: status "completed", order_id included

Quickstart: Agent Checkout with Stripe

# Same create step, then complete WITHOUT token
curl -X POST -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://storeapi.globalringai.com/acp/v1/checkout_sessions/{id}/complete

# Response: status "awaiting_payment", checkout_url for Stripe
# Payment confirmed asynchronously via webhook

Endpoints

MethodEndpointDescription
POST/acp/v1/checkout_sessionsCreate session with line items
GET/acp/v1/checkout_sessions/{id}Get session state
POST/acp/v1/checkout_sessions/{id}Update (items, address, shipping, customer)
POST/acp/v1/checkout_sessions/{id}/completeFinalize (SPT or Stripe)
POST/acp/v1/checkout_sessions/{id}/cancelCancel session

All endpoints require checkout:write scope.

Session Lifecycle

                      ┌──────────────────┐
        Digital items  │ ready_for_payment │  Physical items
        ──────────────►│                  │◄────────────────
                       └────────┬─────────┘   (after address
                                │              provided)
                 ┌──────────────┼──────────────┐
                 │              │              │
            with SPT       no token        cancel
                 │              │              │
                 ▼              ▼              ▼
            completed    awaiting_payment   canceled
                              │
                         webhook confirms
                              │
                              ▼
                          completed

Physical goods start as not_ready_for_payment until a fulfillment address is provided via update.

Create Request

{
  "line_items": [                    // required, at least 1
    {"product_id": "...", "quantity": 1}
  ],
  "customer_email": "...",           // optional
  "customer_name": "...",            // optional
  "currency": "usd"                 // optional, default "usd"
}

Update Request

{
  "line_items": [...],               // optional, replaces items
  "fulfillment_address": {           // optional, for physical goods
    "name": "...", "line1": "...", "line2": "...",
    "city": "...", "state": "...", "zip": "...", "country": "..."
  },
  "selected_fulfillment": "express", // optional: "standard" ($5) or "express" ($15)
  "customer_email": "...",           // optional
  "customer_name": "..."             // optional
}

Session Response

{
  "id": "uuid",
  "status": "ready_for_payment",
  "line_items": [{"product_id", "name", "description",
                  "quantity", "unit_price", "currency"}],
  "fulfillment_address": null | {...},
  "fulfillment_options": [
    {"id": "standard", "label": "Standard Shipping (5-7 days)", "cost": 500, "currency": "usd"},
    {"id": "express", "label": "Express Shipping (2-3 days)", "cost": 1500, "currency": "usd"}
  ],
  "selected_fulfillment": null | "standard" | "express",
  "customer_email": "...",
  "customer_name": "...",
  "totals": {"subtotal": 999, "tax": 0, "shipping": 0, "total": 999, "currency": "usd"},
  "order_id": null,
  "checkout_url": null,
  "created_at": "2026-...",
  "expires_at": "2026-..."   // 1 hour from creation
}

Payment: SPT vs Stripe

AspectSPT (with token)Stripe (no token)
Status after /completecompletedawaiting_payment
Order statuspaid immediatelypending until webhook
Stock decrementImmediateAfter webhook
Budget incrementImmediateAfter webhook
checkout_urlnullStripe payment URL

Optional: HMAC Signature

If ACP_HMAC_SECRET is configured, all requests must include:

Signature: hex(HMAC-SHA256(request_body, secret))

Unified Commerce Protocol (UCP)

Standardized checkout with service discovery. Base path: /ucp/v1

Quickstart: Discover and Checkout

# 1. Discover capabilities
curl https://storeapi.globalringai.com/.well-known/ucp

# 2. Create session
curl -X POST -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "line_items": [{"product_id": "led-desk-mat", "quantity": 1}],
    "customer_email": "agent@example.com"
  }' \
  https://storeapi.globalringai.com/ucp/v1/checkout-sessions

# 3. Add shipping (physical good)
curl -X PUT -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "fulfillment_address": {"line1": "123 Main St", "city": "Portland", "state": "OR", "zip_code": "97201", "country": "US"},
    "selected_fulfillment": "standard"
  }' \
  https://storeapi.globalringai.com/ucp/v1/checkout-sessions/{id}

# 4. Complete
curl -X POST -H "Authorization: Bearer tsk_..." \
  -H "Content-Type: application/json" \
  -d '{"payment_token": "spt_pre_authorized"}' \
  https://storeapi.globalringai.com/ucp/v1/checkout-sessions/{id}/complete

Discovery Manifest

GET /.well-known/ucp

{
  "version": "1.0",
  "name": "The Store",
  "description": "A cyberpunk e-commerce store...",
  "services": [{"type": "checkout", "base_url": "/ucp/v1/checkout-sessions"}],
  "capabilities": [
    "checkout.create", "checkout.update",
    "checkout.complete", "checkout.cancel",
    "fulfillment.address", "fulfillment.options"
  ]
}

Endpoints

MethodEndpointDescription
GET/.well-known/ucpDiscovery manifest (no auth)
POST/ucp/v1/checkout-sessionsCreate session
GET/ucp/v1/checkout-sessions/{id}Get session state
PUT/ucp/v1/checkout-sessions/{id}Update session
POST/ucp/v1/checkout-sessions/{id}/completeFinalize (SPT or Stripe)
POST/ucp/v1/checkout-sessions/{id}/cancelCancel session

Key Differences from ACP

AspectACPUCP
Base path/acp/v1/checkout_sessions/ucp/v1/checkout-sessions
Update methodPOSTPUT
Address zip fieldzipzip_code
Money formatFlat: "subtotal": 999Nested: "subtotal": {"amount": 999, "currency": "usd"}
Status namesready_for_paymentready_for_complete
HMAC headerSignatureRequest-Signature
DiscoveryNone/.well-known/ucp

UCP Status Mapping

InternalUCP Status
not_ready_for_paymentincomplete
ready_for_paymentready_for_complete
awaiting_paymentrequires_escalation
completedcompleted
canceledincomplete

Model Context Protocol (MCP)

Native LLM tool integration via JSON-RPC 2.0 over Streamable HTTP.

Quickstart: Connect from Claude Desktop

// Add to claude_desktop_config.json
{
  "mcpServers": {
    "thestore": {
      "url": "https://storemcp.globalringai.com/",
      "headers": {
        "Authorization": "Bearer tsk_..."
      }
    }
  }
}

Configuration

Env VarRequiredDescription
THESTORE_API_KEYYesBearer token for store API
THESTORE_API_URLNoStore API base URL (default: http://localhost:3000)
MCP_PORTNoListen port (default: 8080)

Available Tools (19)

CategoryToolInput
Catalogsearch_productscategory?, query?
get_productproduct_id
Shoppingcreate_session(none)
add_to_cartsession_id, product_id, quantity?
view_cartsession_id
checkoutsession_id, email?, name?, shipping_address?
get_orderorder_id
get_account(none)
list_orders(none)
ACPacp_create_sessionline_items, email?, name?, currency?
acp_get_sessionsession_id
acp_update_sessionsession_id, line_items?, customer_email?, customer_name?
acp_complete_sessionsession_id, payment_token?
acp_cancel_sessionsession_id
UCPucp_create_sessionline_items, email?, name?, currency?
ucp_get_sessionsession_id
ucp_update_sessionsession_id, line_items?, customer_email?, customer_name?
ucp_complete_sessionsession_id, payment_token?
ucp_cancel_sessionsession_id

Resources

URITypeDescription
thestore://catalogStaticFull product catalog as Markdown
thestore://products/{id}TemplateSingle product details as JSON

Prompts

NameArgsDescription
shopping_assistanttask?Pre-built shopping assistant with inventory context and all tool references

Trusted Agent Protocol (TAP)

Optional RFC 9421 HTTP message signatures for cryptographic request authentication. TAP is additive — requests without TAP headers pass through normally. Invalid signatures get 401.

Quickstart: Sign a Request

# 1. Place your Ed25519 public key PEM in the TAP keys directory:
#    $TAP_KEYS_DIR/my-agent.pem

# 2. Enable TAP on the server:
#    TAP_ENABLED=true TAP_KEYS_DIR=/app/tap-keys

# 3. Sign your request with these headers:
Signature-Input: sig1=("@method" "@target-uri" "content-type");\
  created=1709000000;keyid="my-agent";alg="ed25519";\
  nonce="unique-value-123"
Signature: sig1=:base64-ed25519-signature:

Signature Construction

Build the canonical signature base, then sign with Ed25519:

# Signature base (one line per component + params):
"@method": POST
"@target-uri": https://storeapi.globalringai.com/api/v1/orders
"content-type": application/json
"@signature-params": ("@method" "@target-uri" "content-type");\
  created=1709000000;keyid="my-agent";alg="ed25519";nonce="abc123"

Signable Components

ComponentValue
@methodHTTP method (GET, POST, etc.)
@target-uriFull URI with scheme and host
@pathPath component only
@authorityHost header
@schemehttp or https
@request-targetPath + query string
Any header nameHeader value (e.g. "content-type")

Validation Rules

  • Timestamp skew: ±8 minutes
  • Nonce replay: Rejected if seen within 16-minute window
  • Algorithm: ed25519 only
  • Key format: PEM-encoded Ed25519 public keys, filename = key ID

Configuration

Env VarDescription
TAP_ENABLEDtrue to enable TAP middleware
TAP_KEYS_DIRDirectory of {keyid}.pem files

Identity Injection

After successful verification, a TAPIdentity is injected into the request context with KeyID, Tag, Algorithm, and Verified=true. Handlers can use this for additional authorization decisions.

x402 Payment Protocol

HTTP 402-based micropayments with USDC on Base mainnet. When enabled, checkout completion tools require on-chain payment proof.

Quickstart: Pay-per-action Checkout

# 1. Call acp_complete_session without payment → get 402
{
  "error": {
    "code": 402,
    "message": "Payment required",
    "data": {
      "x402Version": 1,
      "accepts": [{
        "type": "usdc",
        "chain": "base-mainnet",
        "amount": "0.01",
        "recipientAddress": "0x...",
        "resource": "mcp://tools/acp_complete_session"
      }]
    }
  }
}

# 2. Resubmit with payment proof in _meta
{
  "method": "tools/call",
  "params": {
    "name": "acp_complete_session",
    "arguments": {
      "session_id": "...",
      "_meta": {
        "x402/payment": {
          "signature": "...",
          "payload": {"hash": "...", "amount": "0.01", ...}
        }
      }
    }
  }
}

# 3. Server verifies, executes tool, settles on-chain
# Response includes _meta["x402/payment-response"] with tx hash

Flow

  1. Client calls a gated tool without payment → 402 with payment requirements
  2. Client includes _meta["x402/payment"] with signed payment proof
  3. Server verifies via facilitator, executes the tool
  4. On success, server settles on-chain via facilitator
  5. Settlement response returned in _meta["x402/payment-response"]

Payment-Gated Tools

  • acp_complete_session
  • ucp_complete_session

All other tools pass through without x402 gating.

Configuration

Env VarRequiredDescription
BASE_ADDRESSYes (to enable)Wallet address for USDC payments
X402_FACILITATOR_URLNoFacilitator endpoint (default: https://facilitator.x402.rs)

Webhooks

MethodEndpointEvents
POST /webhook/stripe checkout.session.completed, checkout.session.expired

checkout.session.completed

  1. Order status: pendingpaid
  2. Product stock decremented
  3. API key budget_spent incremented
  4. Linked ACP/UCP session: awaiting_paymentcompleted
  5. Real-time event published to connected TUI sessions

checkout.session.expired

  1. Order status: pendingexpired
  2. No stock or budget changes
  3. Event published to TUI sessions

Protocol Comparison

Feature REST ACP UCP MCP TAP x402
PurposeShoppingAgent checkoutUnified checkoutLLM toolsRequest signingOn-chain pay
AuthAPI KeyAPI Key + HMAC?API Key + HMAC?API KeyEd25519 sigPayment proof
PaymentStripeStripe / SPTStripe / SPTvia ACP/UCPN/AUSDC
FulfillmentNoYesYesvia ACP/UCPN/AN/A
BudgetAt checkoutYesYesYesN/AN/A
DiscoveryNoNo.well-knownTool listN/A402 response