Skip to main content

Api

13 min read

Api

Shared across every PremiumPress product. 29 classes in inc/Api.

Auth Auth.php​

Sign-in for the app: the site's own WordPress accounts, reached by email + password, Sign in with Apple, or Google Sign-In. All three end in the same place — a WP user and an Api\App\Tokens session for this device. Social sign-in verifies the provider's identity token server-side (Api\App\Jwt) and links it to an existing account ONLY when the provider vouches for the email (email_verified) — the same rule the website's Account\Social enforces, for the same reason: an unverified email match is an account-takeover path. The provider subject id is remembered in user meta so later sign-ins match even when Apple hides or rotates the relay email.

API
login()register()forgot()apple()google()refresh()logout()
Api/App/Auth.php

Blog Blog.php​

API operations for blog posts — the standard WordPress post type (the theme adds no custom post meta for blog; see inc/Frontend/Blog.php). Plain wp_insert_post / wp_update_post with the native category/post_tag taxonomies + featured image.

API
list()create()update()
Api/Operations/Blog.php

Catalog Catalog.php​

Public catalogue routes: categories, home feeds, search, a listing, its reviews. Everything here works for guests; a signed-in member just gets is_favourite set. Search rebuilds the website's own query rules (Directory\SearchPage) from REST parameters instead of $_GET — same meta keys, same taxonomy semantics, same sold/expired exclusions, same haversine radius SQL — so the app and the site agree on what "results for X near Y" means.

API
categories()listings()search()detail()reviews()
Api/App/Catalog.php

Cimd Cimd.php​

OAuth Client ID Metadata Documents (CIMD) — the MCP authorization spec's alternative to dynamic client registration. The client's client_id IS an https URL; this server fetches the JSON document at that URL and treats it as the client's registration. Why it matters here: the Connectors Directory lists this server under a per-customer URL pattern, and Anthropic prefers CIMD over DCR for that case. DCR mints a new stored client on every fresh connection (see Store::MAX_CLIENTS pruning); CIMD stores nothing and identifies the client by a URL on its own domain (e.g. claude.ai). The fetch is an SSRF surface, so it is deliberately narrow: https only, a real path, no credentials/fragment/dot-segments in the URL, wp_safe_remote_get (refuses private and loopback hosts), no redirects, a short timeout, a small size cap, and a cache so one client can't make the site fetch on every request.

API
isMetadataUrl()validUrl()resolve()parse()
Api/OAuth/Cimd.php

Config Config.php​

GET /app/config — the one call the app makes on launch. Everything a white-label build needs to become THIS site's app at runtime: name, colours, logo, which features and sign-in methods are on, currency and units, legal URLs for the store forms, and the search vocabulary (sorts, filters, taxonomies) so the app never hard-codes what the site can do.

API
get()
Api/App/Config.php

Design Design.php​

API operations for site-level design: switch the active ready-made design, and set the brand colours / logo. Reuses the same options the admin Design screen writes — Frontend\Preview::OPTION_ACTIVE (active design) and Design\Branding's ppt_design option — so an API change is identical to an admin change. Per-PAGE block editing (the Studio working-plan / builder exports) is intentionally out of scope here; this is site-wide design only.

API
listDesigns()setActiveDesign()setBrandColors()setLogo()
Api/Operations/Design.php

Jwt Jwt.php​

Minimal RS256 JWT verification against a provider's published JWKS — enough to validate the identity tokens Sign in with Apple and Google Sign-In hand the app, without a Composer dependency (the bundled HybridAuth vendor tree does not ship firebase/php-jwt). Verifies: header alg RS256 + a kid present in the JWKS; the RSA signature via OpenSSL; exp (required) / nbf (60 s leeway); iss in the allowed list; aud (string or array) intersects the allowed list. Returns the payload claims, or a WP_Error. Key sets are cached for 12 hours and refetched once on an unknown kid, so a provider key rotation does not lock members out until the cache expires.

API
verify()checkClaims()
Api/App/Jwt.php

Keys Keys.php​

