# Referral Page (Empfehlungs-URLs) – Technical Guide

**Last Updated:** 2026-01-30

Technical summary for the partner referral URLs page: promotable paths (sitemap-derived), "Seite wählen" table, UTM handling, asset location.

## Overview

- **Page:** `v2/pages/partner-referral-urls.php`
- **Route:** `/partner/referral-urls`
- **APIs:** `GET /v2/api/affiliate-sitemap-pages.php` (pages for table), `POST /v2/api/affiliate-generate-url.php` (generate URL with UTM)
- **Config:** `v2/config/affiliate-config.php` (base URL, sitemap-derived promotable paths, URL helper)

## Promotable Paths

All paths that appear in the main site URL list (`v2/data/sitemap-pages.json`) and in the blog sitemap (`sitemap-blog.xml`) are promotable for referral URLs. The list is built at runtime via `getAffiliatePromotablePathsFromSitemap()` (which merges main site URLs from sitemap-pages.json and blog from sitemap-blog.xml); `isAffiliatePromotablePath($path)` returns true for any path in that list. The short list in `$affiliate_promotable_paths` is used only for **Schnelllinks** (quick-link buttons).

**Source of truth:** Main site: `v2/data/sitemap-pages.json` (same source as https://www.ordio.com/sitemap.xml). Blog: https://www.ordio.com/sitemap-blog.xml. sitemap.txt is no longer used.

**Excluded paths:** Legal pages (AGB, Impressum, Datenschutz, Nutzungsbedingungen) and **product-updates month archives** (e.g. `/produkt-updates/november-2025`, `/produkt-updates/oktober-2025`) are excluded from the promotable list and from the "Seite wählen" table. The main `/produkt-updates` page and feature slugs (e.g. `/produkt-updates/meet-ordio-novi-ai-agent`) are kept. Defined in `$affiliate_excluded_paths` and `isAffiliateProductUpdatesMonthPath()` in `affiliate-config.php`; use `isAffiliateExcludedPath($path)` to check.

**Adding a new promotable path:** Add the full URL to `v2/data/sitemap-pages.json` (main site only). No change is required in `affiliate-config.php` for the path to be allowed; optional: add a label in `$affiliate_sitemap_path_labels` for a friendlier name in the table. For blog, ensure the URL appears in sitemap-blog.xml.

## Seite wählen (Table)

The "Seite wählen" section shows all sitemap pages in a table:

- **Data:** Loaded from `GET /v2/api/affiliate-sitemap-pages.php` (auth required). Response includes `pages` (path, label, category) from main sitemap **and** all blog post URLs from `sitemap-blog.xml` (category `blog`), and `categories` (id, label) in display order. **Sonstige** is not shown in the category filter; pages categorized as "other" still appear when "Alle Kategorien" is selected.
- **Search:** Filters by page label, path, or **category label** (client-side, debounced). So e.g. searching "produkt" shows both "Produkt" category pages and "Produkt-Updates" category pages.
- **Filter:** Category dropdown (Alle Kategorien, then one per category: Startseite, Produkt, Rechner & Tools, Branchen, Alternativen, Webinar, Vorlagen, Download, Unternehmen & Recht, Produkt-Updates, Blog/Insights). Sonstige is removed from the dropdown.
- **Categories:** The Kategorie column shows each category as a distinct pill/badge tag (`.referral-category-tag` with per-category modifier classes for color).
- **Pagination:** "Pro Seite" options are **dynamic** based on filtered total: 10, 20, 50, 100, and "Alle (N)" when filtered total > 100. "X–Y von Z" info, Vorherige / Nächste, numbered page links, and "Seite" input + "Gehe" to jump. **Page numbers** use a window pattern (boundaryCount=1, siblingCount=1): first and last page always shown, current page ±1, ellipsis when there are gaps (e.g. 1 … 5 6 7 … 11). Numbers are rendered as a semantic list (`<nav>` with `<ul>`/`<li>`); each item is a distinct pill (min 24×24px touch target, border, spacing). Ellipsis has `aria-hidden="true"`. All controls use `:focus-visible` for keyboard users. Pagination bar is grouped into: (1) range/total (`.referral-pagination-info`), (2) prev + numbers list + next (`.referral-pagination-nav`), (3) jump-to-page (`.referral-pagination-jump`). Pagination applies to the filtered list; search/filter/page-size changes reset to page 1; current page is clamped when the filtered result set shrinks.
- **Copy:** Per-row "Kopieren" builds the base referral URL (`base + path + ?affiliate=PARTNER_ID`) and copies to clipboard; shows "Kopiert!" briefly.
- **Mit UTM anpassen:** Sets the path in the "URL mit UTM-Parametern generieren" form, scrolls to that form, and focuses the first UTM field so the partner can add UTM and generate.

## UTM Parameters

- **Supported:** `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term` (all optional).
- **Rules:** Lowercase recommended; use dashes instead of spaces. Medium values like `affiliate` or `referral` are typical for partner links.
- **API:** Request body may include any of the five UTM fields; empty strings are omitted before calling the URL helper.
- **Lead capture:** `v2/api/lead-capture.php` reads UTM (and `affiliate`) from cookies/input and stores them in HubSpot.

## Partner UTM & attribution

Partner attribution (commission/identity) is stored in HubSpot as `affiliate_partner_id` from the `affiliate` query parameter and cookie; it is **independent** of UTM values. Partner links use the **same** HubSpot UTM/traffic source properties as the rest of the site; separation is by **values** (e.g. `utm_source=affiliate` or `partner`), not separate properties. Recommended partner values: source = affiliate/partner, medium = referral (or email/social). See [PARTNER_UTM_AND_ATTRIBUTION.md](PARTNER_UTM_AND_ATTRIBUTION.md) for full details.

## URL Generation

- **Helper:** `generateAffiliateReferralUrl($partnerId, $utmParams = [], $path = '')` in `affiliate-config.php`.
- **Logic:** Base URL + normalized path (from allowlist) + `?affiliate=PARTNER_ID` + UTM query string.
- **Validation:** Invalid path in the API returns `400` with a German error message.

## Asset Location

- **Directory:** `v2/img/affiliate/`
- **Contents:** Logo and other partner-facing assets (e.g. `ordio-logo.svg`). See `v2/img/affiliate/README.md`.
- **Page:** The "Material & Vorlagen" block in `partner-referral-urls.php` links to assets and shows approved short copy. Update that block when adding new assets.

## Cursor Rules

- **affiliate-dashboard.mdc:** Referral subsection: promotable paths = sitemap-derived; pass all UTM params (including term/content) to the API.
- **affiliate-referral.mdc:** "Seite wählen" table data from `affiliate-sitemap-pages.php`; categories from sitemap-parser; search/filter/pagination client-side; Copy builds base referral URL; Customize sets path and scrolls to UTM form.

## Related Docs

- [API_REFERENCE.md](API_REFERENCE.md) – Generate Referral URL endpoint
- [PARTNER_GUIDE.md](PARTNER_GUIDE.md) – User-facing referral section
- [PARTNER_UTM_AND_ATTRIBUTION.md](PARTNER_UTM_AND_ATTRIBUTION.md) – Partner UTM parameters and HubSpot attribution
- [DASHBOARD_GUIDE.md](DASHBOARD_GUIDE.md) – Empfehlungs-URLs feature summary
