| name | wp-api |
| description | Use when working on WCPOS REST API endpoints, debugging auth issues, or understanding the plugin's API structure. |
WCPOS REST API Reference
IMPORTANT: X-WCPOS Header Required
The WCPOS REST API routes (/wcpos/v1/) are ONLY registered when the X-WCPOS: 1 header is present.
curl http://localhost:8888/wp-json/
curl -H "X-WCPOS: 1" http://localhost:8888/wp-json/
Authentication
WCPOS uses JWT tokens for mobile/POS client authentication. The admin/settings frontend uses standard WordPress cookie authentication via @wordpress/api-fetch — no bearer token is needed for those requests.
Token Types
- Access Token: Short-lived (30 min), used for API requests
- Refresh Token: Long-lived (30 days), used to get new access tokens
Generating Tokens
npx wp-env run cli -- wp eval '
$auth = WCPOS\WooCommercePOS\Services\Auth::instance();
$user = get_user_by("ID", 1);
echo json_encode($auth->generate_token_pair($user));
'
Sending Tokens
- Header (preferred):
Authorization: Bearer <token>
- Query param (fallback, avoid in production):
?authorization=Bearer%20<token>
⚠️ Query-parameter tokens may be exposed in server logs, browser history, and proxies.
Use only when header forwarding is unavailable, and only in controlled environments.
Apache Authorization Header Issue
Apache CGI/FastCGI strips the Authorization header by default. Add to .htaccess:
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
IMPORTANT: The .htaccess SetEnvIf rule sets HTTP_AUTHORIZATION to empty string "" when no Authorization header is sent. The code must use !empty() instead of isset() to check for auth headers.
URL Formats
/wp-json/wcpos/v1/products
/?rest_route=/wcpos/v1/products
Testing API Endpoints
curl -s \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "X-WCPOS: 1" \
"http://localhost:8888/?rest_route=/wcpos/v1/products"
Auth Matrix (2x2)
| URL Format | Header Auth | Param Auth |
|---|
Query String (?rest_route=) | Yes | Yes |
Pretty Permalink (/wp-json/) | Yes | Yes |
Common Mistakes
- Forgetting
X-WCPOS: 1 header -> 404 or empty namespace
- Using
/wp-json/ without working Apache rewrite -> 404
- Missing
.htaccess SetEnvIf -> Authorization header stripped -> 401
- Using
isset() instead of !empty() for $_SERVER['HTTP_AUTHORIZATION'] -> param auth fails
Architecture: WordPress Hook Order
plugins_loaded -> init (determine_current_user) -> rest_api_init
Init.php: determine_current_user filter at priority 20 (handles JWT auth, runs during init BEFORE rest_api_init)
API.php: Routes registered on rest_api_init (user already authenticated by this point)