# Gas Stations Feature - API Documentation

## Overview

Complete API reference for the Gas Stations feature. All endpoints require JWT authentication via Auth0. User data is automatically isolated by user_id extracted from the JWT token.

## Authentication

**Method**: JWT Bearer Token

**Header Format**:
```
Authorization: Bearer {jwt_token}
```

**Token Source**: Auth0 authentication flow

**User Identification**: Extracted from token's `sub` claim

## Base URL

**Development**: `https://motovaultpro.com/api/stations`

**Production**: `https://motovaultpro.com/api/stations`

## Endpoints

### Search Nearby Stations

Search for gas stations near a location using Google Maps Places API.

**Endpoint**: `POST /api/stations/search`

**Authentication**: Required (JWT)

**Request Body**:
```json
{
  "latitude": 37.7749,
  "longitude": -122.4194,
  "radius": 5000,
  "fuelType": "regular"
}
```

**Request Schema**:
| Field | Type | Required | Constraints | Description |
|-------|------|----------|-------------|-------------|
| latitude | number | Yes | -90 to 90 | Search center latitude |
| longitude | number | -180 to 180 | Yes | Search center longitude |
| radius | number | No | 100 to 50000 | Search radius in meters (default: 5000) |
| fuelType | string | No | - | Fuel type filter (e.g., "regular", "premium", "diesel") |

**Response**: `200 OK`
```json
{
  "stations": [
    {
      "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
      "name": "Shell Gas Station",
      "address": "123 Main St, San Francisco, CA 94102",
      "latitude": 37.7750,
      "longitude": -122.4195,
      "rating": 4.2,
      "photoReference": "/api/stations/photo/{reference}",
      "distance": 150
    }
  ],
  "searchLocation": {
    "latitude": 37.7749,
    "longitude": -122.4194
  },
  "searchRadius": 5000,
  "timestamp": "2025-01-15T10:30:00.000Z"
}
```

**Response Schema**:
| Field | Type | Description |
|-------|------|-------------|
| stations | Station[] | Array of nearby stations, sorted by distance |
| stations[].placeId | string | Unique Google Place ID |
| stations[].name | string | Station name |
| stations[].address | string | Full address |
| stations[].latitude | number | Station latitude |
| stations[].longitude | number | Station longitude |
| stations[].rating | number | Google rating (0-5) |
| stations[].photoReference | string | Photo URL (nullable) |
| stations[].distance | number | Distance from search location (meters) |
| searchLocation | object | Original search coordinates |
| searchRadius | number | Actual search radius used |
| timestamp | string | ISO 8601 timestamp |

**Error Responses**:

**400 Bad Request** - Invalid input
```json
{
  "error": "Bad Request",
  "message": "Latitude and longitude are required"
}
```

**401 Unauthorized** - Missing or invalid JWT
```json
{
  "error": "Unauthorized",
  "message": "Valid JWT token required"
}
```

**502 Bad Gateway** - Google Maps API unavailable
```json
{
  "error": "Service unavailable",
  "message": "Unable to search stations. Please try again later."
}
```

**500 Internal Server Error**
```json
{
  "error": "Internal server error",
  "message": "Failed to search stations"
}
```

**Rate Limits**: Inherits Google Maps API limits (1000 requests/day free tier)

**Performance**: 500-1500ms (includes Google API call)

**Example cURL**:
```bash
curl -X POST https://motovaultpro.com/api/stations/search \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "latitude": 37.7749,
    "longitude": -122.4194,
    "radius": 5000
  }'
```

---

### Save Station

Save a station to user's favorites with optional metadata.

**Endpoint**: `POST /api/stations/save`

**Authentication**: Required (JWT)

**Request Body**:
```json
{
  "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
  "nickname": "My Regular Station",
  "notes": "Always has cheapest premium gas",
  "isFavorite": true
}
```

