# Agencio Predict - API Specification

## Base URL

```
/api/predict/v1
```

## Authentication

All endpoints require Supabase JWT via `Authorization: Bearer <token>` header.
Public endpoints (marked) available without auth but rate-limited.

---

## 1. Events API

### List Events
```
GET /events
```

**Query Parameters:**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| category | string | all | Filter by category slug |
| status | string | ACTIVE | ACTIVE, RESOLVED_YES, RESOLVED_NO, ALL |
| sort | string | -updated_at | Field to sort by (prefix `-` for DESC) |
| min_probability | number | 0 | Minimum probability filter |
| max_probability | number | 1 | Maximum probability filter |
| trust_min | number | 0 | Minimum trust score |
| search | string | | Full-text search on title/description |
| page | number | 1 | Page number |
| limit | number | 20 | Items per page (max 100) |

**Response:**
```json
{
  "data": [
    {
      "id": "uuid",
      "title": "Will Iran escalation lead to oil price spike above $100?",
      "slug": "iran-oil-price-spike-100",
      "category": { "name": "Geopolitics", "slug": "geopolitics" },
      "current_probability": 0.72,
      "sentiment_score": 0.41,
      "confidence": "MEDIUM",
      "divergence": "HIGH",
      "trust_score": 0.58,
      "volatility": 0.34,
      "status": "ACTIVE",
      "resolution_date": "2026-06-01T00:00:00Z",
      "updated_at": "2026-03-24T10:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 142,
    "pages": 8
  }
}
```

### Get Event Detail
```
GET /events/:id
```

**Response:** Full event object including:
- All base fields
- Latest trust score breakdown
- Recent explanations (last 5)
- Active trust flags count
- Calibration data for this event's category

### Create Event
```
POST /events
```

**Body:**
```json
{
  "title": "Will TikTok be banned in the US by end of 2026?",
  "description": "Whether the US government will enforce a complete ban...",
  "category_id": "uuid",
  "resolution_criteria": "A federal ban is signed into law and enforced.",
  "resolution_date": "2026-12-31T23:59:59Z",
  "source_urls": ["https://polymarket.com/event/..."],
  "tags": ["tech", "regulation", "social-media"]
}
```

### Import Events from Prediction Markets (Admin)
```
POST /events/import
```

Dynamically imports events from Polymarket with live probabilities. **No hardcoded data.**

**Body:**
```json
{
  "source": "polymarket",
  "limit": 50,
  "minVolume": 10000,
  "minLiquidity": 5000
}
```

**Response:**
```json
{
  "success": true,
  "data": {
    "imported": 12,
    "updated": 8,
    "skipped": 30,
    "events": [
      {
        "slug": "polymarket-bitcoin-100k",
        "title": "Will Bitcoin exceed $100,000 by June 2026?",
        "probability": 0.62,
        "volume": 1250000,
        "source": "polymarket"
      }
    ]
  }
}
```

**Note:** The scheduler auto-syncs events every 15 minutes via the `sync-prediction-markets` job.

### Get Event Timeline
```
GET /events/:id/timeline
```

**Query Parameters:**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| from | ISO date | -7d | Start of time range |
| to | ISO date | now | End of time range |
| interval | string | 1h | Snapshot interval (5m, 15m, 1h, 4h, 1d) |

**Response:**
```json
{
  "event_id": "uuid",
  "snapshots": [
    {
      "timestamp": "2026-03-24T10:00:00Z",
      "probability": 0.68,
      "sentiment_score": 0.35,
      "trust_score": 0.62,
      "volume": 234
    }
  ]
}
```

---

## 2. Signals API

### Get Signals for Event
```
GET /events/:id/signals
```

**Query Parameters:**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| source | string | all | Filter by source |
| signal_type | string | all | Filter by type |
| from | ISO date | -24h | Start time |
| limit | number | 50 | Max results |

### Ingest Signal (internal / admin)
```
POST /signals
```

