Pottr is a WordPress plugin that equips WordPress with an application backend layer — providing structured custom collections, REST APIs, decoupled app-user authentication, automated schema blueprints, and outgoing webhooks.
The Developer Mental Model
Traditional WordPress is optimized for content management — posts, pages, categories, and custom fields stored inside wp_posts and wp_postmeta. When you build web apps, client portals, directories, booking systems, or lightweight SaaS products, forcing application entities into WordPress posts requires stitching together plugins and complex queries.
Pottr changes this equation by giving WordPress a dedicated application backend layer:
Your Frontend
React · Next.js · Vue · Mobile · SPA
↓ REST JSON ↓
Pottr Engine
REST API · Auth · Schema · Webhooks
Underneath: Runs natively inside your client's existing WordPress & MySQL server. No external BaaS fees or vendor lock-in.
What Pottr Provides
Dedicated Collections: Define structured schemas with 9 field types without polluting WordPress Custom Post Types or postmeta tables.
API Key Gateway: Public keys (pk_live_...) for frontend clients and Secret keys (sk_live_...) for server/CI pipelines.
Decoupled App-User Authentication: Issue JWT access tokens and 30-day refresh tokens for your application users without creating administrative wp_users records.
Row-Level Security: Automatic user-owned data scoping by adding a user_id field to any collection.
Declarative Provisioning (Blueprints): Bootstrap or migrate collection schemas and auth fields via JSON on application boot or deploy.
HMAC Signed Webhooks: Outgoing event notifications (entry.created, entry.updated, entry.deleted) verified with SHA-256 signatures.
Self-Contained Architecture
Pottr is 100% self-hosted on your WordPress server. All collections, entries, and app-user profiles reside on your MySQL database. No external cloud service is required to store or query your data.
Pottr bridges the gap between client-friendly WordPress management and modern frontend engineering.
The End-to-End Workflow
Install Pottr in WordPress: Activate the plugin in any standard WordPress 6.0+ installation.
Define Your Application Data: Create collections visually in the WordPress Admin or declare them in code using a Blueprint JSON document.
Public REST API Exposure: Pottr immediately exposes structured JSON endpoints at /wp-json/pottr/v1/collections/{slug} with automatic filtering, sorting, and pagination.
Connect Your Frontend: Use your Public Key (pk_live_...) in your React, Next.js, Vue, or mobile frontend to read and write collection entries.
Add Application Capabilities: Enable decoupled app-user authentication (JWTs), turn on row-level security for user-owned records, configure webhooks, or automate deployments.
WordPress vs. Pottr Responsibilities
Layer
Component
Responsibilities
WordPress
Hosting & Runtime
PHP/MySQL runtime, Admin UI, media storage, transactional mail (wp_mail), cron scheduling.
Pottr
Application Engine
Schema definitions, REST API routes, API key security, JWT app authentication, webhook dispatching, query filtering.
Frontend
User Experience
Client UI (React, Vue, Next.js, mobile app), user interaction, frontend state management.
System prerequisites, server checklist, and activation steps for running Pottr on WordPress.
Verified System Requirements
Requirement
Minimum Version
Notes
WordPress
6.0+
Requires active REST API support.
PHP
7.4+
Compatible with PHP 8.0, 8.1, 8.2, and 8.3+.
MySQL / MariaDB
MySQL 5.7+ or MariaDB 10.2.3+
Strictly required for native JSON storage and extraction functions.
Permalinks
Pretty Permalinks Enabled
WordPress permalinks must not be set to "Plain" so /wp-json/ routes resolve cleanly.
Outbound HTTP
HTTPS / cURL
Required for outgoing webhooks and license validation.
Installation Steps
Upload the pottr plugin folder to your WordPress installation's /wp-content/plugins/ directory (or install via the WordPress Plugins screen).
Navigate to Plugins → Installed Plugins in WordPress Admin and click Activate under Pottr.
Upon activation, Pottr registers the /wp-json/pottr/v1/ API namespace and provisions necessary schema tables.
Navigate to the new Pottr menu item in the WordPress admin sidebar to view the dashboard and copy your API Keys.
Permalink Structure Notice
If you receive 404 errors when querying /wp-json/pottr/v1/*, ensure your site has Pretty Permalinks enabled under Settings → Permalinks (select "Post name" or any non-plain format).
How Pottr models structured application entities using 9 field types, strict validation, and normalized schemas.
What is a Collection?
In Pottr, a Collection represents a structured data entity — such as projects, tickets, invoices, or bookings. Unlike WordPress posts, collections do not use wp_posts or wp_postmeta, keeping your application data separated from editorial content.
Collection slugs must use lowercase alphanumeric characters with hyphens or underscores (matching ^[a-z][a-z0-9_-]*$).
The 9 Supported Field Types
Type
Data Representation
Description & Sanitization
text
String
Single-line text. Sanitized via WordPress sanitize_text_field().
textarea
String
Multi-line plain text. Sanitized via sanitize_textarea_field().
rich_text
String (HTML)
Formatted HTML text. Sanitized via wp_kses_post().
number
Number (int / float)
Stored and indexed as numeric values.
boolean
Boolean (true / false)
Boolean flag. Normalized to standard boolean on output.
date
String (ISO-8601)
Date/datetime string (e.g. 2026-05-10 or 2026-05-10T14:30:00Z).
relation
Integer (Entry ID)
Foreign key ID referencing an entry in another collection.
media
Integer (Attachment ID)
WordPress media attachment ID.
json
Object / Array
Arbitrary structured JSON data payload.
The Primary Title Field
Every collection in Pottr includes a primary title field (type text, required). If you declare a collection via Blueprints without a title field, Pottr automatically prepends it to the schema.
Field Validation Rules
Field schemas support optional validation constraints enforced on POST, PUT, and PATCH:
required (boolean): Rejects empty or missing values.
min_length / max_length (integers): String length bounds on text and textarea.
pattern (string): Regex pattern for custom format validation (e.g. ^[A-Z0-9]{6}$).
min / max (numbers): Numerical range constraints on number fields.
options (array of strings): Enum list of allowed values for dropdown/select fields.
Connecting entities across collections and resolving linked records with single-level relation expansion.
Declaring a Relation Field
To link one collection to another, declare a field with type: "relation" and specify the target_collection slug. By convention, relation field keys should use the _id suffix (e.g. project_id, author_id).
By default, querying a collection returns the numeric ID stored in the relation field. When your frontend needs the related data, append ?expand=field_key to inline the full related entry in a single HTTP request:
GET /wp-json/pottr/v1/collections/tasks?expand=project_id
Relation expansion is single-level. Pottr does not perform recursive nested expansions to ensure predictable performance and prevent circular dependency loops.
Automatic data isolation and row-level privacy for multi-tenant portals, user dashboards, and private application stores.
How User-Ownership Works
Any collection containing a field named user_id (type relation or number) is automatically designated as user-owned. Pottr enforces strict row-level security without requiring custom permissions code:
Read Scoping:GET /collections/{slug} queries automatically filter to only return entries owned by the authenticated app user (identified by their JWT). Unauthenticated requests return an empty list [].
Server-Side ID Injection: When an authenticated user creates an entry (POST /collections/{slug}), Pottr extracts the user's ID from their verified JWT and attaches it server-side. Clients should omit user_id from the request body.
Modification & Deletion Protection: When updating (PUT/PATCH) or deleting (DELETE), Pottr verifies that the target entry's user_id matches the authenticated user. Attempts to alter another user's entry return 403 forbidden.
Administrative Access: Requests authenticated with the Secret Key (sk_live_...) or an active WordPress Administrator session bypass ownership checks, allowing backends and administrators to manage entries across all users.
Example: Creating a User-Owned Entry
create-private-note.js
// Notice: 'user_id' is NOT included in the body — it is injected server-side from the JWTconst res = awaitfetch('https://example.com/wp-json/pottr/v1/collections/notes', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.NEXT_PUBLIC_POTTR_PUBLIC_KEY}`,
'X-Pottr-User-Token': userAccessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'My Private Financial Goal',
content: 'Confidential user data'
})
});
Replaces the full entry record. All required schema fields must be present and will be re-validated. Omitted optional fields are reset to their default empty values.
5. Partial Update (PATCH)
PATCH/collections/{slug}/{id}Public or Secret Key
Partially updates an entry. Only the submitted fields are validated and updated; omitted fields retain their existing values.
PATCH Request
// Mark a task as completed without sending other fields:awaitfetch('https://example.com/wp-json/pottr/v1/collections/tasks/42', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ completed: true })
});
6. Delete Entry (Soft-Delete)
DELETE/collections/{slug}/{id}Public or Secret Key
Soft-deletes the entry. The entry immediately becomes invisible to all REST API queries. Soft-deleted records can be viewed or restored in the WordPress Admin.
Pagination parameters, sorting directives, field equality filters, and batch ID retrieval.
Query Parameters Reference
Parameter
Type
Default
Description
limit
Integer
20
Number of entries to return. Maximum value is 100.
offset
Integer
0
Number of entries to skip for pagination.
order_by
String
created_at
Sort field: created_at, updated_at, or any custom schema field key.
order
String
DESC
Sort direction: ASC (ascending) or DESC (descending).
filter[key]
String
—
Equality filter on any schema field (e.g. filter[status]=active).
ids
String
—
Comma-separated list of entry IDs to fetch directly (e.g. ?ids=1,2,5).
expand
String
—
Relation field key to inline related target collection entry.
Filtering Examples
Multiple filters combine automatically with boolean AND:
cURL Query Example
# Filter active projects, sorted by updated date, page 2:
curl -H "Authorization: Bearer pk_live_..." \
"https://example.com/wp-json/pottr/v1/collections/project?filter\[status\]=active&filter\[category\]=design&order_by=updated_at&order=DESC&limit=10&offset=10"
Batch ID Lookups
To fetch specific entries without manual iterative loops, pass ?ids=101,102,105. Batch queries maintain full row-level security for user-owned collections.
Understanding the Public Key vs Secret Key security model and endpoint permission gates.
Key Types & Prefixes
Key Type
Prefix
Environment
Permitted Scope
Public Key
pk_live_...
Frontend Web, Mobile, Jamstack
• App bootstrapping (POST /auth/bootstrap)
• User authentication (Signup, Login, Refresh, Password Reset)
• Public collection reads
• User-owned collection writes (paired with User JWT)
Secret Key
sk_live_...
Backend Servers, CI/CD Pipelines
• Full administrative access across all endpoints
• Blueprint provisioning (POST /blueprint)
• Schema discovery (GET /schema)
• Webhooks management (/webhooks)
• Bypasses row-level user ownership restrictions
Security Rule: Never Expose Secret Keys
Never embed your sk_live_... secret key in frontend JavaScript bundles, mobile app binaries, or public Git repositories. Secret keys have administrative privileges to manage webhooks, blueprints, and view un-scoped data.
Key Regeneration
If a secret key is compromised, navigate to Pottr → API Keys in WordPress Admin and click Regenerate. Regenerating a key immediately invalidates the previous key across all active API requests.
How are App Users Different from WordPress Admins?
Pottr provides an independent authentication layer designed specifically for end-users of your custom application:
No WordPress Admin Access: App users cannot log into /wp-admin and have no core WordPress capabilities.
No wp_users Bloat: App users are stored in dedicated application authentication tables, keeping WordPress administration clean.
Custom Profile Fields: Blueprints allow declaring custom fields on user accounts (e.g. full_name, company, avatar_url).
Stateless JWT Tokens: Authenticated via 15-minute HS256 JWT access tokens and 30-day hashed refresh tokens.
App Header Requirement (X-Pottr-App)
Every authentication endpoint requires an application identifier passed in the X-Pottr-App: <app_slug> request header (or an app string in the JSON body).
Authentication Endpoints
1. User Signup
POST/auth/signupPublic Key Required
Registers a new app user. Password must be at least 8 characters. Returns 201 Created with tokens.
Declarative schema definitions for automated, non-destructive application provisioning on boot or deploy.
What are Blueprints?
A Blueprint is a JSON document declaring your application's required collections, fields, and user authentication profile schema. When submitted to Pottr, the engine provisions missing collections and adds new fields automatically.
Additive, Non-Destructive Provisioning
Pottr provisioning is strictly additive:
Existing collections and entries are never dropped or deleted.
Existing fields retain their data.
Any field defined in the blueprint that does not yet exist is safely added to the schema.
The required primary title text field is automatically prepended if omitted.
Public vs Secret Provisioning Endpoints
Endpoint
Required Credential
Typical Usage
POST /auth/bootstrap
Public Key (pk_live_...)
Client-side web or mobile applications on initial boot. Rejects secret keys.