**Request Schema**:
| Field | Type | Required | Constraints | Description |
|-------|------|----------|-------------|-------------|
| placeId | string | Yes | Must exist in cache | Google Place ID from search results |
| nickname | string | No | Max 255 chars | Custom name for station |
| notes | string | No | Max 5000 chars | Personal notes |
| isFavorite | boolean | No | Default: false | Mark as favorite |

**Response**: `201 Created`
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userId": "auth0|123456789",
  "stationId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
  "nickname": "My Regular Station",
  "notes": "Always has cheapest premium gas",
  "isFavorite": true,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:30:00.000Z",
  "station": {
    "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "name": "Shell Gas Station",
    "address": "123 Main St, San Francisco, CA 94102",
    "latitude": 37.7750,
    "longitude": -122.4195,
    "rating": 4.2,
    "photoReference": "/api/stations/photo/{reference}"
  }
}
```

**Response Schema**:
| Field | Type | Description |
|-------|------|-------------|
| id | string (UUID) | Saved station record ID |
| userId | string | User ID from JWT token |
| stationId | string | Google Place ID |
| nickname | string | Custom name (nullable) |
| notes | string | Personal notes (nullable) |
| isFavorite | boolean | Favorite flag |
| createdAt | string | ISO 8601 timestamp |
| updatedAt | string | ISO 8601 timestamp |
| station | Station | Enriched station data from cache |

**Error Responses**:

**400 Bad Request** - Missing placeId
```json
{
  "error": "Bad Request",
  "message": "Place ID is required"
}
```

**404 Not Found** - Station not in cache
```json
{
  "error": "Not Found",
  "message": "Station not found. Please search for stations first."
}
```

**409 Conflict** - Station already saved
```json
{
  "error": "Conflict",
  "message": "Station already saved"
}
```

**500 Internal Server Error**
```json
{
  "error": "Internal server error",
  "message": "Failed to save station"
}
```

**Rate Limits**: None (database operation)

**Performance**: 50-100ms

**Example cURL**:
```bash
curl -X POST https://motovaultpro.com/api/stations/save \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "nickname": "My Regular Station",
    "isFavorite": true
  }'
```

---

### Get Saved Stations

Retrieve all stations saved by the authenticated user.

**Endpoint**: `GET /api/stations/saved`

**Authentication**: Required (JWT)

**Request Parameters**: None

**Response**: `200 OK`
```json
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "userId": "auth0|123456789",
    "stationId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "nickname": "My Regular Station",
    "notes": "Always has cheapest premium gas",
    "isFavorite": true,
    "createdAt": "2025-01-15T10:30:00.000Z",
    "updatedAt": "2025-01-15T10:30:00.000Z",
    "station": {
      "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
      "name": "Shell Gas Station",
      "address": "123 Main St, San Francisco, CA 94102",
      "latitude": 37.7750,
      "longitude": -122.4195,
      "rating": 4.2,
      "photoReference": "/api/stations/photo/{reference}"
    }
  }
]
```

**Response Schema**: Array of saved stations (same schema as POST /save response)

**Error Responses**:

**401 Unauthorized**
```json
{
  "error": "Unauthorized",
  "message": "Valid JWT token required"
}
```

**500 Internal Server Error**
```json
{
  "error": "Internal server error",
  "message": "Failed to get saved stations"
}
```

**Rate Limits**: None

**Performance**: 50-100ms

**Example cURL**:
```bash
curl -X GET https://motovaultpro.com/api/stations/saved \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
```

---

### Get Specific Saved Station

Retrieve a specific saved station by Google Place ID.

**Endpoint**: `GET /api/stations/saved/:placeId`

**Authentication**: Required (JWT)

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| placeId | string | Yes | Google Place ID |

**Response**: `200 OK`
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userId": "auth0|123456789",
  "stationId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
  "nickname": "My Regular Station",
  "notes": "Always has cheapest premium gas",
  "isFavorite": true,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:30:00.000Z",
  "station": {
    "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "name": "Shell Gas Station",
    "address": "123 Main St, San Francisco, CA 94102",
    "latitude": 37.7750,
    "longitude": -122.4195,
    "rating": 4.2,
    "photoReference": "/api/stations/photo/{reference}"
  }
}
```

