Skip to main content
Developers

GPLC Connector API reference

The v1 REST API the connector plugin uses: authenticate a site with a license key, browse the catalog, spend credits on installs, check for updates. Same contract, documented.

Overview

The v1 API provides endpoints for:

  • Client authentication (WordPress site → Zeebrar)
  • Plugin catalog access (all plugins visible to paying subscribers)
  • Credit consumption for downloads
  • Update checking
  • Account status
Base URL: https://zeebrar.com/api/v1

Authentication

License Key Auth (Preferred)

POST /api/v1/client/auth
Request:
{

"licenseKey": "zb_xxxxxxxxxxxxxxxx",

"siteUrl": "https://client-site.com",

"siteTitle": "Client Site"

}

Response:
{

"success": true,

"token": "eyJhbGciOiJIUzI1NiIs...",

"customer": {

"id": "cust_xxx",

"email": "[email protected]",

"name": "John Doe",

"tier": "agency",

"tierLabel": "Agency",

"subscription_status": "active"

},

"credits": {

"available": 89,

"used": 11,

"reset_at": "2026-02-01T00:00:00Z",

"_note_reset_at": "Next credit allocation date. null if subscription is cancelled (credits frozen)."

},

"subscriptions": [

{

"id": "sub_xxx",

"tier": "agency",

"status": "active",

"product_name": "Agency Monthly",

"start_date": "2026-01-01T00:00:00Z",

"end_date": null

}

],

"client": {

"id": "client_xxx",

"api_key": "zb_xxx"

},

"store": {

"api_url": "https://zeebrar.com",

"store_type": "saas_managed",

"brand_name": "Zeebrar",

"upgrade_url": "https://zeebrar.com/pricing",

"checkout_url": "https://zeebrar.com/checkout",

"account_url": "https://zeebrar.com/account"

}

}

Legacy Email/Password Auth (Deprecated)

POST /api/v1/client/auth
Request:
{

"email": "[email protected]",

"password": "xxx",

"site_url": "https://client-site.com",

"site_name": "Client Site"

}

Response includes _deprecation warning. Migrate to license key auth.

Subsequent Requests

All other endpoints require JWT Bearer token:

Authorization: Bearer <token>

Endpoints

Catalog

#### GET /api/v1/plugins

List all plugins in the catalog. All paying subscribers see the full catalog - credits are the only gate for downloads.

Query params:
ParamTypeDefaultDescription
pageint1Page number
limitint20Items per page (max: 100)
searchstring-Search in name and description
sortstringfeaturedSort order (see below)
categorystring-Filter by category slug
tagsstring-Comma-separated tag names (case-insensitive, plugins with at least one match)
levelstring-Exact product level: lite, pro, geek, exclusive
newstring-If true, only show plugins added within the last 3 days
Sort Options:
ValueDescription
featuredFeatured plugins first, then alphabetical (default)
name-ascAlphabetical A-Z
name-descAlphabetical Z-A
updatedMost recently updated first
newMost recently added first (by date added to catalog)
popularMost downloaded first (Zeebrar store downloads only)
relevanceBest search match first (auto-selected when search is provided)
Level Filtering:
  • level: Filters by exact product level. Use when user wants to see only "Pro" plugins.
- Example: ?level=pro → returns only plugins where tier_required == "pro"

- Example: ?level=lite → returns only Lite-level plugins

Response:
{

"plugins": [

{

"id": "uuid",

"slug": "plugin-name",

"name": "Plugin Name",

"version": "1.2.0",

"description": "Short description",

"credits_required": 1,

"tier_required": "pro",

"has_access": true,

"icon_url": "https://...",

"category": "forms",

"tags": ["woocommerce", "forms"],

"compatibility": {

"wp_requires": "6.0",

"wp_tested": "6.7",

"php_requires": "7.4",

"wc_requires": null,

"wc_tested": null

},

"updated_at": "2026-01-20T00:00:00Z",

"created_at": "2025-12-23T00:00:00Z"

}

],

"pagination": {

"page": 1,

"per_page": 20,

"total": 45,

"total_pages": 3

},

"filters": {

"categories": [

{ "name": "Forms", "slug": "forms", "count": 45 }

],

"tiers": [

{ "id": "lite", "credits": 1, "count": 100 },

{ "id": "pro", "credits": 3, "count": 50 }

]

}

}