API key store for the PPT management API. Keys are bearer tokens an admin generates (see Api\Loader's settings page) and pastes into their AI agent / MCP client. A valid key authorises admin-equivalent calls (see Api\Server::authenticate). Only a HASH of each token is stored (wp_hash, keyed by the site's secret salt); the plaintext is shown ONCE at creation and never recoverable. A short display prefix is kept so a key is recognisable in the admin table. Everything lives in the theme's own option ppt_api_keys (not the shared ppt_settings sanitiser) — the self-contained model used by Admin\Payments.

API
allScopeKeys()normaliseScopes()enabled()setEnabled()oauthEnabled()setOauthEnabled()all()count()create()revoke()verify()touch()
inc/Api/Keys.php

Listings Listings.php​

API operations for directory listings. Thin adapters that translate a clean API payload into the shape Ppt\Directory\ListingEditor::save() expects, so the API reuses the exact same validation/persistence path as the admin + member editors (no duplicated field logic). Runs as an admin (mode 'admin').

API
list()get()create()update()
Api/Operations/Listings.php

Loader Loader.php​

Wires the companion-app API into WordPress: the REST routes (Api\App\Server), the sessions table, the push triggers, the deep-link verification files (/.well-known/apple-app-site-association, /.well-known/assetlinks.json), the Settings ▸ Mobile app admin screen, and the daily session purge. Registered from inc/Theme.php $services. Everything stays dormant until the admin switches the app on (Api\App\Settings::enabled()).

API
register()boot()wellKnown()visible()menu()url()render()handleAdmin()clientJson()
Api/App/Loader.php

Loader Loader.php​

Wires the PPT management API into WordPress: registers the REST/MCP routes (via Api\Server) on rest_api_init and handles the admin-post actions that enable the API and create/revoke keys. The management UI lives on the Settings screen, under the "MCP server" tab, fetched on demand by Admin\SettingsPanels (inc/Admin/views/partials/settings-mcp-server.php requires inc/Api/views/api.php). Add Api\Loader::class to inc/Theme.php $services to activate.

API
register()handleAdmin()
inc/Api/Loader.php

Marketplace Marketplace.php​

Management API: marketplace orders and payouts, READ-ONLY (owner, 2026-09-28: admin API keys only; no seller tokens). Registered in Api\Server::registry() only on the Marketplace, under the orders scope (a read key sees them too). list_orders buyer orders (the parent MV-<id>), newest first, with each shop's part get_order one order in full: totals, coupon, currency, delivery address, and per shop the lines, commission, seller net, hold state and fulfilment stage list_payouts seller earnings per sale (held / released / refunded / cancelled, with release dates), each seller's balance, and withdrawal requests - never the payout details a seller typed (bank / PayPal), which stay in wp-admin

API
listOrders()getOrder()listPayouts()
Api/Operations/Marketplace.php

Me Me.php​

The signed-in member's own things: profile, favourites, saved searches, their listings, the device's push registration, the notification feed — and account deletion, which the stores require to be possible from inside the app.

API
get()update()delete()listings()favourites()favouriteAdd()favouriteRemove()searches()searchAdd()searchRemove()device()notifications()
Api/App/Me.php

Media Media.php​

API operation: add media to the library. Accepts an image by URL (downloaded) or by base64 data, sideloads it into the WordPress media library, and returns the new attachment id + URL — the id you then pass as a listing image or a blog featured image.

API
upload()
Api/Operations/Media.php

Members Members.php​

API operations for directory members and their submissions. Members are WordPress Subscribers; their "posts" are listing_type posts they authored (there is no separate submission type — see inc/Frontend/MemberCaps.php). Includes moderation actions on those listings (approve / reject / feature / extend).

API
listMembers()get()listPosts()manageListing()
Api/Operations/Members.php

Messaging Messaging.php​

Member-to-member messages for the app — a thin JSON face on Account\Messages, which already owns the table, the pair-keyed threads, blocking and the "you can only read your own conversations" scoping. Nothing here touches the table directly.

API
threads()thread()send()block()unblock()
Api/App/Messaging.php

Pages Pages.php​