**Error Responses**:

**404 Not Found** - Station not saved or doesn't belong to user
```json
{
  "error": "Not Found",
  "message": "Saved station not found"
}
```

**500 Internal Server Error**
```json
{
  "error": "Internal server error",
  "message": "Failed to get saved station"
}
```

**Rate Limits**: None

**Performance**: 50-100ms

**Example cURL**:
```bash
curl -X GET https://motovaultpro.com/api/stations/saved/ChIJN1t_tDeuEmsRUsoyG83frY4 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
```

---

### Update Saved Station

Update metadata for a saved station.

**Endpoint**: `PATCH /api/stations/saved/:placeId`

**Authentication**: Required (JWT)

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| placeId | string | Yes | Google Place ID |

**Request Body** (all fields optional):
```json
{
  "nickname": "Updated Nickname",
  "notes": "Updated notes",
  "isFavorite": false
}
```

**Request Schema**:
| Field | Type | Required | Constraints | Description |
|-------|------|----------|-------------|-------------|
| nickname | string | No | Max 255 chars | Updated custom name |
| notes | string | No | Max 5000 chars | Updated notes |
| isFavorite | boolean | No | - | Updated favorite flag |

**Response**: `200 OK`
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userId": "auth0|123456789",
  "stationId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
  "nickname": "Updated Nickname",
  "notes": "Updated notes",
  "isFavorite": false,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T11:00:00.000Z",
  "station": {
    "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "name": "Shell Gas Station",
    "address": "123 Main St, San Francisco, CA 94102",
    "latitude": 37.7750,
    "longitude": -122.4195,
    "rating": 4.2,
    "photoReference": "/api/stations/photo/{reference}"
  }
}
```

**Error Responses**:

**404 Not Found** - Station not saved or doesn't belong to user
```json
{
  "error": "Not Found",
  "message": "Saved station not found"
}
```

**500 Internal Server Error**
```json
{
  "error": "Internal server error",
  "message": "Failed to update saved station"
}
```

**Rate Limits**: None

**Performance**: 50-100ms

**Example cURL**:
```bash
curl -X PATCH https://motovaultpro.com/api/stations/saved/ChIJN1t_tDeuEmsRUsoyG83frY4 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "nickname": "Updated Nickname",
    "isFavorite": false
  }'
```

---

### Delete Saved Station

Remove a station from user's saved list.

**Endpoint**: `DELETE /api/stations/saved/:placeId`

**Authentication**: Required (JWT)

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| placeId | string | Yes | Google Place ID |

**Response**: `204 No Content` (empty body)

**Error Responses**:

**404 Not Found** - Station not saved or doesn't belong to user
```json
{
  "error": "Not Found",
  "message": "Saved station not found"
}
```

**500 Internal Server Error**
```json
{
  "error": "Internal server error",
  "message": "Failed to remove saved station"
}
```

**Rate Limits**: None

**Performance**: 50-100ms

**Example cURL**:
```bash
curl -X DELETE https://motovaultpro.com/api/stations/saved/ChIJN1t_tDeuEmsRUsoyG83frY4 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
```

---

## Common Patterns

### User Data Isolation

All endpoints automatically filter by user_id extracted from JWT token. Users can only access their own saved stations.

**Example**:
- User A saves a station (placeId: ABC123)
- User B tries to GET /api/stations/saved/ABC123
- Returns 404 (not found) because it doesn't belong to User B

### Soft Deletes

Deleted saved stations use soft delete pattern (deleted_at timestamp). This preserves referential integrity with fuel logs and other features.

### Station Cache Workflow

1. User searches for stations (POST /search)
2. Stations are cached in database for 24 hours
3. User saves a station (POST /save)
4. Service retrieves cached data and creates saved record
5. Future requests (GET /saved) enrich with cached data

**Important**: Users must search before saving. Saving without prior search returns 404.

### Error Code Summary

| Status Code | Meaning | Common Causes |
|-------------|---------|---------------|
| 200 | Success | Valid request processed |
| 201 | Created | Station saved successfully |
| 204 | No Content | Station deleted successfully |
| 400 | Bad Request | Missing required fields, invalid input |
| 401 | Unauthorized | Missing/invalid JWT token |
| 404 | Not Found | Station not found, not cached, or not owned by user |
| 409 | Conflict | Duplicate save attempt |
| 500 | Internal Server Error | Database error, unexpected exception |
| 502 | Bad Gateway | Google Maps API unavailable |

## Testing the API

### Get JWT Token

```bash
# Use Auth0 authentication flow or get test token
TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
```

### Complete Workflow Example

```bash
# 1. Search for stations
curl -X POST https://motovaultpro.com/api/stations/search \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "latitude": 37.7749,
    "longitude": -122.4194,
    "radius": 5000
  }' | jq

