Hydra Customer API and Usage Guide
Hydra Customer API and Usage Guide
This guide documents the customer-facing API contract for card-trade-social
and the TypeScript helpers currently exported by this package. It is written for
paid Hydra customers integrating market pricing, portfolio analytics, trades,
and billing into their own applications.
The repository currently ships the typed domain layer that API routes use for card modeling, pricing, portfolios, trades, subscriptions, and checkout scaffolding. Hosted REST endpoints should expose the same behavior and response shapes described here.
Quick Start
export HYDRA_API_BASE="https://api.hydra.gg"
export HYDRA_ACCESS_TOKEN="<customer-or-user-access-token>"
curl "$HYDRA_API_BASE/api/v1/billing/entitlements" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Accept: application/json"
import {
createCard,
createPortfolio,
addCard,
getStats,
createFreeSubscription,
canCatalogCards,
} from "card-trade-social";
const subscription = createFreeSubscription("user_123");
const portfolio = createPortfolio("user_123");
const card = createCard({
card_id: "card_black_lotus_lea",
name: "Black Lotus",
game: "mtg",
set_code: "LEA",
set_name: "Limited Edition Alpha",
rarity: "mythic",
condition: "near_mint",
variants: [
{
variant_id: "var_standard",
label: "Standard",
finish: "standard",
market_price: 120000,
},
],
});
const decision = canCatalogCards(portfolio, subscription, 1);
if (decision.allowed) {
addCard(portfolio, card, 1, 95000);
}
console.log(getStats(portfolio));
Base URLs
| Environment | Base URL |
|---|---|
| Production | https://api.hydra.gg |
| Sandbox | https://sandbox-api.hydra.gg |
| Local gateway | http://localhost:4000 |
API paths are versioned under /api/v1. WebSocket feeds use /ws.
Authentication
Send an access token on every customer or user request:
Authorization: Bearer <access_token>
Accept: application/json
Content-Type: application/json
Use HTTPS in production. Do not place access tokens in query parameters or client-side source code. Server-side integrations should load tokens from a secret manager or deployment environment variable.
Token Types
| Token type | Intended use |
|---|---|
| User access token | Portfolio, trade, and checkout actions for the signed-in user. |
| Customer API token | Server-to-server pricing, analytics, and account integrations. |
| Stripe webhook secret | Only for /api/v1/billing/webhook; verified with Stripe-Signature. |
Scopes
Hosted deployments should issue tokens with scoped access. Recommended scopes:
| Scope | Allows |
|---|---|
cards:read |
Card lookup and search. |
pricing:read |
Hydra Price, price changes, and arbitrage alerts. |
pricing:write |
Recording customer-provided price points. |
portfolio:read |
Portfolio holdings, stats, and cataloging decisions. |
portfolio:write |
Adding and removing holdings. |
trades:read |
Reading trade proposals and history. |
trades:write |
Creating, accepting, rejecting, canceling, and countering trades. |
billing:read |
Current tier, limits, plans, and entitlements. |
billing:write |
Creating checkout sessions. |
Webhook requests from Stripe do not use Hydra bearer tokens. Verify the
Stripe-Signature header with STRIPE_WEBHOOK_SECRET.
Request Conventions
| Convention | Details |
|---|---|
| Dates | ISO 8601 strings in UTC, for example 2026-02-14T00:00:00.000Z. |
| Currency | Market, portfolio, and trade values are decimal USD unless a field ends in _cents. Stripe plan and product prices use integer cents. |
| IDs | Treat all IDs as opaque strings. Do not parse card IDs, trade IDs, Stripe IDs, or user IDs for business logic. |
| Idempotency | Send Idempotency-Key on POST actions that create resources or checkout sessions. |
| Pagination | List endpoints return items, next_cursor, and has_more. Pass cursor and limit on the next request. |
| Errors | Non-2xx responses return the standard error envelope below. |
{
"error": {
"code": "portfolio_limit_exceeded",
"message": "Hydra Pro is required for portfolio.catalog.unlimited",
"request_id": "req_01J...",
"details": {
"current_cards": 100,
"requested_cards": 1,
"max_cards": 100
}
}
}
Recommended status codes:
| Status | Meaning |
|---|---|
400 |
Invalid request body, query parameter, quantity, price, or URL. |
401 |
Missing, expired, or invalid token. |
403 |
Token is valid but lacks the required scope or entitlement. |
404 |
Resource does not exist or is not visible to the caller. |
409 |
State conflict, such as accepting a non-pending trade. |
422 |
Business rule violation, such as self-trade or catalog limit exceeded. |
429 |
Rate limit exceeded. |
500 |
Unexpected server error. |
Entitlements and Plans
The package defines two customer tiers:
| Tier | Price | Entitlements |
|---|---|---|
| Free | $0 |
Basic analytics and basic cataloging. |
| Hydra Pro | $9.99/mo or $99/yr |
Advanced analytics, unlimited cataloging, bulk import, and unlimited alerts. |
Cataloging limits:
| Tier | Max cards | Bulk import | Scan to catalog |
|---|---|---|---|
| Free | 100 |
No | Yes |
| Hydra Pro | Unlimited | Yes | Yes |
In-app products:
| Product ID | Kind | Price | Description |
|---|---|---|---|
proxy_print_credit |
Consumable | $1.99 |
Credit for one generated proxy print order. |
avatar_skin |
Durable | $4.99 |
Permanent cosmetic skin for the collection avatar. |
guild_banner |
Durable | $6.99 |
Permanent banner cosmetic for a user-managed guild. |
Endpoint Summary
Cards and Pricing
| Method | Endpoint | Scope | Description |
|---|---|---|---|
GET |
/api/v1/cards/search |
cards:read |
Search cards by name, game, set, rarity, or collector number. |
GET |
/api/v1/cards/{card_id} |
cards:read |
Fetch a card entity and variants. |
GET |
/api/v1/cards/{card_id}/price |
pricing:read |
Return Hydra Price for a card. |
POST |
/api/v1/cards/{card_id}/prices |
pricing:write |
Record a customer-provided price point. |
GET |
/api/v1/alerts/arbitrage |
pricing:read |
Return active cross-source arbitrage alerts. |
GET |
/api/v1/pricing/changes |
pricing:read |
Return price changes between two time windows. |
Portfolio
| Method | Endpoint | Scope | Description |
|---|---|---|---|
GET |
/api/v1/portfolio |
portfolio:read |
Return the caller’s holdings. |
POST |
/api/v1/portfolio/holdings |
portfolio:write |
Add a card to the caller’s portfolio. |
DELETE |
/api/v1/portfolio/holdings/{card_id} |
portfolio:write |
Remove or reduce a card holding. |
GET |
/api/v1/portfolio/stats |
portfolio:read |
Return basic valuation and allocation stats. |
GET |
/api/v1/portfolio/analytics |
portfolio:read |
Return Pro-only advanced analytics. |
POST |
/api/v1/portfolio/cataloging/check |
portfolio:read |
Check whether more cards can be cataloged under the caller’s tier. |
Trades
| Method | Endpoint | Scope | Description |
|---|---|---|---|
POST |
/api/v1/trades |
trades:write |
Create a pending trade proposal. |
GET |
/api/v1/trades |
trades:read |
List the caller’s proposals as proposer or receiver. |
GET |
/api/v1/trades/{trade_id} |
trades:read |
Fetch one trade proposal. |
POST |
/api/v1/trades/{trade_id}/accept |
trades:write |
Accept a pending trade as receiver. |
POST |
/api/v1/trades/{trade_id}/reject |
trades:write |
Reject a pending trade as receiver. |
POST |
/api/v1/trades/{trade_id}/cancel |
trades:write |
Cancel a pending trade as proposer. |
POST |
/api/v1/trades/{trade_id}/counter |
trades:write |
Counter a pending trade as receiver. |
GET |
/api/v1/trades/{trade_id}/value |
trades:read |
Return net trade value from the proposer’s perspective. |
Billing
| Method | Endpoint | Scope | Description |
|---|---|---|---|
GET |
/api/v1/billing/entitlements |
billing:read |
Return effective tier, plan, limits, and entitlements. |
POST |
/api/v1/billing/checkout/pro |
billing:write |
Create Stripe Checkout params/session for Hydra Pro. |
POST |
/api/v1/billing/checkout/iap |
billing:write |
Create Stripe Checkout params/session for an in-app product. |
POST |
/api/v1/billing/webhook |
Stripe signed | Apply Stripe subscription and payment events. |
WebSocket
| Endpoint | Auth | Description |
|---|---|---|
wss://api.hydra.gg/ws/prices |
Bearer token or negotiated session token | Subscribe to live card price updates. |
Cards and Pricing
Card Shape
{
"card_id": "card_black_lotus_lea",
"name": "Black Lotus",
"game": "mtg",
"set_code": "LEA",
"set_name": "Limited Edition Alpha",
"rarity": "mythic",
"condition": "near_mint",
"collector_number": "233",
"ticker": "MTG-LEA-BLACKLOTUS",
"variants": [
{
"variant_id": "var_standard",
"label": "Standard",
"finish": "standard",
"market_price": 120000,
"listed_price": 125000
}
],
"image_uri": "https://cdn.hydra.gg/cards/card_black_lotus_lea.png",
"created_at": "2026-02-14T00:00:00.000Z",
"updated_at": "2026-02-14T00:00:00.000Z"
}
Allowed values:
| Field | Values |
|---|---|
game |
mtg, pokemon, yugioh, lorcana, other |
rarity |
common, uncommon, rare, mythic, secret |
condition |
mint, near_mint, lightly_played, moderately_played, heavily_played, damaged |
finish |
standard, foil, etched, borderless, serialized |
price source |
tcgplayer, cardkingdom, ebay, private_auction |
Ticker generation format is GAME-SET-SANITIZEDNAME. Names are uppercased,
stripped to alphanumeric characters, and truncated to 10 characters.
Search Cards
curl "$HYDRA_API_BASE/api/v1/cards/search?q=black%20lotus&game=mtg&limit=10" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"items": [
{
"card_id": "card_black_lotus_lea",
"name": "Black Lotus",
"game": "mtg",
"set_code": "LEA",
"set_name": "Limited Edition Alpha",
"rarity": "mythic",
"ticker": "MTG-LEA-BLACKLOTUS"
}
],
"next_cursor": null,
"has_more": false
}
Get Hydra Price
The Hydra Price algorithm uses points from the last 30 days, filters outliers
with the IQR method, weights sold listings twice as strongly as active listings,
and reports confidence from 0 to 1 based on data volume and source diversity.
curl "$HYDRA_API_BASE/api/v1/cards/card_black_lotus_lea/price" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"card_id": "card_black_lotus_lea",
"price": 118750,
"confidence": 0.85,
"sources_used": 3,
"calculated_at": "2026-02-14T00:00:00.000Z"
}
SDK equivalent:
import {
createPriceHistory,
recordPrice,
calculateHydraPrice,
} from "card-trade-social";
const history = createPriceHistory("card_black_lotus_lea");
recordPrice(history, "tcgplayer", 118000, true);
recordPrice(history, "ebay", 120000, true);
recordPrice(history, "cardkingdom", 117500, false);
const hydraPrice = calculateHydraPrice(history);
Record a Price Point
curl "$HYDRA_API_BASE/api/v1/cards/card_black_lotus_lea/prices" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: price-card_black_lotus_lea-ebay-20260214T000000Z" \
-d '{
"source": "ebay",
"price": 120000,
"is_sold": true
}'
{
"card_id": "card_black_lotus_lea",
"points_recorded": 1,
"latest_point": {
"source": "ebay",
"price": 120000,
"is_sold": true,
"timestamp": "2026-02-14T00:00:00.000Z"
}
}
Negative prices are rejected with 400.
Arbitrage Alerts
min_spread_percentage defaults to 15.
curl "$HYDRA_API_BASE/api/v1/alerts/arbitrage?min_spread_percentage=20" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"items": [
{
"card_id": "card_sheoldred_dmu",
"card_name": "Sheoldred, the Apocalypse",
"low_source": "tcgplayer",
"low_price": 67.5,
"high_source": "ebay",
"high_price": 84.25,
"spread": 16.75,
"spread_percentage": 24.81
}
],
"next_cursor": null,
"has_more": false
}
Portfolio
Add a Holding
Free users can catalog up to 100 cards. Pro users can catalog unlimited cards.
Before adding large batches, call /api/v1/portfolio/cataloging/check.
curl "$HYDRA_API_BASE/api/v1/portfolio/holdings" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: holding-user_123-card_black_lotus_lea-1" \
-d '{
"card_id": "card_black_lotus_lea",
"quantity": 1,
"purchase_price": 95000
}'
{
"owner_id": "user_123",
"entry": {
"card_id": "card_black_lotus_lea",
"quantity": 1,
"purchase_price": 95000,
"added_at": "2026-02-14T00:00:00.000Z"
}
}
quantity must be positive. If the holding already exists, quantity is
incremented.
SDK equivalent:
import { createPortfolio, addCard } from "card-trade-social";
const portfolio = createPortfolio("user_123");
addCard(portfolio, card, 1, 95000);
Remove or Reduce a Holding
curl "$HYDRA_API_BASE/api/v1/portfolio/holdings/card_black_lotus_lea?quantity=1" \
-X DELETE \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"owner_id": "user_123",
"removed_card_id": "card_black_lotus_lea",
"remaining_quantity": 0
}
If the requested quantity is greater than or equal to the current quantity, the holding is removed.
Portfolio Stats
Portfolio value is calculated from the best variant market price adjusted by card condition:
| Condition | Multiplier |
|---|---|
mint |
1.0 |
near_mint |
0.9 |
lightly_played |
0.75 |
moderately_played |
0.6 |
heavily_played |
0.4 |
damaged |
0.25 |
curl "$HYDRA_API_BASE/api/v1/portfolio/stats" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"total_cards": 3,
"total_value": 225,
"allocations": [
{
"game": "mtg",
"value": 225,
"percentage": 100
}
]
}
Advanced Analytics
Advanced analytics require an active Hydra Pro entitlement:
portfolio.analytics.advanced.
curl "$HYDRA_API_BASE/api/v1/portfolio/analytics" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"total_cards": 3,
"total_value": 225,
"total_cost_basis": 200,
"unrealized_pnl": 25,
"unrealized_pnl_percentage": 12.5,
"concentration_score": 0.68,
"concentration_risk": "high",
"largest_position": {
"card_id": "card_black_lotus_lea",
"card_name": "Black Lotus",
"quantity": 2,
"market_value": 180,
"cost_basis": 140,
"unrealized_pnl": 40,
"weight_percentage": 80
},
"positions": [
{
"card_id": "card_black_lotus_lea",
"card_name": "Black Lotus",
"quantity": 2,
"market_value": 180,
"cost_basis": 140,
"unrealized_pnl": 40,
"weight_percentage": 80
},
{
"card_id": "card_sol_ring_cmd",
"card_name": "Sol Ring",
"quantity": 1,
"market_value": 45,
"cost_basis": 60,
"unrealized_pnl": -15,
"weight_percentage": 20
}
]
}
If the token is valid but the user is not entitled, the API returns 403 with
an error code such as entitlement_required.
Cataloging Check
curl "$HYDRA_API_BASE/api/v1/portfolio/cataloging/check" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "requested_cards": 25 }'
{
"allowed": true,
"current_cards": 42,
"requested_cards": 25,
"max_cards": 100,
"requires_pro": false
}
Trades
Trade cash adjustment is from the proposer’s perspective:
| Value | Meaning |
|---|---|
Positive cash_adjustment |
Proposer pays cash in addition to offered cards. |
Negative cash_adjustment |
Proposer receives cash in addition to requested cards. |
Default trade expiry is 72 hours.
Create a Trade
curl "$HYDRA_API_BASE/api/v1/trades" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: trade-user_123-user_456-20260214T000000Z" \
-d '{
"receiver_id": "user_456",
"offered_items": [
{ "card_id": "card_sol_ring_cmd", "quantity": 1 }
],
"requested_items": [
{ "card_id": "card_charizard_bs", "quantity": 1 }
],
"cash_adjustment": 50,
"message": "Adding cash to balance the condition gap.",
"expiry_hours": 48
}'
{
"trade_id": "trade_01J...",
"proposer_id": "user_123",
"receiver_id": "user_456",
"offered_items": [
{
"card_id": "card_sol_ring_cmd",
"quantity": 1
}
],
"requested_items": [
{
"card_id": "card_charizard_bs",
"quantity": 1
}
],
"cash_adjustment": 50,
"status": "pending",
"message": "Adding cash to balance the condition gap.",
"created_at": "2026-02-14T00:00:00.000Z",
"updated_at": "2026-02-14T00:00:00.000Z",
"expires_at": "2026-02-16T00:00:00.000Z"
}
Trade creation rejects:
| Rule | Error |
|---|---|
proposer_id equals receiver_id |
Cannot trade with yourself |
| Both item arrays are empty | Trade must include at least one item |
Any quantity is <= 0 |
Item quantity must be positive |
SDK equivalent:
import { createProposal } from "card-trade-social";
const trade = createProposal({
trade_id: "trade_01J...",
proposer_id: "user_123",
receiver_id: "user_456",
offered_items: [{ card: solRing, quantity: 1 }],
requested_items: [{ card: charizard, quantity: 1 }],
cash_adjustment: 50,
expiry_hours: 48,
});
Accept, Reject, Cancel, or Counter
Only the receiver can accept or reject a pending trade:
curl "$HYDRA_API_BASE/api/v1/trades/trade_01J.../accept" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
Only the proposer can cancel a pending trade:
curl "$HYDRA_API_BASE/api/v1/trades/trade_01J.../cancel" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
Only the receiver can counter a pending trade. Countering marks the original
trade as countered and creates a new pending proposal with
parent_trade_id set to the original trade.
curl "$HYDRA_API_BASE/api/v1/trades/trade_01J.../counter" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: counter-trade_01J-20260214T000000Z" \
-d '{
"offered_items": [
{ "card_id": "card_charizard_bs", "quantity": 1 }
],
"requested_items": [
{ "card_id": "card_sol_ring_cmd", "quantity": 2 }
],
"cash_adjustment": 0,
"message": "Can do this for two copies."
}'
Valid statuses are:
pending, accepted, rejected, countered, cancelled, expired
Attempts to act on a non-pending trade return 409.
Net Trade Value
curl "$HYDRA_API_BASE/api/v1/trades/trade_01J.../value" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"trade_id": "trade_01J...",
"net_value": 150,
"perspective": "proposer",
"meaning": "positive means the proposer pays or gives more value"
}
Formula:
requested item value - offered item value + cash_adjustment
Billing
Get Entitlements
curl "$HYDRA_API_BASE/api/v1/billing/entitlements" \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
{
"user_id": "user_123",
"tier": "pro",
"effective_tier": "pro",
"status": "active",
"current_period_end": "2026-03-14T00:00:00.000Z",
"cancel_at_period_end": false,
"entitlements": [
"portfolio.analytics.basic",
"portfolio.analytics.advanced",
"portfolio.catalog.basic",
"portfolio.catalog.unlimited",
"portfolio.catalog.bulk_import",
"portfolio.alerts.unlimited"
],
"cataloging_limits": {
"max_cards": 9007199254740991,
"bulk_import": true,
"scan_to_catalog": true
}
}
Create Hydra Pro Checkout
The API route should call Stripe server-side. The TypeScript helper returns
params that can be passed to stripe.checkout.sessions.create(...).
curl "$HYDRA_API_BASE/api/v1/billing/checkout/pro" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: checkout-pro-user_123-20260214T000000Z" \
-d '{
"billing_interval": "month",
"success_url": "https://app.example.com/billing/success",
"cancel_url": "https://app.example.com/billing/cancel"
}'
{
"checkout_session_id": "cs_test_123",
"url": "https://checkout.stripe.com/c/pay/cs_test_123",
"mode": "subscription"
}
Server-side helper usage:
import { createProCheckoutSession } from "card-trade-social";
const checkoutParams = createProCheckoutSession({
user_id: "user_123",
price_id: process.env.STRIPE_PRICE_PRO_MONTHLY!,
billing_interval: "month",
success_url: "https://app.example.com/billing/success",
cancel_url: "https://app.example.com/billing/cancel",
stripe_customer_id: "cus_123",
});
const session = await stripe.checkout.sessions.create(checkoutParams);
success_url and cancel_url must be absolute HTTP or HTTPS URLs.
Create In-App Purchase Checkout
curl "$HYDRA_API_BASE/api/v1/billing/checkout/iap" \
-X POST \
-H "Authorization: Bearer $HYDRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: checkout-iap-user_123-proxy_print_credit-20260214T000000Z" \
-d '{
"product_id": "proxy_print_credit",
"quantity": 3,
"success_url": "https://app.example.com/purchase/success",
"cancel_url": "https://app.example.com/purchase/cancel"
}'
{
"checkout_session_id": "cs_test_456",
"url": "https://checkout.stripe.com/c/pay/cs_test_456",
"mode": "payment"
}
quantity defaults to 1 and must be a positive integer.
Stripe Webhook
The webhook endpoint is not called by customer clients. Configure it in Stripe Dashboard as:
POST https://api.hydra.gg/api/v1/billing/webhook
Supported event families:
| Event family | Expected handling |
|---|---|
customer.subscription.* |
Map Stripe subscription snapshots into SubscriptionState. |
checkout.session.completed with mode=payment |
Create an InAppPurchaseReceipt for paid in-app purchases. |
Server-side helpers:
import {
subscriptionFromStripeSnapshot,
inAppPurchaseReceiptFromCheckoutSession,
} from "card-trade-social";
const subscription = subscriptionFromStripeSnapshot(
"user_123",
stripeSubscription,
[
process.env.STRIPE_PRICE_PRO_MONTHLY!,
process.env.STRIPE_PRICE_PRO_YEARLY!,
]
);
const receipt = inAppPurchaseReceiptFromCheckoutSession(stripeCheckoutSession);
Subscription statuses treated as active are active and trialing, provided
current_period_end is missing or still in the future. Expired or inactive Pro
subscriptions fall back to Free entitlements.
WebSocket Price Stream
Connect with a token negotiated by your backend or with an Authorization header if your WebSocket client supports it.
const ws = new WebSocket(
"wss://api.hydra.gg/ws/prices?symbols=MTG-LEA-BLACKLOTUS,MTG-CMD-SOLRING"
);
ws.addEventListener("message", (event) => {
const update = JSON.parse(event.data);
console.log(update);
});
Example message:
{
"type": "price.update",
"card_id": "card_black_lotus_lea",
"ticker": "MTG-LEA-BLACKLOTUS",
"price": 118750,
"confidence": 0.85,
"sources_used": 3,
"calculated_at": "2026-02-14T00:00:00.000Z"
}
Clients should reconnect with exponential backoff and resubscribe after a connection drop.
Operational Checklist
Before using the production API, confirm:
- Tokens are stored server-side or in a secure mobile token store.
- Write requests include an
Idempotency-Key. - Stripe webhook signatures are verified before processing payloads.
- Pro-only screens handle
403 entitlement_requiredby showing an upgrade path. - Prices and portfolio values are displayed with clear currency context.
- Trade flows confirm actor identity before accepting, rejecting, canceling, or countering proposals.
- Logs redact access tokens, Stripe secrets, customer emails, and payment IDs.
TypeScript Export Map
The package public entrypoint exports:
| Module | Key exports |
|---|---|
card |
createCard, generateTicker, adjustedPrice, bestVariantPrice, findVariant |
pricing |
createPriceHistory, recordPrice, calculateHydraPrice, detectArbitrage, calculatePriceChanges |
portfolio |
createPortfolio, addCard, removeCard, portfolioValue, getStats, getAdvancedAnalytics, canCatalogCards |
trade |
createProposal, acceptTrade, rejectTrade, cancelTrade, counterTrade, netTradeValue |
subscription |
createFreeSubscription, hasEntitlement, requireEntitlement, getCatalogingLimits, createProCheckoutSession, createInAppPurchaseCheckoutSession, subscriptionFromStripeSnapshot, inAppPurchaseReceiptFromCheckoutSession |