Skip to main content

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 moved off the WordPress.com public API

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.

ProductionStaging and development
Backend hostwp-prod.{locale}.aleteia.orgwp-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.

Backend cutover (2026-10-08)

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.

Disabled editions

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​

FeatureWhat it doesTriggerStatus
Post scrape webhookOn post publish/update, dispatches a Facebook Open Graph re-scrapeWebhook on every post create/updateActive — entry point of the whole content pipeline
Media upload webhookNotified the app that an image was uploaded so it could be processed and copiedWebhook (JWT authenticated) on media uploadRemoved — see Media Copy (retired)
Email campaigns APIWordPress manages SendGrid campaigns (create, update, delete, deliver, test) through JWT-authenticated calls to the appEditor actions in WordPressActive — see Email Campaigns
Slideshow copy APIWordPress editors requested copying a slideshow from one edition to anotherEditor action → async API requestRemoved after the backend cutover (#1108)
Duplicate scrape webhooks

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​

FeatureDirectionTriggerStatus
Algolia search indexingRead (posts, guest authors, categories)Scrape webhook per article + batch reindex, plus POST /post-deleted for removalsRemoved after the backend cutover (#1106): site search no longer reads our indices
Media copy between editionsRead metadata + write attachments to other editionsMedia webhook + background jobsRemoved after the backend cutover, its wordpress_pictures ledger included
Author synchronizationRead (WordPress users → local database)Hourly cron (SyncWordpressAuthorsJob)Active
Taxonomy managementRead + write (remap categories/tags, delete terms)On-demand: admin UI actions and maintenance tasksActive, manual use only
Article deindexingWrite (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 & exportsRead (posts, content, categories, co-authors, media library)Monthly crons (Slovenian article export, media library export), on-demand author payment reports, maintenance export tasksActive

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​