**Body:**
```json
{
  "event_id": "uuid",
  "source": "polymarket",
  "signal_type": "MARKET_PRICE",
  "value": 0.72,
  "source_url": "https://...",
  "raw_data": { "volume_24h": 125000 }
}
```

---

## 3. Explanations API

### Get Explanations
```
GET /events/:id/explanations
```

**Query Parameters:**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| trigger_type | string | all | Filter by trigger |
| limit | number | 10 | Max results |

**Response:**
```json
{
  "data": [
    {
      "id": "uuid",
      "trigger_type": "PROBABILITY_SHIFT",
      "headline": "Iran odds spike 27% after Reuters missile report",
      "summary": "Market probability jumped from 0.45 to 0.72 in 2 hours following a Reuters report citing anonymous Pentagon sources. Social sentiment remains cautious at 0.41, creating a HIGH divergence signal.",
      "factors": [
        { "factor": "Reuters report on missile activity", "impact": "HIGH", "direction": "UP" },
        { "factor": "Social sentiment cautious", "impact": "MEDIUM", "direction": "DOWN" },
        { "factor": "Historical pattern: similar spikes revert 60% within 48h", "impact": "MEDIUM", "direction": "DOWN" }
      ],
      "created_at": "2026-03-24T10:35:00Z"
    }
  ]
}
```

### Generate Explanation (admin / scheduled)
```
POST /events/:id/explain
```

Triggers AI explanation generation for the current state of the event.

---

## 4. Trust API

### Get Trust Score
```
GET /trust/:eventId
```

**Response:**
```json
{
  "event_id": "uuid",
  "overall_score": 0.58,
  "manipulation_risk": "HIGH",
  "flagged_activity": true,
  "breakdown": {
    "market_integrity": 0.45,
    "sentiment_authenticity": 0.72,
    "source_diversity": 0.65,
    "volume_consistency": 0.38,
    "temporal_consistency": 0.52
  },
  "flag_count": 3,
  "flags": [
    {
      "type": "INSIDER_TIMING",
      "severity": "ALERT",
      "description": "Large position opened 12 minutes before Reuters report published",
      "created_at": "2026-03-24T10:23:00Z"
    }
  ],
  "computed_at": "2026-03-24T10:40:00Z"
}
```

### Get Trust Flags
```
GET /trust/:eventId/flags
```

### Get Audit Trail
```
GET /trust/audit/:eventId
```

Returns full chronological audit trail of all trust-related activity.

### Subscribe to Alerts
```
POST /trust/alerts/subscribe
```

**Body:**
```json
{
  "event_ids": ["uuid1", "uuid2"],
  "severity_min": "WARNING",
  "channel": "webhook",
  "webhook_url": "https://..."
}
```

---

## 5. Marketing Prediction API

### Predict Campaign
```
POST /marketing/predict/campaign
```

**Body:**
```json
{
  "name": "Gen Z sneaker drop",
  "description": "Limited edition sneaker launch targeting Gen Z via TikTok",
  "target_audience": "Gen Z, 18-24, sneaker culture, US urban",
  "platform": "tiktok",
  "campaign_type": "launch",
  "persona_id": "uuid",
  "variants": [
    {
      "label": "A",
      "hook": "These sold out in 3 minutes last time",
      "content_description": "UGC-style unboxing with scarcity messaging"
    },
    {
      "label": "B",
      "hook": "The shoes your favorite creator is wearing",
      "content_description": "Creator endorsement with social proof"
    }
  ]
}
```

