# Polygon.io BYOK & Tier Detection

This document describes the Polygon.io (now Massive) integration with BYOK (Bring Your Own Key) support and automatic tier detection.

## Overview

Polygon.io provides comprehensive market data across multiple asset classes. The platform supports:

1. **Platform-level API key** — Configured by admins, used as fallback
2. **User BYOK keys** — Users can provide their own Polygon API key
3. **Automatic tier detection** — System probes to detect subscription capabilities

## Pricing Tiers (as of May 2026)

| Tier | Price | Rate Limit | Key Features |
|------|-------|------------|--------------|
| **Basic** | $0/month | 5 calls/min | EOD aggregates, 2-year history |
| **Starter** | $29-49/month | Higher limits | Snapshots, technical indicators, Options Greeks |
| **Developer** | $79/month | Higher limits | Trades access, extended hours, short interest |
| **Advanced** | $199/month | Highest limits | Real-time quotes, financials, insider transactions |
| **Business Bundle** | ~$8.50/month | Enterprise | All features (enterprise pricing) |

## Architecture

```
┌──────────────────────────────────────────────────────────────────┐
│                     Polygon API Request                          │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. getConfig(userId?)                                          │
│     ├─ Check user BYOK key (if userId provided)                 │
│     │   └─ getBYOKKey(userId, 'polygon')                        │
│     │       └─ Return encrypted key from billing.user_byok_keys │
│     │                                                           │
│     ├─ Fall back to platform key                                │
│     │   └─ getSecret('polygon', 'POLYGON_API_KEY')              │
│     │       └─ DB (predict.platform_integration_secrets) → env  │
│     │                                                           │
│     └─ Return null if no key available                          │
│                                                                  │
│  2. Make API request with resolved key                          │
│                                                                  │
│  3. Return data or graceful fallback (empty array/null)         │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘
```

## User BYOK Configuration

### Adding a Polygon Key

Users can add their own Polygon API key at `/settings/ai-billing/byok`:

1. Navigate to **Settings** → **AI Billing** → **BYOK Keys**
2. Select **Massive (Polygon.io)**
3. Enter API key (20-64 characters)
4. Optionally add a label
5. Click **Add Key**

The system validates the key by making a test API call to `/v2/aggs/ticker/AAPL/prev`.

### Key Priority

When making Polygon API calls:

1. **User BYOK key** (if `userId` provided and user has valid key)
2. **Platform key** (configured by admin)
3. **No key** (function returns empty/null gracefully)

### Security

- Keys encrypted with AES-256-GCM
- Per-user salt for encryption
- Only last 4 characters visible in UI
- Keys stored in `billing.user_byok_keys` table

## Tier Detection

### How It Works

The system probes 9+ API endpoints to determine which tier the API key has access to:

| Endpoint | Feature | Tier Required |
|----------|---------|---------------|
| `/v2/aggs/ticker/AAPL/prev` | Aggregates | Basic |
| `/v2/snapshot/locale/us/markets/stocks/tickers/AAPL` | Snapshots | Starter |
| `/v1/indicators/rsi/AAPL` | Technical indicators | Starter |
| `/v2/ticks/stocks/trades/AAPL/{date}` | Trades | Developer |
| `/v2/last/nbbo/AAPL` | Quotes | Advanced |
| `/vX/reference/financials` | Financials | Advanced |
| `/v2/reference/shorts/AAPL` | Short interest | Developer |
| `/v2/reference/insiders/AAPL` | Insider transactions | Developer |
| `/v1/reference/tickers/TSLA/news` | Treasury yields | Developer |

### Caching

- **In-memory cache**: 24 hours
- **Database cache**: 7 days (in `predict.integration_tier_capabilities`)
- Cache invalidated when key changes

### Admin UI

Admins can view tier capabilities at `/admin/integrations`:

1. Find **Polygon.io** integration
2. Click **Tier Info** button
3. Modal shows:
   - Detected tier
   - Rate limits
   - Available features by asset class
   - Last detection timestamp
4. Click **Refresh** to re-probe

## API Functions

### Core Functions

```typescript
// Check if Polygon is configured (BYOK or platform)
await isPolygonConfigured(userId?: string): Promise<boolean>

// Check if user has their own BYOK key
await hasUserPolygonKey(userId: string): Promise<boolean>

// Detect tier capabilities
await detectPolygonTier(userId?: string): Promise<PolygonTierCapabilities>

// Check specific feature availability
await hasPolygonFeature(feature: PolygonFeature, userId?: string): Promise<boolean>
```

### Data Functions with BYOK Support

```typescript
// Forex history (uses BYOK if userId provided)
await fetchPolygonForexHistory(pair: string, days: number, options?: { userId?: string })

// Stock aggregates
await fetchPolygonStockAggregates(ticker: string, days: number, timeframe: string, options?: { userId?: string })

// Crypto aggregates
await fetchPolygonCryptoAggregates(pair: string, days: number, timeframe: string, options?: { userId?: string })

// Equity trades (whale/bot classification)
await fetchPolygonTrades(ticker: string, from: string, to: string, limit: number, options?: { userId?: string })
```