#### GET /api/v1/plugins/[slug]

Get detailed plugin information including version history for rollback support.

Response:
{

"plugin": {

"slug": "plugin-name",

"name": "Plugin Name",

"version": "1.2.0",

"currentVersion": "1.2.0",

"description": "Full description...",

"creditCost": 1,

"tier": "pro",

"tier_required": "pro",

"has_access": true,

"iconUrl": "https://...",

"changelog": "### 1.2.0\n- Feature A\n- Bug fix B",

"requires": "6.0",

"tested": "6.7",

"php_requires": "7.4",

"compatibility": {

"wp_requires": "6.0",

"wp_tested": "6.7",

"php_requires": "7.4",

"wc_requires": "8.0",

"wc_tested": "9.0"

},

"lastUpdated": "2026-01-20T00:00:00Z",

"versions": [

{

"version": "1.2.0",

"changelog": "### 1.2.0\n- Feature A",

"released_at": "2026-01-20T00:00:00Z",

"file_size": 245760,

"has_file": true,

"scan_status": "completed",

"threat_level": "clean",

"scan_report_url": "https://www.virustotal.com/gui/file/abc123..."

},

{

"version": "1.1.0",

"changelog": "### 1.1.0\n- Initial release",

"released_at": "2026-01-10T00:00:00Z",

"file_size": 230400,

"has_file": false,

"scan_status": "completed",

"threat_level": "clean",

"scan_report_url": null

}

]

}

}

FieldTypeDescription
versionstringLatest version (same as currentVersion)
currentVersionstringLatest version (client compatibility alias)
tier_requiredstringProduct level (determines credit cost)
has_accessbooleantrue for all paying subscribers, false for free tier
compatibilityobjectWordPress/PHP/WooCommerce compatibility (may be null if not parsed)
compatibility.wp_requiresstringMinimum WordPress version required
compatibility.wp_testedstringWordPress version tested up to
compatibility.php_requiresstringMinimum PHP version required
compatibility.wc_requiresstring\nullMinimum WooCommerce version (WC extensions only)
compatibility.wc_testedstring\nullWooCommerce version tested up to (WC extensions only)
versionsarrayVersion history for rollback support
versions[].has_filebooleanWhether version file is available for download. false for archived versions where the file has been removed from S3. The download endpoint rejects archived versions - disable rollback/install UI when false.
versions[].scan_statusstring\nullVirus scan status: completed, scanning, error, skipped, unknown, or null
versions[].threat_levelstring\nullThreat assessment: clean, suspicious, malicious, or null
versions[].scan_report_urlstring\nullFull VirusTotal scan report URL (null if not scanned)

Downloads

#### POST /api/v1/plugins/[slug]/download

Request download URL. Deducts credits from customer account (or free for version rollbacks).

Request:
{

"version": "1.2.0",

"wpVersion": "6.7.1",

"phpVersion": "8.2.0",

"wcVersion": "9.5.1",

"bypassSuspicious": false

}

FieldTypeRequiredDescription
versionstringNoSpecific version to download. Defaults to latest.
wpVersionstringNoSite's WordPress version (for compatibility check)
phpVersionstringNoSite's PHP version (for compatibility check)
wcVersionstringNoSite's WooCommerce version (for compatibility check)
bypassSuspiciousbooleanNoIf true, skip the suspicious file warning and proceed
Response:
{

"success": true,

"downloadUrl": "https://...",

"expiresAt": "2026-01-23T13:00:00Z",

"creditsDeducted": 1,

"creditsRemaining": 88,

"version": "1.2.0",

"isRollback": false,

"warnings": [

{ "code": "INCOMPATIBLE_WP", "message": "Requires WordPress 6.8+, site runs 6.7.1" }

]

}

