# SEMIPRO Event Demand Intelligence API v1

Event Demand Intelligence converts a user-supplied cycling GPX into route-only performance intelligence: course character, terrain burden, decisive climbs, critical sections, likely duration band and the performance qualities the route rewards.

This is a public capability, not an athlete coaching endpoint. It does not read athlete records, prescribe training, predict outcomes or expose SEMIPRO's private coaching memory.

## Surfaces

| Surface | URL | Access |
|---|---|---|
| Public web tool | `https://semipro.coach/event-demand` | Free, low-volume |
| Marketplace origin | `POST https://semipro.coach/api/v1/event-demands` | Provider API key |
| Public result | `GET https://semipro.coach/api/v1/event-demands/{slug}` | Free |
| Direct agent endpoint | `https://api.semipro.coach/v1/event-demands` | MPP / HTTP 402 when launched |
| OpenAPI | `https://semipro.coach/docs/public-event-demand-openapi.yaml` | Public |

The marketplace origin and direct agent endpoint return the same `semipro_event_demand_v1` object. Payment and distribution are adapters around one portable SEMIPRO contract.

## Pricing

The v1 list price is **USD $0.10 per paid request**. A marketplace may present a different buyer price under its own commercial terms. Public browser use is free and tightly rate limited.

Paid calls require an `Idempotency-Key`. Repeating the same request with the same key returns the original result. Reusing a key for a different request returns `409 idempotency_conflict`.

The payment gateway must apply the same idempotency key to payment settlement. A network retry that returns an existing SEMIPRO result must not create a second charge.

## Request

Send JSON with either `gpx_xml` or `gpx_base64`. GPX is capped at 3 MB. URL fetching is deliberately excluded from v1 to avoid server-side request forgery and ambiguous source retention.

```http
POST /api/v1/event-demands HTTP/1.1
Host: semipro.coach
Authorization: Bearer <marketplace-provider-key>
Idempotency-Key: event-123-attempt-1
Content-Type: application/json

{
  "gpx_xml": "<?xml version=\"1.0\"?><gpx>...</gpx>",
  "event": {
    "name": "Example Gran Fondo",
    "type": "gran_fondo",
    "date": "2026-11-08",
    "start_time": "07:30",
    "course_format": "point_to_point"
  }
}
```

All event fields are optional. Supported type values include `road_race`, `criterium`, `time_trial`, `gran_fondo`, `sportive`, `gravel`, `mtb`, `mtb_xc`, `ultra`, `stage`, `triathlon_bike_leg` and `other`.

## Response

The response contains:

- stable contract and analysis version identifiers;
- a public result ID and share URL;
- route facts and a bounded elevation profile;
- course-level judgement;
- up to six ranked decisive climbs;
- critical segments and course sections;
- evidence, retention and uncertainty statements.

Raw GPX, trackpoints and start/end coordinates are not returned or retained. The persisted public object contains only derived route information.

```json
{
  "object": "semipro.event_demand_analysis",
  "contract_version": "semipro_event_demand_v1",
  "capability": "event.demand",
  "id": "K7d9R2pQa1xz",
  "public_url": "https://semipro.coach/event-demand/K7d9R2pQa1xz",
  "route": {
    "distance_km": 156.4,
    "elevation_gain_m": 2380
  },
  "judgement": {
    "primary_demand": "repeatable rolling pressure before a decisive sustained climb",
    "secondary_demand": "durability so the final third still matters"
  },
  "evidence": {
    "source": "user-supplied GPX",
    "derived_only": true,
    "raw_gpx_retained": false
  }
}
```

## Payment architecture

SEMIPRO supports two paid machine lanes:

1. A marketplace authenticates to the origin using a scoped provider key. It bills its own buyer and settles with SEMIPRO according to the marketplace agreement.
2. A Cloudflare Worker at `api.semipro.coach` issues an MPP `402 Payment Required` challenge. After verifying payment, it forwards the request to the private origin with a gateway token and payment receipt reference.

The origin never accepts an MPP assertion directly from an untrusted caller. The gateway must add:

```http
X-SEMIPRO-Gateway-Token: <secret stored in Cloudflare and Netlify>
X-SEMIPRO-Payment-Receipt: <verified receipt reference>
```

The generic Cloudflare `mpp-proxy` grants a one-hour session after payment, so it is not the v1 choice for strict per-request pricing. The production gateway should use a custom Worker charge route. A funded MPP session can be added later for high-volume callers.

Secrets must live in Cloudflare/Netlify secret stores, never source control. Marketplace keys should be separate per distributor and independently revocable.

## Status and error contract

| Status | Meaning |
|---|---|
| `200` | Analysis or idempotent replay returned |
| `400` | Invalid JSON or required idempotency key missing |
| `402` | Payment or provider credential required |
| `403` | Free route called outside the SEMIPRO web surface |
| `409` | Idempotency key reused for a different request |
| `413` | GPX exceeds 3 MB |
| `422` | GPX cannot produce a valid route |
| `429` | Rate limit reached |
| `503` | Route analysis temporarily unavailable |

No charge should be represented as a successful capability call unless an analysis result is returned. Payment-gateway failures and compensation policy remain the responsibility of the gateway integration.

## Versions

- Contract: `semipro_event_demand_v1`
- Current analysis: `route-demands-v2+lens1-v1`

The contract version changes only for breaking response changes. The analysis version can change when SEMIPRO improves route judgement while retaining the v1 response shape.
