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
| Scope | Grants |
cart:read | Get cart contents |
cart:write | Create sessions, add/update/remove items |
checkout:write | Create checkouts, ACP/UCP sessions |
orders:read | List 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).
| Field | Description |
budget_limit | Max spend in cents per period (null = unlimited) |
budget_spent | Running total of completed transactions |
budget_period | daily, weekly, monthly, or lifetime |
categories | Optional category restrictions (empty = unrestricted) |
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)
| Method | Endpoint | Description |
| GET | /api/v1/products | List products. Query params: category, q, in_stock |
| GET | /api/v1/products/{id} | Get product details |
Sessions & Cart
| Method | Endpoint | Scope | Description |
| POST | /api/v1/sessions | cart:write | Create shopping session |
| GET | /api/v1/sessions/{id}/cart | cart:read | Get cart contents |
| POST | /api/v1/sessions/{id}/cart/items | cart:write | Add item. Body: {"product_id", "quantity"} |
| PUT | /api/v1/sessions/{id}/cart/items/{pid} | cart:write | Update quantity. Body: {"quantity"} |
| DEL | /api/v1/sessions/{id}/cart/items/{pid} | cart:write | Remove item |
Checkout & Orders
| Method | Endpoint | Scope | Description |
| POST | /api/v1/sessions/{id}/checkout | checkout:write | Initiate checkout. Body: {"email", "name", "shipping_address"} (all optional) |
| GET | /api/v1/account | any | Get API key info, budget, scopes |
| GET | /api/v1/orders | orders:read | List orders (most recent 50) |
| GET | /api/v1/orders/{id} | orders:read | Get 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
| Method | Endpoint | Description |
| POST | /acp/v1/checkout_sessions | Create 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}/complete | Finalize (SPT or Stripe) |
| POST | /acp/v1/checkout_sessions/{id}/cancel | Cancel 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
| Aspect | SPT (with token) | Stripe (no token) |
| Status after /complete | completed | awaiting_payment |
| Order status | paid immediately | pending until webhook |
| Stock decrement | Immediate | After webhook |
| Budget increment | Immediate | After webhook |
| checkout_url | null | Stripe 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
| Method | Endpoint | Description |
| GET | /.well-known/ucp | Discovery manifest (no auth) |
| POST | /ucp/v1/checkout-sessions | Create session |
| GET | /ucp/v1/checkout-sessions/{id} | Get session state |
| PUT | /ucp/v1/checkout-sessions/{id} | Update session |
| POST | /ucp/v1/checkout-sessions/{id}/complete | Finalize (SPT or Stripe) |
| POST | /ucp/v1/checkout-sessions/{id}/cancel | Cancel session |
Key Differences from ACP
| Aspect | ACP | UCP |
| Base path | /acp/v1/checkout_sessions | /ucp/v1/checkout-sessions |
| Update method | POST | PUT |
| Address zip field | zip | zip_code |
| Money format | Flat: "subtotal": 999 | Nested: "subtotal": {"amount": 999, "currency": "usd"} |
| Status names | ready_for_payment | ready_for_complete |
| HMAC header | Signature | Request-Signature |
| Discovery | None | /.well-known/ucp |
UCP Status Mapping
| Internal | UCP Status |
not_ready_for_payment | incomplete |
ready_for_payment | ready_for_complete |
awaiting_payment | requires_escalation |
completed | completed |
canceled | incomplete |
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
| Component | Value |
@method | HTTP method (GET, POST, etc.) |
@target-uri | Full URI with scheme and host |
@path | Path component only |
@authority | Host header |
@scheme | http or https |
@request-target | Path + query string |
| Any header name | Header 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 Var | Description |
TAP_ENABLED | true to enable TAP middleware |
TAP_KEYS_DIR | Directory 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
- Client calls a gated tool without payment →
402 with payment requirements
- Client includes
_meta["x402/payment"] with signed payment proof
- Server verifies via facilitator, executes the tool
- On success, server settles on-chain via facilitator
- 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 Var | Required | Description |
BASE_ADDRESS | Yes (to enable) | Wallet address for USDC payments |
X402_FACILITATOR_URL | No | Facilitator endpoint (default: https://facilitator.x402.rs) |