FieldTypeDescription
downloadUrlstringSigned S3 URL for download
expiresAtstringURL expiration timestamp
creditsDeductednumberCredits charged (0 for rollbacks)
creditsRemainingnumberUser's remaining credit balance
versionstringVersion being downloaded
isRollbackbooleanTrue if downloading an older version
warningsarraySafety warnings (only present if non-empty). See Safety Gates below.
Errors:
  • 402: Insufficient credits (INSUFFICIENT_CREDITS)
  • 403: Version rollback requires prior download (ROLLBACK_NOT_ELIGIBLE)
  • 403: Malicious file blocked (MALICIOUS_FILE) - see Safety Gates below
  • 404: Plugin not found (NOT_FOUND)
  • 404: Plugin file not available (FILE_NOT_AVAILABLE)

#### Version Rollback

Users can download previous versions for free if they have previously downloaded the plugin:

  • Eligibility: User must have at least one prior download of the plugin
  • Cost: Rollbacks are free (0 credits deducted)
  • Tracking: Downloads are still recorded with isRollback: true in store context
Rollback Example:
// Request older version

{ "version": "1.1.0" }

// Response (free rollback)

{

"success": true,

"downloadUrl": "https://...",

"creditsDeducted": 0,

"creditsRemaining": 88,

"version": "1.1.0",

"isRollback": true

}

> Credit Refunds: If download fails after credits are deducted (e.g., backend error), credits are automatically refunded.

> Note: Legacy endpoint /api/v1/plugins/[slug]/install also exists with slightly different response format. Use /download for new integrations.

Safety Gates (Pre-Install Checks)

All download and install endpoints run pre-install safety checks before deducting credits. Two types of checks:

#### Compatibility Checks

If the connector sends wpVersion, phpVersion, or wcVersion in the request body, the API validates them against the plugin's requirements (wpRequires, phpRequires, wcRequires). Incompatible versions produce warnings (non-blocking):

Warning CodeExample Message
INCOMPATIBLE_WP"Requires WordPress 6.8+, site runs 6.7.1"
INCOMPATIBLE_PHP"Requires PHP 8.1+, site runs 7.4.33"
INCOMPATIBLE_WC"Requires WooCommerce 9.0+, site runs 8.5.2"

Warnings are included in the warnings array of successful responses. The download/install still proceeds.

#### VirusTotal Threat Checks

Each plugin version carries a VirusTotal scan status. The API checks the version's threatLevel before allowing download:

Threat LevelBehaviorHTTP Status
cleanProceed normally200
suspiciousWarning in response, proceed200
maliciousBlocked - install refused403
Suspicious file response (success with warning):
{

"success": true,

"warnings": [

{

"code": "SUSPICIOUS_FILE",

"message": "Version 2.1.0 has been flagged as suspicious by VirusTotal - proceed with caution. Report: https://..."

}

]

}

To bypass the suspicious warning (user confirmed), re-send with "bypassSuspicious": true.

Malicious file response (blocked):
{

"error": "Version 2.1.0 has been flagged as malicious by VirusTotal and cannot be installed. Report: https://...",

"code": "MALICIOUS_FILE",

"warnings": []

}

HTTP 403. Hard block - no bypass option. The connector should display the error with the VirusTotal report link.

#### Dashboard Remote Install

The dashboard install endpoint (POST /api/account/sites/[clientId]/plugins/install) runs the same safety checks but automatically fetches the site's WP/PHP versions via getRemoteSiteHealth() - no manual version passing needed.

Updates

#### POST /api/v1/plugins/check-updates

Check for available updates for installed plugins.

Request:
{

"plugins": [

{ "slug": "plugin-a", "version": "1.0.0" },

{ "slug": "plugin-b", "version": "2.3.1" }

]

}

