# Product Updates Feed Specification

**Last Updated:** 2026-03-25

## Overview

The Product Updates system exposes two syndication feeds for consumption by RSS readers, Ordio, and other clients:

- **RSS Feed** (`/produkt-updates/feed.xml`) – RSS 2.0 format
- **JSON Feed** (`/produkt-updates/feed.json`) – JSON Feed 1.1 format

Both feeds include big features and small improvements, sorted chronologically, with an optional `show_in_app_banner` flag for in-app update banners in Ordio.

## Feed Content Processing

The `summary` (JSON Feed) and RSS item descriptions are derived from HTML `description` content. To preserve spaces between block elements (e.g. `<p>First.</p><p>Second.</p>`), feeds use `htmlToPlainText()` from `v2/helpers/html-to-plain-text.php` instead of raw `strip_tags()`. This prevents concatenation bugs such as "Nutzung.Genau" when stripping multi-paragraph HTML.

**Implementation:** Both `produkt-updates-feed.json.php` and `produkt-updates-feed.xml.php` require the helper and use it for all HTML-to-plain-text conversion. Never use raw `strip_tags()` on multi-paragraph HTML for feed output.

## Image URLs (Ordio app and other consumers)

- **Canonical paths in JSON** are **root-relative** `/produkt-updates/bilder/{filename}` (e.g. `/produkt-updates/bilder/foo.webp`). Bytes stay on disk under `wp-content/uploads/produkt-updates/`; Apache **301-redirects** legacy `/wp-content/uploads/produkt-updates/{file}` and retired `/produkt-updates/media/{file}` to **`/produkt-updates/bilder/`** (see `.htaccess` Product Updates block). Run `php v2/admin/produkt-updates/normalize-produkt-updates-image-paths.php --dry-run` then `--write` to rewrite stored refs after deploy.
- **Parsing** accepts legacy wp-content paths, absolute `https://www.ordio.com/...`, and proxy URLs (`serve-produkt-updates-image.php?file=`) via `canonicalizeProduktUpdatesImageRef()` in `v2/includes/produkt-updates-paths.php`.
- **Feed output** (`feed.json` / `feed.xml`) builds **absolute `https://www.ordio.com/produkt-updates/bilder/...`** URLs, preferring **`.webp`** on disk when available (`preferProduktUpdatesWebpCanonicalIfExists`).
- **Small improvements** include an `image` property in JSON Feed when `featured_image` is set, using the same resolution rules as big features.

## Feed URLs

| Format | URL | Content-Type |
|--------|-----|--------------|
| RSS | `https://www.ordio.com/produkt-updates/feed.xml` | `application/rss+xml` |
| JSON | `https://www.ordio.com/produkt-updates/feed.json` | `application/feed+json` |

## RSS Feed

### Format

- RSS 2.0 with Atom namespace
- Custom namespace: `xmlns:ordio="https://www.ordio.com/ns/produkt-updates"`

### Items

1. **Month aggregates** – One item per month (e.g. "Ordio Updates November 2025"), linking to the month page
2. **Big features** – Individual items for all features (with `read_more_link` → post URL; without → month page `#feature-{id}`)
3. **Small improvements** – Individual items for each improvement, linking to the month page with `#improvement-{id}`

### Ordio Extensions (per item)

- `<ordio:type>big_feature</ordio:type>` or `<ordio:type>small_improvement</ordio:type>`
- `<ordio:show_in_app_banner>true</ordio:show_in_app_banner>` or `false`
- `<ordio:platforms>desktop,mobile</ordio:platforms>` – comma-separated platform list

### Query Parameters

Same as JSON Feed: `in_app_banner_only`, `type`, `platform`, `month`, `year`, `from`, `to`.

### Caching

- `Cache-Control: public, max-age=300` (5 minutes)
- `ETag` and `Last-Modified` headers for conditional GET (304 Not Modified)

### Scope

- Last 12 months of updates

---

## JSON Feed

### Format

- JSON Feed 1.1 specification: https://jsonfeed.org/version/1.1
- MIME type: `application/feed+json`

### Query Parameters