API operations for building PAGE LAYOUTS from the PPT block library — the deep "redesign a page" surface (beyond swapping a whole design). A remote agent can read the block palette (list/get_block) and compose a page as an ordered list of blocks with per-block content. Pages are persisted as Gutenberg markup: one ppt/block dynamic block per block (the same block the block-editor integration registers). So a page built here renders on the front end AND stays editable in the wp-admin block editor. Reuses Blocks\Registry as the source of truth and Design\Tokens for brand fallbacks.

API
listBlocks()getBlock()listPages()createPage()updatePageBlocks()
Api/Operations/Pages.php

Push Push.php​

Push notifications to the app, sent through Expo's push service (one HTTP API that fans out to APNs and FCM). Device tokens live on the app sessions (Api\App\Tokens), so "notify this member" is "post to every push-enabled device they are signed in on". Triggers (wired by Api\App\Loader): • every on-site notification the theme raises (ppt_notification, fired from Notifications\Center::push — new message, payout, whatever a fork adds) • a member's listing going live (pending → publish) • a newly published listing matching another member's saved search Members switch each stream off per device from the app (Tokens prefs: messages / listings / searches). Tokens Expo reports as dead are forgotten.

API
toUser()onNotification()onTransition()savedSearchFanout()
Api/App/Push.php

Serialize Serialize.php​

JSON shapes for the companion app. One place decides what a listing, review, member or category looks like over the wire, so every route (search, favourites, my listings, detail) returns the same card and the app has one type per thing. Reads through the theme's own helpers (Media, Reviews, ListingHours, ListingEditor field definitions, Currencies) rather than raw meta, so app data agrees with the website page for the same listing.

API
contactKeys()term()categories()card()detail()distanceKm()gallery()videos()location()hours()fields()contact()social()pricing()review()user()canSubmit()
Api/App/Serialize.php

Server Server.php​