Response:
{

"updates": [

{

"slug": "plugin-a",

"currentVersion": "1.0.0",

"newVersion": "1.2.0",

"creditCost": 1,

"changelog": "### 1.2.0\n- ..."

}

]

}

Account

#### GET /api/v1/account

Get current account status including tier, credits, and subscription info.

Response:
{

"email": "[email protected]",

"tier": "agency",

"tierLabel": "Agency",

"creditsAvailable": 88,

"creditsUsed": 12,

"creditsResetAt": "2026-02-01T00:00:00Z", // Next allocation date; null if cancelled (credits frozen)

"subscriptionStatus": "active"

}

#### GET /api/v1/credits

Alternative endpoint for credit balance only.

Response:
{

"tier": "agency",

"subscription_status": "active",

"credits": {

"available": 88,

"used": 12,

"reset_at": "2026-02-01T00:00:00Z" // Next allocation date; null if cancelled (credits frozen)

},

"next_payment": "2026-02-01T00:00:00Z",

"top_up_url": "https://zeebrar.com/checkout/credits"

}

Site Disconnect

#### POST /api/v1/client/disconnect

Called by the gplc-connector plugin when the user disconnects from the WordPress side.

Deactivates the client record so it no longer counts toward the site limit and shows as revoked on the Zeebrar account page.

Auth: Bearer token (JWT from /client/auth) Headers:
Authorization: Bearer <token>
Response (success):
{

"success": true,

"message": "Site disconnected successfully"

}

Response (already disconnected - idempotent):
{

"success": true,

"message": "Site already disconnected"

}

Errors: TOKEN_INVALID (401), DISCONNECT_ERROR (500) Connector implementation: Call this endpoint when the user clicks "Disconnect" or "Log Out" in the WordPress plugin settings, before clearing local credentials. This ensures the site is properly freed from the account's site limit.

Site Revoke

#### POST /api/v1/client/revoke

Revoke access for a connected site from the Zeebrar dashboard. Sets the client to inactive and invalidates its API key. The site will show as "Revoked" on the account page and no longer count toward the site limit.

Auth: Session (user must be logged into Zeebrar dashboard) Request:
{

"clientId": "client-uuid"

}

Response:
{

"success": true

}

Errors: UNAUTHORIZED (401), NOT_FOUND (404) Side effects: Syncs revocation to GPLC via deleteClientSite (fire-and-forget).

Site Reconnect

#### POST /api/v1/client/reconnect

Reactivate a previously revoked site. Generates a new API key and syncs the client with the customer's current tier/credits. Returns the customer's license key so the user can re-enter it in WordPress.

Auth: Session (user must be logged into Zeebrar dashboard) Request:
{

"clientId": "client-uuid"

}

Response (success):
{

"success": true,

"message": "Site reconnected. Re-enter your license key in the WordPress plugin to complete setup.",

"licenseKey": "zb_xxxxxxxxxxxxxxxx"

}

Errors:
  • 404: Site not found or not eligible for reconnection
  • 403: SITE_LIMIT_REACHED - tier site limit exceeded
Note: Checks the tier's maxSites limit before reactivating. If the limit is reached, returns an error suggesting the user revoke another site or upgrade.

Site Remove

#### POST /api/v1/client/remove

Permanently delete a revoked site record. Only works on sites that are already revoked (isActive: false). Download history is preserved (FK set to null).

Auth: Session (user must be logged into Zeebrar dashboard) Request:
{

"clientId": "client-uuid"

}

Response:
{

"success": true

}

Errors: 404 - Site not found or still active (must revoke first) Side effects: Syncs deletion to GPLC via deleteClientSite (fire-and-forget). Download records are detached (not deleted).

---

Error Format

All errors return:

{

"error": "Human-readable message",

"code": "ERROR_CODE"

}

