WordPress Integration Overview
The Reports application integrates with the Aleteia WordPress backend (a Bedrock install on Clever Cloud, 7 language editions) in both directions: WordPress calls the app through webhooks and JSON APIs, and the app reads from / writes to WordPress through the native WP-JSON REST API and XML-RPC.
Reads used to go through the WordPress.com public API (public-api.wordpress.com/rest/v1.1). That API now answers 403 on most editions — en is a private blog and es, pt, si, it, ar have REST access disabled — so every read was failing. Wordpress::RestAPI replaces it with the native /wp-json/wp/v2/ endpoints of each backend host, normalizing responses back to the v1.1 shape callers expect. Writes are unchanged and still go through XML-RPC.
The two APIs differ in more than the host: WP-JSON returns a bare array plus an X-WP-Total header instead of a {found, posts} envelope, spells the identifier and permalink id/link rather than ID/URL, wraps text fields in a {rendered:} object, reports dates in site local time with no offset, and serves each custom post type from its own route rather than through a type parameter. Callers use WP-JSON field names directly; the client only assembles what the API does not hand over ready to use — it unwraps the rendered envelope, rebuilds an unambiguous UTC date from date_gmt, and resolves featured_image from the bare id WP-JSON returns.
This page is the inventory of every integration feature. Use it as the entry point to the detailed documentation and as a checklist when the WordPress backend changes.
Sites and Configuration
The WordpressAPI class maintains the map of language editions (en, es, fr, it, pl, pt, ar, si) and their backend hosts, including staging equivalents used automatically outside production.
| Production | Staging and development | |
|---|---|---|
| Backend host | wp-prod.{locale}.aleteia.org | wp-preprod.{locale}.aleteia.org |
| XML-RPC | /wp/xmlrpc.php | /wp/xmlrpc.php |
| WP-JSON | /wp-json/ | /wp-json/ |
XML-RPC lives under /wp because Bedrock keeps WordPress core in that subdirectory; WP-JSON stays at the root.
The backend moved from WordPress VIP (wp.{locale}.aleteia.org, XML-RPC at /xmlrpc.php) to Clever Cloud on 2026-10-08. Users and post IDs were migrated as they were, so credentials and stored post IDs keep working. The Arabic edition was not migrated: BACKEND_SITES lists the editions the backend serves, and jobs that walk every edition (author sync, the all-locales taxonomy export) iterate it instead of I18n.available_locales. See #948 for what was kept, adapted or removed.
The ar and it editions are currently excluded from active synchronization (ACTIVE_SITES): the SendGrid site grouping skips them. The remaining six editions are fully active.
Credentials and shared secrets are provided via environment variables (WORDPRESS_USERNAME, WORDPRESS_PASSWORD, WP_PRE_SHARED_KEY) — see Environment Variables.
High-Level Architecture
Inbound: WordPress → Reports
| Feature | What it does | Trigger | Status |
|---|---|---|---|
| Post scrape webhook | On post publish/update, dispatches a Facebook Open Graph re-scrape | Webhook on every post create/update | Active — entry point of the whole content pipeline |
| Media upload webhook | Notified the app that an image was uploaded so it could be processed and copied | Webhook (JWT authenticated) on media upload | Removed — see Media Copy (retired) |
| Email campaigns API | WordPress manages SendGrid campaigns (create, update, delete, deliver, test) through JWT-authenticated calls to the app | Editor actions in WordPress | Active — see Email Campaigns |
| Slideshow copy API | WordPress editors requested copying a slideshow from one edition to another | Editor action → async API request | Removed after the backend cutover (#1108) |
WordPress fires the post webhook on every save, so a single article being edited produces
two or three calls within a few minutes — roughly three webhooks per distinct post. Facebook
re-scrapes are coalesced per url over a five-minute window (FacebookScrapeJob::COALESCE_WINDOW),
because Meta's app-level request limit is low enough that the duplicates alone can exhaust it
during a busy publishing hour.
When the limit is reached anyway, Meta answers HTTP 403 with error code 4. That answer opens the
FacebookScrape circuit breaker after three failures and the job backs off for 15 minutes before
probing again, up to FacebookScrapeJob::RATE_LIMIT_ATTEMPTS attempts. A url still refused at the
end of the backoff is dropped with a warning in the logs and no Rollbar report: the re-scrape is
best effort, Facebook re-scrapes shared urls on its own, and the alternative is one reported error
per url every time a publishing burst hits the limit.
Outbound: Reports → WordPress
| Feature | Direction | Trigger | Status |
|---|---|---|---|
| Algolia search indexing | Read (posts, guest authors, categories) | Scrape webhook per article + batch reindex, plus POST /post-deleted for removals | Removed after the backend cutover (#1106): site search no longer reads our indices |
| Media copy between editions | Read metadata + write attachments to other editions | Media webhook + background jobs | Removed after the backend cutover, its wordpress_pictures ledger included |
| Author synchronization | Read (WordPress users → local database) | Hourly cron (SyncWordpressAuthorsJob) | Active |
| Taxonomy management | Read + write (remap categories/tags, delete terms) | On-demand: admin UI actions and maintenance tasks | Active, manual use only |
| Article deindexing | Write (Yoast noindex/nofollow meta) | On-demand: CSV-driven job (DeindexPostsJob) | Removed after the backend cutover: the Yoast meta has no effect on All in One SEO, which keeps robots settings in its own table |
| Editorial reports & exports | Read (posts, content, categories, co-authors, media library) | Monthly crons (Slovenian article export, media library export), on-demand author payment reports, maintenance export tasks | Active |
Configuration-Only Usage
Some parts of the app do not call WordPress at all but rely on the locale→domain maps defined in WordpressAPI:
- Donation and subscription redirects build return URLs from the edition domains.
- Donations analytics resolves per-locale Google Analytics secondary tracker IDs.
If the WordPress client is ever replaced, these mappings must be preserved.
Detailed Documentation
- Media Copy (retired) — what is left of the image copy pipeline
- Maintenance Tasks — taxonomy management and data exports
- Email Campaigns — WordPress-driven SendGrid campaigns
- Environment Variables — credentials and shared secrets