## Integration Points

### Price Service

The `getHistoricalPrices()` function automatically uses BYOK keys when `userId` is provided:

```typescript
// packages/be/src/trading/data/price-service.ts
const history = await getHistoricalPrices('EURUSD', 30, 'forex', { userId: 'user-123' });
// → Uses user's Polygon BYOK key if available, else platform key
```

### Algorithm Builder

DSL evaluations that fetch prices automatically thread `userId` through:

```typescript
// User's algorithms use their BYOK key for data
const price = await getPrice('AAPL', { userId: run.userId });
```

### Tier-Aware Fallbacks

Functions check tier capabilities before calling:

```typescript
// Check before calling trades endpoint (Developer+ required)
if (await hasPolygonFeature('trades', userId)) {
  const trades = await fetchPolygonTrades(symbol, from, to, limit, { userId });
} else {
  // Fall back to alternative data source
  const trades = await fetchBinanceTrades(symbol, from, to, limit);
}
```

## Database Schema

### BYOK Keys Table

```sql
-- billing.user_byok_keys
CREATE TABLE billing.user_byok_keys (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL REFERENCES auth.users(id),
  organization_id TEXT REFERENCES auth.organizations(id),
  provider TEXT NOT NULL,  -- 'anthropic' | 'openai' | 'polygon'
  encrypted_key TEXT NOT NULL,
  key_iv TEXT NOT NULL,
  key_tag TEXT NOT NULL,
  key_last_four TEXT NOT NULL,
  label TEXT,
  is_valid BOOLEAN DEFAULT true,
  last_validated_at TIMESTAMPTZ,
  validation_error TEXT,
  last_used_at TIMESTAMPTZ,
  total_calls INTEGER DEFAULT 0,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(user_id, provider)
);
```

### Tier Capabilities Table

```sql
-- predict.integration_tier_capabilities
CREATE TABLE predict.integration_tier_capabilities (
  id TEXT PRIMARY KEY,
  integration_id TEXT NOT NULL,
  tier TEXT NOT NULL,
  capabilities JSONB NOT NULL DEFAULT '{}',
  detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  CONSTRAINT uq_integration_tier UNIQUE (integration_id)
);
```

## API Endpoints

### User BYOK Management

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/predict/v1/byok` | List user's BYOK keys |
| POST | `/api/predict/v1/byok` | Add BYOK key (validates first) |
| PUT | `/api/predict/v1/byok/:provider` | Update key label |
| DELETE | `/api/predict/v1/byok/:provider` | Remove BYOK key |
| POST | `/api/predict/v1/byok/:provider/validate` | Re-validate key |
| POST | `/api/predict/v1/byok/:provider/test` | Test key with minimal call |

### Admin Tier Detection

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/predict/v1/admin/integrations` | List all integrations with tier info |
| POST | `/api/predict/v1/admin/integrations/polygon/detect-tier` | Force tier detection |

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `POLYGON_API_KEY` | No | Platform-level Polygon API key (fallback) |
| `BROKER_ENCRYPTION_KEY` | Yes | 32-byte hex key for BYOK encryption |

## Troubleshooting

### "No Polygon key configured"

**Cause**: Neither user BYOK nor platform key is set.

**Solution**:
1. User: Add key at `/settings/ai-billing/byok`
2. Admin: Add platform key at `/admin/integrations`

### "Polygon validation failed"

**Cause**: Invalid API key format or key rejected by Polygon.

**Solution**:
1. Verify key is 20-64 characters
2. Check key on Polygon dashboard
3. Ensure key is active and not rate-limited

### "Feature not available with current tier"

**Cause**: API key tier doesn't include the requested feature.

**Solution**:
1. Check tier at `/admin/integrations` → Polygon → Tier Info
2. Upgrade Polygon subscription
3. Or use alternative data source

### "Rate limited"

**Cause**: Exceeded calls/minute for tier.

**Solution**:
1. Wait and retry
2. Upgrade to higher tier
3. Implement request caching

## Related Documentation

- [Integration Registry](./internal/integrations/26-integration-registry.md) — Full integration catalog
- [Platform Integration Secrets](./internal/integrations/73-platform-integration-secrets.md) — Admin key management
- [AI Billing Credits Plan](./internal/ai-ml/42-ai-billing-credits-plan.md) — BYOK billing model

## File References

| File | Purpose |
|------|---------|
| `packages/be/src/integrations/polygon.ts` | Core Polygon integration |
| `packages/be/src/billing/services/byok-service.ts` | BYOK key management |
| `packages/be/src/trading/data/price-service.ts` | Price service with BYOK support |
| `apps/web/src/app/settings/ai-billing/byok/page.tsx` | User BYOK UI |
| `apps/web/src/app/admin/integrations/page.tsx` | Admin integrations UI |
| `db/migrations/273_polygon_tier_capabilities.sql` | Tier capabilities table |