Error codes:
CodeHTTP StatusDescription
UNAUTHORIZED401Missing/invalid token
FORBIDDEN403Access denied
NOT_FOUND404Resource not found
FILE_NOT_AVAILABLE404Plugin exists but file not uploaded
INSUFFICIENT_CREDITS402Not enough credits
MALICIOUS_FILE403VirusTotal flagged version as malicious (hard block)
LICENSE_INVALID401Invalid license key
LICENSE_EXPIRED403License key expired
LICENSE_SUSPENDED403License suspended
SITE_REVOKED403Site access was revoked by user
SITE_LIMIT_REACHED403Plan site limit exceeded (admin-configurable via SubscriptionPlan.maxSites)
RATE_LIMITED429Too many requests
INTERNAL_ERROR500Server error

Rate Limits

Limits are enforced per client IP (X-Forwarded-For) with a sliding window; a 429 carries Retry-After.

  • POST /api/v1/client/auth: 5 requests/minute
  • POST /api/v1/plugins/{slug}/download: 30 requests/minute
  • POST /api/v1/plugins/{slug}/install (dashboard-initiated): 20 requests/minute
  • Other v1 endpoints: no dedicated limiter today; the per-client, per-plugin 30-second duplicate-download guard still applies

Subscription Tiers

All paying subscribers can browse and install any plugin. Credits are the only gate.

TierDisplay NameCredits/moSite Limit
freeFree01
personalPersonal755
proPro20015
agencyAgency600Unlimited

> Note: Use tierLabel from API responses for user-facing display.

Credit Costs (Plugin Pricing)

Plugins have a product level (tier_required) that determines how many credits are required to download.

Credit costs are configured per-store in Zeebrar admin settings.

Current Zeebrar Credit Costs:
Product LevelCreditsBadge Color
Lite1Green
Pro3Blue
Geek5Purple
Exclusive10Amber
API Response Fields (from /api/v1/plugins):
  • credits_required: Number of credits required (integer) - use this for display
  • tier_required: Product level (lite, pro, geek, exclusive) - determines credit cost
  • has_access: true for all paying subscribers, false for free tier
  • compatibility: Object with version requirements (may be null if not parsed):
- wp_requires, wp_tested, php_requires - WordPress/PHP compatibility

- wc_requires, wc_tested - WooCommerce compatibility (only for WC extensions, null otherwise)

The connector should display the credit cost directly (not tier names):

// Example: Display credit badge in plugin list

$credits = $plugin['credits_required']; // e.g., 3

$has_access = $plugin['has_access']; // e.g., true

// Badge color based on credit count

$badge_class = match(true) {

$credits === 0 => 'badge-gray',

$credits === 1 => 'badge-green',

$credits <= 3 => 'badge-blue',

$credits <= 5 => 'badge-purple',

default => 'badge-amber',

};

// Display: "3 credits"

echo sprintf('<span class="%s">%d credit%s</span>',

$badge_class,

$credits,

$credits !== 1 ? 's' : ''

);

// Compatibility badges

$compat = $plugin['compatibility'] ?? null;

if ($compat) {

if (!empty($compat['wp_requires'])) {

echo '<span class="badge-blue">WP ' . $compat['wp_requires'] . '+</span>';

}

if (!empty($compat['wp_tested'])) {

echo '<span class="badge-green">Tested ' . $compat['wp_tested'] . '</span>';

}

if (!empty($compat['php_requires'])) {

echo '<span class="badge-purple">PHP ' . $compat['php_requires'] . '+</span>';

}

// WooCommerce badges (only for WC extensions)

if (!empty($compat['wc_requires'])) {

echo '<span class="badge-violet">WC ' . $compat['wc_requires'] . '+</span>';

}

if (!empty($compat['wc_tested'])) {

echo '<span class="badge-fuchsia">WC Tested ' . $compat['wc_tested'] . '</span>';

}

}