The companion-app REST API: route table, bearer-session authentication, and the shared response helpers every controller uses. Routes live under wp-json/ppt/v1/app/* beside the management API (Api\Server), but are a different animal: PUBLIC reads for guests, member-scoped writes behind an opaque per-device token (Api\App\Tokens) — never cookies, never admin keys. Each route declares one of three auth modes: none — guests welcome; a valid token is still honoured (is_favourite etc.) optional — same as none (explicit in the table for readability) required — 401 without a live session The whole API is switched on from Settings ▸ Mobile app; while off every route answers 403 so a site that never asked for an app exposes nothing new.

API
registerRoutes()route()guard()grantMemberCaps()viewer()session()setSession()fail()notFound()paged()paging()listing()visibleListing()near()device()
Api/App/Server.php

Server Server.php​

OAuth 2.1 authorization server for the management API, so claude.ai web (and other MCP clients that require OAuth) can connect. Implements the subset the MCP authorization spec + Anthropic's connectors require: RFC 9728 protected-resource metadata, RFC 8414 authorization-server metadata, RFC 7591 dynamic client registration, Client ID Metadata Documents (see Cimd), the PKCE-S256 authorization-code grant, and refresh-token rotation. Discovery docs + the authorization (consent) page are served on the FRONT END (parse_request) so the WordPress login cookie identifies the admin — a REST route would need an X-WP-Nonce the browser can't supply. The machine endpoints (register, token) are REST routes. Everything here is gated by the OAuth toggle (Keys::oauthEnabled()).

API
register()interceptFrontEnd()registerRoutes()handleRegister()handleToken()verifyBearer()addAuthenticateHeader()protectedResourceMetadata()authServerMetadata()
Api/OAuth/Server.php

Server Server.php​

The PPT management API server: bearer authentication, the single TOOL REGISTRY (one source of truth), and two transports over it — • MCP — POST /wp-json/ppt/v1/mcp (JSON-RPC 2.0: initialize / tools/list / tools/call / ping) so an AI agent (Claude) connects it as a connector. • REST — GET /wp-json/ppt/v1/tools and POST /wp-json/ppt/v1/tools/<name>, so any non-MCP agent can list + invoke the same tools. Every tool maps to an Operations* method. A valid key runs as the admin bound to it (admin-equivalent), so the reused editor/save paths behave exactly as in wp-admin.

API
registerRoutes()authenticate()handleToolsList()handleToolCall()fixAllowHeader()handleMcpMethodNotAllowed()handleMcp()dispatch()negotiateProtocol()registry()toolSchemas()toolAnnotationMap()toolScopeMap()
inc/Api/Server.php

Settings Settings.php​

Mobile app settings — one option (ppt_app) holding everything the companion app and its build pipeline read from the site: whether the app API is on, the app's display name and accent, the store identifiers (iOS bundle id, Android package), the identity-provider client ids used to verify Apple / Google sign-in tokens, the legal URLs the stores require, and the feature switches the app honours. Edited on Settings ▸ Mobile app (inc/Admin/views/partials/settings-mobile.php, saved by Api\App\Loader::handleAdmin). Read by Api\App\Config for GET /app/config and by the theme's own push sender.

API
defaults()all()get()enabled()feature()adminLoginAllowed()pushEnabled()name()accent()googleClientIds()androidFingerprints()update()
Api/App/Settings.php

Site Site.php​

API operation: site overview — the discovery call an agent makes first to learn what it's working with (name, url, content counts, the active design, and the listing categories it can assign).

API
info()listingCategories()
Api/Operations/Site.php

Store Store.php​

OAuth persistence + crypto for the management API's authorization server. Holds the dynamically-registered clients, and issues/verifies the three short-lived secrets of the authorization-code flow: authorization codes, access tokens, refresh tokens. Only HASHES of codes/tokens are stored (wp_hash, keyed by the site salt); the plaintext is returned once to the caller. Codes and tokens live in transients so they auto-expire; clients live in an option (pruned). Everything here is data + crypto — no HTTP.

API
registerClient()client()issueCode()consumeCode()issueAccessToken()verifyAccessToken()issueRefreshToken()consumeRefreshToken()verifyPkce()b64url()
Api/OAuth/Store.php

Submit Submit.php​

Posting from the app: the submission schema, create / update / delete a listing, photo upload, report a listing, write a review. Saving goes through ListingEditor::save — the same code path as the website's editor — so every site rule (moderation, category lock, max images, plan expiry, custom-field scoping) applies unchanged. The member-side backstops the website enforces with redirects (terms, description length, image required, category required, payment due) are reproduced here as structured 422 / 402 answers that keep the listing as a draft, so the app can fix and resubmit. "The same code path as the website's editor" is only true if we call the SAME class the website does, so the four listing classes are resolved through Theme::forkClass() rather than imported — see the resolvers below.

API
schema()create()update()delete()upload()report()review()
Api/App/Submit.php

Throttle Throttle.php​

Fixed-window rate limiter for the app API, backed by transients so it works on any host without extra infrastructure. Buckets are keyed by route group + caller (the signed-in user id, else the client IP), so one abusive caller cannot exhaust a shared limit. Not a hard security boundary — a determined attacker with many IPs gets many windows — but it stops the cheap abuse (credential stuffing, register floods, report spam) that a public JSON API otherwise invites.

API
allow()error()ip()
Api/App/Throttle.php

Tokens Tokens.php​

App sessions — the bearer tokens the companion app holds, one row per signed-in device. The row doubles as the device record: it carries the Expo push token and the member's per-device notification switch, so "who do we push to" and "who is signed in where" are the same question. Only a SHA-256 of the token is stored; the plaintext is returned once at sign-in. Tokens live 90 days and slide: any use after a day extends them, so an app that is opened now and then stays signed in, and one abandoned for three months does not.

API
table()install()issue()verify()rotate()revoke()revokeAll()setPush()pushTargets()pushUserIds()forgetPushToken()purge()
Api/App/Tokens.php

Users Users.php​

API operations for WordPress user accounts: list, create, update, delete. Admin protection — the administrator role is off-limits to the API: • an administrator account cannot be deleted; • no user can be GRANTED the administrator role (create or update); • an existing administrator's role cannot be changed (no demotion). Non-admin accounts (subscribers, editors, …) can be managed freely. The API also refuses to delete the account its own key belongs to.

API
list()get()create()update()delete()
Api/Operations/Users.php