# 2. Save a station from search results
PLACE_ID="ChIJN1t_tDeuEmsRUsoyG83frY4"
curl -X POST https://motovaultpro.com/api/stations/save \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"placeId\": \"$PLACE_ID\",
    \"nickname\": \"My Favorite Station\",
    \"isFavorite\": true
  }" | jq

# 3. Get all saved stations
curl -X GET https://motovaultpro.com/api/stations/saved \
  -H "Authorization: Bearer $TOKEN" | jq

# 4. Update saved station
curl -X PATCH https://motovaultpro.com/api/stations/saved/$PLACE_ID \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "notes": "Cheapest gas on my route"
  }' | jq

# 5. Delete saved station
curl -X DELETE https://motovaultpro.com/api/stations/saved/$PLACE_ID \
  -H "Authorization: Bearer $TOKEN"
```

## Rate Limiting

### Google Maps API Limits

- **Free Tier**: 1,000 requests per day
- **Cost**: $5 per 1,000 additional requests (Places Nearby Search)
- **Place Details**: $17 per 1,000 requests

### Application Limits

No application-level rate limiting currently implemented. Consider implementing:
- Per-user rate limits (e.g., 100 searches per hour)
- IP-based throttling for abuse prevention
- Exponential backoff on failures

## Security Considerations

### JWT Validation

All endpoints validate JWT signature, expiration, and issuer. Tokens must be issued by configured Auth0 tenant.

### SQL Injection Prevention

All database queries use parameterized statements. Never concatenate user input into SQL strings.

### Secrets Management

Google Maps API key is loaded from `/run/secrets/google-maps-api-key` at container startup. Never log or expose the API key.

### CORS Configuration

API endpoints are configured to accept requests from:
- https://motovaultpro.com (production)
- https://motovaultpro.com (development)

## Versioning

**Current Version**: v1 (implicit in /api/stations path)

**Breaking Changes Policy**:
- Version bump required for:
  - Field removal
  - Field type changes
  - New required fields
  - Changed error codes
  - Authentication changes
- Minimum 30-day deprecation notice
- Both versions supported during transition

## Support

For API issues:
1. Check container logs: `docker compose logs mvp-backend`
2. Verify JWT token is valid
3. Check Google Maps API key is configured
4. Review circuit breaker status in logs

## References

- Architecture: `/backend/src/features/stations/docs/ARCHITECTURE.md`
- Testing Guide: `/backend/src/features/stations/docs/TESTING.md`
- Google Maps Setup: `/backend/src/features/stations/docs/GOOGLE-MAPS-SETUP.md`
- Feature README: `/backend/src/features/stations/README.md`