**Response:**
```json
{
  "campaign_id": "uuid",
  "overall_success_probability": 0.74,
  "recommended_variant": "B",
  "prediction_explanation": "Variant B leverages stronger emotional hook through creator social proof, which historically outperforms scarcity messaging with Gen Z on TikTok by 23%. Trend alignment score is 0.91 due to current creator-economy momentum.",
  "variants": [
    {
      "label": "A",
      "success_probability": 0.63,
      "predicted_engagement": 0.045,
      "predicted_conversion": 0.012,
      "scoring_factors": {
        "emotional_hook": 0.65,
        "trend_alignment": 0.72,
        "audience_fit": 0.78,
        "platform_fit": 0.80,
        "historical_pattern": 0.55
      },
      "reasoning": "Scarcity hooks perform well but are overused in sneaker vertical. Diminishing returns detected."
    },
    {
      "label": "B",
      "success_probability": 0.81,
      "predicted_engagement": 0.067,
      "predicted_conversion": 0.018,
      "scoring_factors": {
        "emotional_hook": 0.82,
        "trend_alignment": 0.91,
        "audience_fit": 0.85,
        "platform_fit": 0.88,
        "historical_pattern": 0.72
      },
      "reasoning": "Creator social proof aligns with current Gen Z trust patterns. Strong emotional resonance."
    }
  ],
  "factors": [
    { "factor": "trend_alignment", "score": 0.91, "impact": "positive", "detail": "Creator economy trending strongly" },
    { "factor": "audience_saturation", "score": 0.35, "impact": "negative", "detail": "Sneaker drop fatigue in target segment" }
  ]
}
```

### Predict Viral Potential
```
POST /marketing/predict/viral
```

**Body:**
```json
{
  "platform": "tiktok",
  "content_description": "POV: You open the box and it's the wrong color but you love it more",
  "hook": "Wait for the reveal...",
  "target_audience": "Gen Z",
  "content_type": "short_video"
}
```

### Record Campaign Outcome
```
POST /marketing/campaigns/:id/outcome
```

Records actual results for calibration. Used to improve future predictions.

---

## 6. Calibration API

### Get Calibration Report
```
GET /calibration/report
```

**Query Parameters:**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| category | string | all | Filter by category |
| window | string | 30D | Time window (7D, 30D, 90D, ALL_TIME) |

**Response:**
```json
{
  "window": "30D",
  "total_predictions": 342,
  "resolved_predictions": 198,
  "brier_score": 0.182,
  "accuracy_at_50": 0.71,
  "accuracy_at_70": 0.78,
  "accuracy_at_90": 0.91,
  "calibration_curve": [
    { "bucket": 0.1, "predicted_avg": 0.10, "actual_rate": 0.12, "count": 45 },
    { "bucket": 0.2, "predicted_avg": 0.20, "actual_rate": 0.18, "count": 38 },
    { "bucket": 0.5, "predicted_avg": 0.50, "actual_rate": 0.52, "count": 56 },
    { "bucket": 0.9, "predicted_avg": 0.90, "actual_rate": 0.91, "count": 22 }
  ],
  "by_category": [
    { "category": "Marketing", "brier_score": 0.145, "count": 89 },
    { "category": "Geopolitics", "brier_score": 0.210, "count": 67 }
  ]
}
```

---

## WebSocket (Realtime)

Subscribe to live updates via Supabase Realtime:

```javascript
const channel = supabase
  .channel('prediction-events')
  .on('postgres_changes', {
    event: 'UPDATE',
    schema: 'public',
    table: 'prediction_events',
    filter: `id=eq.${eventId}`
  }, (payload) => {
    // Live probability update
  })
  .on('postgres_changes', {
    event: 'INSERT',
    schema: 'public',
    table: 'prediction_explanations',
    filter: `event_id=eq.${eventId}`
  }, (payload) => {
    // New explanation generated
  })
  .subscribe();
```

---

## Rate Limits

| Tier | Requests/min | WebSocket connections |
|------|-------------|----------------------|
| Free | 30 | 2 |
| Pro | 300 | 20 |
| Enterprise | 3000 | unlimited |
| API | 1000 | N/A |

## Error Format

```json
{
  "error": {
    "code": "INSUFFICIENT_DATA",
    "message": "Not enough signals to generate prediction",
    "details": { "signals_required": 5, "signals_found": 2 }
  }
}
```