Compatibility Badge Colors:
FieldBadge ColorExample
wp_requiresBlue"WP 6.0+"
wp_testedGreen"Tested 6.7"
php_requiresPurple"PHP 7.4+"
wc_requiresViolet"WC 8.0+"
wc_testedFuchsia"WC Tested 9.0"
WooCommerce Extensions: Plugins with non-null wc_requires or wc_tested are WooCommerce extensions. You may display a "WooCommerce Extension" label for these plugins.

Admin Support Endpoints

Endpoints for Zeebrar admin staff to support customer sites.

GET /api/admin/clients/{clientId}/diagnostics

Fetch diagnostics from a customer's WordPress site for support troubleshooting.

Authentication: Admin session required Query Parameters:
ParamTypeDefaultDescription
include_connectivitybooleanfalseInclude connectivity tests (slower)
error_limitint20Max errors to return (1-100)
Response:
{

"success": true,

"client": {

"id": "client-uuid",

"siteUrl": "https://customer-site.com",

"siteName": "Customer Site"

},

"diagnostics": {

"environment": {

"wordpress_version": "6.7",

"php_version": "8.2",

"plugin_version": "1.0.5",

"site_url": "https://customer-site.com",

"is_multisite": false,

"active_theme": "Astra",

"memory_limit": "256M",

"max_execution_time": 30

},

"gplc_state": {

"connected": true,

"email": "[email protected]",

"tier": "agency",

"credits_available": 89,

"credits_used": 11,

"subscription_status": "active",

"api_url": "https://zeebrar.com",

"last_auth": "2026-01-20T14:00:00Z",

"token_valid": true,

"installed_plugins": 12,

"installed_themes": 3

},

"sync_health": {

"last_sync": "2026-01-20T14:00:00Z",

"syncs_24h": 24,

"failed_syncs_24h": 0,

"average_sync_time": 150

},

"errors": [

{

"timestamp": "2026-01-20T12:00:00Z",

"type": "download_failed",

"message": "Connection timeout",

"context": "plugin-slug v1.2.0"

}

],

"performance": {

"php_memory_usage": 52428800,

"php_memory_peak": 67108864,

"db_queries": 42,

"db_query_time": 0.125

},

"connectivity": {

"zeebrar_api": { "reachable": true, "latency_ms": 120 },

"ssl_valid": true,

"curl_available": true

}

},

"fetchedAt": "2026-01-20T15:30:00Z"

}

Errors:
CodeHTTP StatusDescription
UNAUTHORIZED401Not logged in as admin
NOT_FOUND404Client not found
CLIENT_INACTIVE400Client site is inactive
CLIENT_REVOKED400Client access was revoked
FETCH_FAILED502Failed to fetch from client site
TIMEOUT504Request timed out (>15s)
CONNECTION_FAILED502Could not connect to client site
Usage: This endpoint is used by the admin customer detail page (/admin/customers/[id]) to fetch live diagnostics from connected WordPress sites for support troubleshooting. How it works:
  1. Admin clicks "Diagnostics" button on a connected site
  2. Zeebrar calls the client's WordPress site at GET /wp-json/gplc/v1/diagnostics
  3. Request is authenticated using the client's stored API key (X-GPLC-API-Key header)
  4. Response is returned to admin UI for display

WordPress Plugin Integration

The gplc-connector plugin uses this API:

// License key authentication (preferred)

$response = wp_remote_post(ZEEBRAR_API_URL . '/v1/client/auth', [

'headers' => ['Content-Type' => 'application/json'],

'body' => json_encode([

'licenseKey' => $license_key,

'siteUrl' => home_url(),

'siteTitle' => get_bloginfo('name')

])

]);

// Subsequent requests with token

$response = wp_remote_get(ZEEBRAR_API_URL . '/v1/plugins', [

'headers' => [

'Authorization' => 'Bearer ' . $token,

'Content-Type' => 'application/json'

]

]);

---

Last updated: 2026-02-27 (added safety gates: compatibility checks, VirusTotal threat blocking, warnings array)