Automated Update API: Building a Secure WordPress Plugin Delivery System
Learn how to build a custom Update API to deliver secure, versioned plugin updates. Master the WordPress update process to automate deployments for your customers.
Previously in this course, we built a Licensing Infrastructure: Secure Remote Verification for WordPress Plugins to gate access to our premium features. Now that we can verify customer entitlements, we need to deliver the code itself. This lesson adds the final piece of the distribution puzzle: building a custom Update API to notify WordPress sites of new versions and facilitate seamless, secure plugin updates.
The WordPress Update Mechanism
WordPress checks for updates by firing the site_transient_update_plugins filter. Every time the dashboard updates its update transients (usually every 12 hours), it triggers this hook. Our goal is to intercept this request, check our remote server for a newer version, and inject our plugin's update metadata if a newer version exists.
The Communication Flow
To build a robust system, your update server should behave like the official WordPress.org API. When a request hits your endpoint, it provides the current plugin version and the site's license key.
Sequence diagram: participant Site as WordPress Site; participant API as Update Server; Site → API: GET /check-update version, license; API → API: Verify License & Version; API → Site: JSON new_version, package_url, changelog; Site → Site: Displays "Update Available"; Site → API: Download Request authorized; API → Site: Plugin ZIP file
Implementing the Update API Client
On the client side (inside your plugin), you need a Service Provider that listens for the update transient check. You shouldn't hardcode this; it should be handled via a dedicated UpdateManager class.
PHPnamespace KnowledgeBase\Services; class UpdateManager { private $api_url = 'https:#6A9955">//updates.yourdomain.com/v1/'; private $plugin_slug = 'knowledge-base/knowledge-base.php'; public function __construct() { add_filter('site_transient_update_plugins', [$this, 'check_for_updates']); } public function check_for_updates($transient) { if (empty($transient->checked)) return $transient; $response = wp_remote_get($this->api_url . 'check', [ 'body' => [ 'version' => $transient->checked[$this->plugin_slug], 'license' => get_option('kb_license_key') ] ]); if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) { return $transient; } $data = json_decode(wp_remote_retrieve_body($response)); if (version_compare($data->new_version, $transient->checked[$this->plugin_slug], '>')) { $transient->response[$this->plugin_slug] = (object) [ 'slug' => 'knowledge-base', 'plugin' => $this->plugin_slug, 'new_version' => $data->new_version, 'package' => $data->download_url, #6A9955">// Must be a secure, expiring URL 'url' => $data->changelog_url ]; } return $transient; } }
Securing the Delivery Pipeline
The package URL is the most critical part. Never expose a static link to your ZIP file. If a user shares that link, anyone can download your premium code.
- Expiring URLs: When the client requests an update, generate a one-time-use, expiring URL (e.g., valid for 5 minutes).
- Signature Verification: Use a private key to sign the download request. Ensure the server validates the license key headers before streaming the file.
- Authentication: Use the Automated CI/CD Pipelines: Streamlining WordPress Plugin Delivery to push your build artifacts to a secure S3 bucket, and have your API generate a pre-signed URL from there.
Hands-on Exercise: Implementing the Mock API
- Create a local PHP script (or a separate microservice) that acts as your Update API.
- Ensure it returns a JSON object containing
new_version,download_url, andchangelog_url. - In your
KnowledgeBaseplugin, register theUpdateManagerservice. - Force the transient update by deleting the
update_pluginstransient inwp_optionsviadelete_site_transient('update_plugins');. - Observe your plugin showing an "Update Available" notification in the WordPress admin panel.
Common Pitfalls
- Caching the Response: The
site_transient_update_pluginsfilter runs frequently. If your API is slow, you'll degrade site performance. Always cache your remote API response for at least 12 hours. - The "Package" URL Format: WordPress expects the
packagekey to be a direct URL to a.zipfile. If you use a redirect, ensure your server follows it correctly and that the final URL ends in.zip(WordPress enforces this). - Missing
plugin_slug: If the slug in your API response doesn't match the folder structure of your plugin (e.g.,knowledge-base/knowledge-base.php), the update will fail to unzip correctly.
Recap
We've bridged our plugin to a custom distribution server. By leveraging the site_transient_update_plugins filter, we provide a native WordPress experience for our customers while maintaining total control over licensing and versioning. This completes the distribution architecture we began in the earlier modules.
Up next: Documentation Systems — we'll automate the generation of API docs and user manuals directly from your codebase.
Work with me

Custom WordPress Plugin Development
Custom WordPress & WooCommerce plugins built to standard — by the developer behind a plugin with 5,000+ active installs and a SaaS with 10,000+ users.

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.