| Parameter | Values | Description |
|-----------|--------|-------------|
| `in_app_banner_only` | `1` | Return only items with `show_in_app_banner: true` |
| `type` | `big_feature` \| `small_improvement` | Filter by item type |
| `platform` | `desktop` \| `mobile` | Return only items available on that platform |
| `month` | `YYYY-MM` | Return only items from that month (e.g. `2025-02`) |
| `year` | `YYYY` | Return only items from that year |
| `from` | `YYYY-MM-DD` | Return only items on or after this date |
| `to` | `YYYY-MM-DD` | Return only items on or before this date |

Examples:

- `https://www.ordio.com/produkt-updates/feed.json?in_app_banner_only=1`
- `https://www.ordio.com/produkt-updates/feed.json?month=2025-02`
- `https://www.ordio.com/produkt-updates/feed.json?type=big_feature&platform=mobile`

### Response Structure

```json
{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Ordio Produkt-Updates",
  "home_page_url": "https://www.ordio.com/produkt-updates",
  "feed_url": "https://www.ordio.com/produkt-updates/feed.json",
  "description": "Entdecke die neuesten Features und Verbesserungen von Ordio.",
  "language": "de-DE",
  "items": [
    {
      "id": "ordio-novi",
      "url": "https://www.ordio.com/produkt-updates/meet-ordio-novi-ai-agent",
      "title": "Meet Ordio Novi",
      "content_html": "<p>...</p>",
      "summary": "...",
      "date_published": "2025-11-10T00:00:00+00:00",
      "tags": ["neues-feature"],
      "image": "https://www.ordio.com/...",
      "_ordio": {
        "type": "big_feature",
        "show_in_app_banner": true,
        "platforms": ["desktop", "mobile"]
      }
    }
  ]
}
```

### `_ordio` Extension

Per JSON Feed spec, custom extensions use `_`-prefixed keys. The `_ordio` object contains:

| Field | Type | Description |
|-------|------|-------------|
| `type` | string | `"big_feature"` or `"small_improvement"` |
| `show_in_app_banner` | boolean | When true, item is intended for in-app update banner display |
| `platforms` | array | `["desktop", "mobile"]` – target platforms |

### CORS

- `Access-Control-Allow-Origin: *` – Allows cross-origin fetch from Ordio
- `Access-Control-Allow-Methods: GET`

### Caching

- `Cache-Control: public, max-age=300` (5 minutes)
- `ETag` and `Last-Modified` headers for conditional GET (304 Not Modified)

### Scope

- Last 12 months, max 50 items

---

## In-App Banner Usage (Ordio)

Ordio can consume the JSON feed to display "New feature" banners:

1. **Fetch**: `https://www.ordio.com/produkt-updates/feed.json?in_app_banner_only=1`
2. **Filter**: Items with `item._ordio.show_in_app_banner === true`
3. **Display**: Use `title`, `content_html`, `url`, `image` for banner content
4. **Track**: Store shown IDs (e.g. localStorage) to avoid repeated display
5. **Platform**: Respect `platforms` – show desktop banner only when on web, etc.

---

## Admin Configuration

The `show_in_app_banner` flag is set in the Product Updates Admin:

- **Feature modal**: Checkbox "In-App-Banner anzeigen (Web-App)" (after Platforms)
- **Improvement modal**: Same checkbox

When checked, the item is included in the feed with `show_in_app_banner: true` and can be displayed as an update banner in Ordio.

---

## Discovery

The main produkt-updates page includes feed discovery links in `<head>`:

```html
<link rel="alternate" type="application/rss+xml" title="Ordio Produkt-Updates RSS" href="/produkt-updates/feed.xml">
<link rel="alternate" type="application/feed+json" title="Ordio Produkt-Updates JSON Feed" href="https://www.ordio.com/produkt-updates/feed.json">
```

---

## Content Typos (Fix via Admin)

The following content typos were identified in feed items. Fix via Product Updates Admin when editing:

| Item | Field | Current | Correct |
|------|-------|---------|---------|
| Probezeitraum schneller festlegen | description | "eim Anlegen" | "Beim Anlegen" |
| Besserer Kontrast für Badges im Plan | description | "Schichplan", "Konstrast" | "Schichtplan", "Kontrast" |

## Related Documentation

- [API Reference](./PRODUCT_UPDATES_API_REFERENCE.md) – JSON Feed endpoint details
- [Product Updates README](./PRODUCT_UPDATES_README.md) – System index
