# API Documentation

All API endpoints live under `/api/`. Every state-changing endpoint requires a valid CSRF token in the request body (`csrf_token` field). Tokens are embedded in every page that uses the API — the JavaScript reads them from hidden inputs or data attributes.

---

## Authentication

Most endpoints require the user to be logged in (a valid PHP session). Exceptions are noted per endpoint.

**Session requirement:** Standard PHP session cookie. No Bearer token, no OAuth.

**CSRF requirement:** All POST and DELETE requests must include `"csrf_token": "<value>"` in the JSON body. The token for the current session is available from hidden inputs on any page that uses the API, or from `data-csrf` attributes on widget containers.

---

## Likes — `POST /api/likes.php`

Toggle a like on any approved content item. Works for logged-in and anonymous users.

**Auth required:** No (guests are de-duplicated by salted IP+UA hash)

**Request:**
```json
{
  "csrf_token": "...",
  "content_type": "news",
  "content_id": 42
}
```

**`content_type` values:** `news` | `blog` | `audio` | `video` | `podcast`

**Response (200):**
```json
{
  "ok": true,
  "state": "liked",
  "count": 7
}
```
`state` is `"liked"` or `"unliked"` depending on the resulting state after the toggle.

**Errors:** 403 (invalid CSRF), 404 (content not found or not approved), 422 (invalid type or ID)

---

## Comments — `POST /api/comments.php` and `DELETE /api/comments.php`

### POST — add a comment or reply

**Auth required:** No (guests provide a display name)

**Request:**
```json
{
  "csrf_token": "...",
  "content_type": "news",
  "content_id": 42,
  "body": "Great article!",
  "guest_name": "Jane Smith",
  "parent_comment_id": null
}
```

`guest_name` is required only when not logged in. `parent_comment_id` is the ID of the top-level comment being replied to — must be null for a new top-level comment. Replies to replies are not allowed (two-level threading only).

**Response (200):**
```json
{
  "ok": true,
  "comment_id": 99,
  "commenter_name": "Jane Smith",
  "body": "Great article!",
  "is_reply": false,
  "parent_id": null,
  "created_at": "Jul 10, 2026",
  "is_owner": false
}
```

### DELETE — hide a comment

**Auth required:** Yes (must be the comment's author OR an editor/admin)

**Request:**
```json
{
  "csrf_token": "...",
  "comment_id": 99
}
```

**Response (200):** `{"ok": true}`

Comments are soft-hidden (`status = 'hidden'`), not hard-deleted. Replies to a hidden comment are also no longer shown.

**Errors:** 403 (not the owner, not editor/admin, or invalid CSRF), 404 (comment not found), 422 (missing comment ID or reply-to-reply attempt)

---

## Favorites — `POST /api/favorites.php`

Toggle save/unsave on any approved content item.

**Auth required:** Yes

**Request:**
```json
{
  "csrf_token": "...",
  "content_type": "podcast",
  "content_id": 15
}
```

**Response (200):**
```json
{
  "ok": true,
  "state": "saved"
}
```
`state` is `"saved"` or `"unsaved"`.

**Errors:** 401 (not logged in), 403 (invalid CSRF), 404 (content not found), 422 (invalid type or ID)

---

## Shares — `POST /api/shares.php`

Record a share event and get the platform-specific share URL.

**Auth required:** No

**Request:**
```json
{
  "csrf_token": "...",
  "content_type": "news",
  "content_id": 42,
  "platform": "whatsapp",
  "page_url": "https://your-domain.example/content/news-single.php?slug=campus-election",
  "page_title": "Campus Election Results — JMS Media Hub"
}
```

**`platform` values:** `whatsapp` | `facebook` | `x` | `telegram`

**Response (200):**
```json
{
  "ok": true,
  "share_url": "https://wa.me/?text=Campus%20Election%20Results%20https%3A%2F%2F..."
}
```

The JavaScript opens `share_url` in a new tab. Invalid platform names are silently ignored (no row written, `share_url` will be null).

---

## AI Tools — `POST /api/ai-tools.php`

Run one of the 7 journalism AI tools.

**Auth required:** Yes (student, editor, or admin)

**Request:**
```json
{
  "csrf_token": "...",
  "tool": "headline_generator",
  "input": {
    "topic": "Student council rejects cafeteria proposal"
  }
}
```

**`tool` values:** `headline_generator` | `story_outline_generator` | `interview_question_generator` | `news_summary_generator` | `grammar_checker` | `seo_title_generator` | `article_improvement_suggestions`

**`input` fields per tool:**

| Tool | Required fields | Optional fields |
|---|---|---|
| `headline_generator` | `topic` | — |
| `story_outline_generator` | `topic` | `story_type` |
| `interview_question_generator` | `subject` | `context` |
| `news_summary_generator` | `article_text` | — |
| `grammar_checker` | `text` | — |
| `seo_title_generator` | `headline` | `keywords` |
| `article_improvement_suggestions` | `article_text` | — |

**Response (200):**
```json
{
  "ok": true,
  "text": "1. Council Blocks Cafeteria Overhaul\n2. Students Left Hungry...",
  "used": 3,
  "limit": 10,
  "remaining": 7
}
```

**Errors:** 401 (not logged in), 403 (invalid CSRF or tool disabled), 422 (unknown tool or empty input), 429 (daily limit reached), 502 (AI provider error)

---

## Podcast Analytics — `POST /api/podcast-track.php`

Record a podcast play or download event. Used by the podcast player JS.

**Auth required:** No (analytics work for anonymous listeners)

**Request:**
```json
{
  "csrf_token": "...",
  "podcast_id": 12,
  "event": "play",
  "seconds": 342
}
```

`event` is `"play"` or `"download"`. `seconds` is optional and only meaningful for play events.

**Response (200):** `{"ok": true}`

---

## Radio Heartbeat — `POST /api/radio-heartbeat.php`

Increment or decrement the live listener counter.

**Auth required:** No

**Request:**
```json
{
  "csrf_token": "...",
  "action": "join"
}
```

`action` is `"join"` (play pressed) or `"leave"` (pause/tab hidden/page closed).

**Response (200):** `{"ok": true}`

---

## Push Subscribe — `POST` and `DELETE /api/push-subscribe.php`

Save or delete a browser push subscription.

**Auth required:** No (anonymous visitors can subscribe; their subscription is stored without a user_id)

**POST Request:**
```json
{
  "csrf_token": "...",
  "subscription": {
    "endpoint": "https://fcm.googleapis.com/...",
    "keys": {
      "p256dh": "...",
      "auth": "..."
    }
  }
}
```

**DELETE Request:**
```json
{
  "csrf_token": "...",
  "endpoint": "https://fcm.googleapis.com/..."
}
```

**Response (200):** `{"ok": true}`
