Files
fuel-price/docs/api-reference.md
Ovidiu U 4f57c97015
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
docs: add comprehensive API reference documentation
Adds complete API reference (api-reference.md) covering all endpoints: /api/stations (nearby search with postcode/lat+lng), /api/stats/searches (aggregated search stats), /api/prediction (7-day price forecast with multi-signal breakdown), and auth routes (register/login/logout/me). Includes request/response examples, error shapes, fuel type aliases, and signal structure details. Also removes unused AccountWidget from admin panel, disables BrentPriceChartWidget discovery, and adds searches stat to StatsOverviewWidget.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 19:34:32 +01:00

354 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# FuelAlert API Reference
Base URL: `https://fuel-price.test/api`
All endpoints return JSON. No auth required on public endpoints (same-origin only for now — token auth planned).
---
## Stations
### GET `/api/stations`
Returns nearby petrol stations with live prices for a given fuel type.
**Location — provide one of:**
| Parameter | Type | Description |
|---|---|---|
| `postcode` | string | UK postcode, outcode, or place name (resolved via postcodes.io) |
| `lat` + `lng` | float | Decimal lat/lng (required if no postcode) |
**Optional:**
| Parameter | Type | Default | Notes |
|---|---|---|---|
| `fuel_type` | string | — | **Required.** See fuel type aliases below |
| `radius` | float | `10.0` | Search radius in km (0.150) |
| `sort` | string | `"price"` | `"price"` or `"distance"` |
| `pricing_mode` | string | — | `"pump"` (reserved, no effect yet) |
**Fuel type aliases** (`fuel_type` accepts any of these):
| Alias | Maps to |
|---|---|
| `petrol`, `unleaded`, `e10` | E10 (standard unleaded) |
| `premium_unleaded`, `e5` | E5 (super unleaded) |
| `diesel`, `b7_standard` | B7 Standard (standard diesel) |
| `premium_diesel`, `b7_premium` | B7 Premium (premium diesel) |
| `b10` | B10 (biodiesel blend) |
| `hvo` | HVO (hydrotreated vegetable oil) |
**Example request:**
```
GET /api/stations?postcode=SW1A1AA&fuel_type=petrol&radius=5&sort=price
GET /api/stations?lat=51.5074&lng=-0.1278&fuel_type=diesel&radius=10&sort=distance
```
**Response:**
```json
{
"data": [
{
"station_id": "0028acef5f3afc41...",
"name": "Alex Fuel Station",
"brand": "BP",
"is_supermarket": false,
"address": "123 High Street, London",
"postcode": "SW1A 1AA",
"lat": 51.5074,
"lng": -0.1278,
"distance_km": 1.23,
"fuel_type": "e10",
"price_pence": 14390,
"price": 143.9,
"price_updated_at": "2026-04-05T08:00:00.000Z"
}
],
"meta": {
"count": 12,
"fuel_type": "e10",
"radius_km": 10.0,
"lowest_pence": 14290,
"highest_pence": 14890,
"cheapest_price_pence": 14290,
"avg_pence": 14523.5
}
}
```
**Notes:**
- `price_pence` is the raw integer (e.g. `14390` = 143.90p). `price` is the float in pence (e.g. `143.9`).
- Closed stations (temporary or permanent) are excluded.
- Every call logs a search record used by the stats endpoint.
**Error — postcode not found:**
```json
{ "errors": { "postcode": ["Postcode not found."] } }
```
**Error — missing location:**
```json
{ "errors": { "lat": ["The lat field is required when postcode is not present."] } }
```
---
## Stats
### GET `/api/stats/searches`
Aggregate search statistics. Useful for a "social proof" counter on the frontend.
| Parameter | Type | Default | Notes |
|---|---|---|---|
| `period` | string | `"week"` | `"week"` (7 days) or `"month"` (30 days) |
**Example request:**
```
GET /api/stats/searches
GET /api/stats/searches?period=month
```
**Response:**
```json
{
"total_searches": 3842,
"unique_searchers": 1201,
"avg_results": 14.3,
"avg_lowest_price": 141.2,
"avg_highest_price": 148.7,
"avg_price": 144.9,
"period": "week",
"message": "Helped 1201 drivers find cheaper fuel this week so far!"
}
```
**Notes:**
- `avg_lowest_price`, `avg_highest_price`, `avg_price` are in **pence** as floats (e.g. `141.2` = 141.2p).
- `unique_searchers` is based on hashed IP — approximate unique users.
---
## Prediction
### GET `/api/prediction`
National (or regional) fuel price direction forecast for the next 7 days, based on live price data signals.
| Parameter | Type | Description |
|---|---|---|
| `fuel_type` | string | **Required.** Same aliases as `/api/stations` |
| `lat` | float | Optional. Enables regional momentum signal |
| `lng` | float | Optional. Enables regional momentum signal |
**Example request:**
```
GET /api/prediction?fuel_type=diesel
GET /api/prediction?fuel_type=petrol&lat=51.5074&lng=-0.1278
```
**Response:**
```json
{
"fuel_type": "e10",
"current_avg": 143.9,
"predicted_direction": "down",
"predicted_change_pence": -2.1,
"confidence_score": 74.5,
"confidence_label": "high",
"action": "wait",
"reasoning": "National prices have been falling at 0.3p/day. Supermarkets led last week — independents typically follow within 48h.",
"prediction_horizon_days": 7,
"region_key": "national",
"methodology": "multi_signal_live_fallback",
"signals": {
"trend": {
"score": -0.8,
"confidence": 0.92,
"direction": "down",
"detail": "Slope: -0.30p/day over 5 days (R²=0.92) [Adaptive lookback active]",
"data_points": 5,
"enabled": true,
"slope": -0.3,
"r_squared": 0.92
},
"day_of_week": {
"score": 0.0,
"confidence": 0.0,
"direction": "stable",
"detail": "Insufficient history for day-of-week pattern.",
"data_points": 0,
"enabled": true
},
"brand_behaviour": {
"score": -1.0,
"confidence": 0.6,
"direction": "down",
"detail": "Supermarkets fell 3.5p vs majors 0.5p (divergence: 3.0p). Expect majors to follow.",
"data_points": 2800,
"enabled": true
},
"national_momentum": {
"score": 0.0,
"confidence": 0.0,
"direction": "stable",
"detail": "National momentum disabled for national predictions",
"data_points": 0,
"enabled": false
},
"regional_momentum": {
"score": 0.0,
"confidence": 0.0,
"direction": "stable",
"detail": "No coordinates provided for regional momentum analysis",
"data_points": 0,
"enabled": false
},
"price_stickiness": {
"score": 0.05,
"confidence": 0.8,
"direction": "stable",
"detail": "Sticky prices (avg hold: 5.2 days) — more predictable.",
"data_points": 160,
"enabled": true
}
}
}
```
**Key fields:**
| Field | Values | Meaning |
|---|---|---|
| `predicted_direction` | `"up"`, `"down"`, `"stable"` | Price trend direction |
| `action` | `"fill_now"`, `"wait"`, `"no_signal"` | Consumer-facing recommendation |
| `confidence_label` | `"high"` (≥70), `"medium"` (≥40), `"low"` (<40) | Signal strength |
| `predicted_change_pence` | float | Expected p/litre change over 7 days |
| `current_avg` | float | Current national average in pence (e.g. `143.9` = 143.9p) |
**Signal structure** (each signal in `signals`):
| Field | Type | Notes |
|---|---|---|
| `score` | float (-1.0 to 1.0) | Negative = falling pressure, positive = rising |
| `confidence` | float (0.01.0) | How reliable this signal is |
| `direction` | `"up"` / `"down"` / `"stable"` | Signal direction |
| `detail` | string | Human-readable explanation |
| `data_points` | int | Number of price records used |
| `enabled` | bool | False if signal was skipped (missing data/coords) |
**Error — unknown fuel type:**
```json
{ "errors": { "fuel_type": ["Unknown fuel type. Use: diesel, petrol, e10, e5, hvo, b10."] } }
```
---
## Auth
> **Note:** Auth routes are implemented (`AuthController` exists) but not yet wired into `routes/api.php`. Add when token-based access is needed.
### POST `/api/auth/register`
Create a new account and receive a Sanctum token.
**Body (JSON):**
```json
{
"name": "Jane Smith",
"email": "jane@example.com",
"password": "secret123",
"password_confirmation": "secret123"
}
```
**Response `201`:**
```json
{
"token": "1|abc123...",
"user": {
"id": 42,
"name": "Jane Smith",
"email": "jane@example.com",
"created_at": "2026-04-05T10:00:00.000000Z"
}
}
```
---
### POST `/api/auth/login`
**Body (JSON):**
```json
{
"email": "jane@example.com",
"password": "secret123"
}
```
**Response `200`:**
```json
{
"token": "1|abc123...",
"user": { ... }
}
```
**Response `401` (wrong credentials):**
```json
{ "message": "Invalid credentials." }
```
---
### POST `/api/auth/logout`
Revokes the current token.
**Headers:** `Authorization: Bearer {token}`
**Response `200`:**
```json
{ "message": "Logged out." }
```
---
### GET `/api/auth/me`
Returns the authenticated user.
**Headers:** `Authorization: Bearer {token}`
**Response `200`:** Full `User` model JSON.
---
## Using the Token (when auth is wired up)
```
Authorization: Bearer 1|abc123...
```
All protected routes must include this header.
---
## Error Shapes
**Validation error (422):**
```json
{
"message": "The fuel type field is required.",
"errors": {
"fuel_type": ["The fuel type field is required."]
}
}
```
**Unauthenticated (401):**
```json
{ "message": "Unauthenticated." }
```