Why Secure Your API Requests?
WPResidence’s API provides powerful access to sensitive real estate data including property listings, client information, agent details, and financial transactions. Proper security measures are essential to:
- Protect sensitive client information – Prevent unauthorized access to personal and financial data
- Maintain data integrity – Ensure only authorized users can modify property listings and agent profiles
- Prevent abuse – Block malicious requests that could harm system performance or data
- Comply with regulations – Meet legal requirements for data protection (GDPR, CCPA, etc.)
- Build trust – Demonstrate commitment to security best practices to clients and partners
The API endpoints in WPResidence handle high-value operations such as creating properties, managing agents and agencies, processing invoices, and facilitating communications—all of which require robust security controls.
Understanding WordPress REST API Authentication Methods
WPResidence’s API is built on WordPress REST API architecture, which offers several authentication options:
1. JWT (JSON Web Tokens)
The WPResidence API primarily uses JWT authentication as evidenced in the code files. JWT tokens are compact, self-contained tokens that securely transmit information between parties.
From agents_functions.php
:
// Verify the JWT token
$user_id = apply_filters('determine_current_user', null);
if (!$user_id) {
return new WP_Error(
'jwt_auth_failed',
__('Invalid or missing JWT token.'),
['status' => 403]
);
}
wp_set_current_user($user_id);
This pattern appears consistently across different API endpoint handlers.
2. WordPress Cookie Authentication
While not primarily used for API requests, WordPress cookie authentication may be used for testing in admin contexts.
3. OAuth Authentication
Though not explicitly implemented in the reviewed files, OAuth can be added through plugins for more complex authorization flows.
Implementing OAuth, JWT, or API Key Authentication
JWT Implementation Steps
- Token Acquisition Client applications must first obtain a JWT token by authenticating with valid credentials. This typically involves:
POST /jwt-auth/v1/token { "username": "user@example.com", "password": "secure_password" }
Response:{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user_display_name": "John Smith", "user_email": "user@example.com", "user_id": 42 }
- Using JWT with API Requests Include the token in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
- Token Validation As seen in
invoices_functions.php
:function wpresidence_check_auth_token() { // Verify the JWT token to ensure authenticated API access $userID = apply_filters('determine_current_user', null); if (!$userID) { return new WP_Error( 'jwt_auth_failed', __('Invalid or missing JWT token.'), ['status' => 403] ); } // Set current user context for WordPress permission checks wp_set_current_user($userID); // If we get here, authentication was successful return true; }
Security Considerations for JWT
- Token Expiration: Configure reasonable expiration times
- HTTPS Only: Transmit tokens only over encrypted connections
- Secure Storage: Store tokens securely on client-side (avoid localStorage)
- Token Revocation: Implement a mechanism to invalidate tokens when needed
Setting User Roles and Permissions for API Access
WPResidence implements a comprehensive role-based access control system for API endpoints. Each endpoint is protected with appropriate permission checks that verify not only authentication but also authorization.
Permission Hierarchy
From examining the code, we can identify these key permission levels:
- Public Access: Minimal endpoints (like viewing published properties) available without authentication
- User-Specific Access: Data owners can access their own records
- Agent/Agency Capabilities: Specialized abilities for real estate professionals
- Administrator Access: Full system access
Permission Implementation
Permission checks are implemented via callback functions like:
// From agency_routes.php
register_rest_route('wpresidence/v1', '/agency/edit/(?P<id>\d+)', [
'methods' => 'PUT',
'callback' => 'wpresidence_update_agency',
'permission_callback' => 'wpresidence_check_permissions_for_agency',
'args' => [
'id' => [
'validate_callback' => function($param) {
return is_numeric($param) && $param > 0;
},
'sanitize_callback' => 'absint',
'description' => 'The agent ID (positive integer)',
],
],
]);
Entity-Specific Permission Checks
For each entity type (agent, agency, developer, property, invoice), specialized permission functions examine:
- Ownership: Is the current user the creator/owner?
- Role-Based Access: Does the user have appropriate capabilities?
- Membership Status: Does the user have active membership privileges?
For example, from agencies_functions.php
:
function wpresidence_check_permissions_for_agency(WP_REST_Request $request) {
// Authentication verification...
// Check if the current user is the author of the agency or has admin rights
if (intval($agency->post_author) !== intval($user_id) && !current_user_can('edit_others_posts')) {
return new WP_Error(
'rest_forbidden',
__('You do not have permission to update this agency.'),
['status' => 403]
);
}
return true;
}
Preventing Unauthorized API Calls
Input Validation and Sanitization
WPResidence implements thorough parameter validation and sanitization to prevent injection attacks and data corruption.
From properties_functions.php
:
function wpresidence_sanitize_params_by_type($params) {
if (!is_array($params)) {
return sanitize_text_field($params);
}
$sanitized = [];
foreach ($params as $key => $value) {
$safe_key = sanitize_key($key);
if (is_array($value)) {
$sanitized[$safe_key] = wpresidence_sanitize_params_by_type($value);
} else if (is_numeric($value)) {
$sanitized[$safe_key] = filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
} else if (is_bool($value)) {
$sanitized[$safe_key] = (bool)$value;
} else if ($value === null) {
$sanitized[$safe_key] = null;
} else if (strtotime($value) !== false) {
// Date handling
$sanitized[$safe_key] = sanitize_text_field($value);
} else {
$sanitized[$safe_key] = sanitize_text_field($value);
}
}
return $sanitized;
}
Rate Limiting
Although not explicitly implemented in the provided code, production deployments should implement rate limiting to prevent abuse.
Security Headers
Critical security headers should be implemented at the server level:
- Content-Security-Policy
- X-XSS-Protection
- X-Content-Type-Options: nosniff
- Strict-Transport-Security
Using API Logs to Monitor Activity
While the provided code doesn’t explicitly implement logging, a comprehensive API security strategy should include:
Log Implementation Recommendations
- Transaction Logging: Record all API requests including:
- Timestamp
- Endpoint
- User ID
- IP address
- Request parameters
- Response status
- Suspicious Activity Alerts:
- Multiple failed authentication attempts
- Unusual request patterns
- Access attempts from unexpected locations
- Requests for sensitive operations
- Audit Log Review Process:
- Schedule regular reviews of logs
- Automate anomaly detection
- Establish response procedures for suspicious activities
- Log Retention Policy:
- Define retention period based on business needs and regulations
- Ensure logs are stored securely
- Implement log rotation to manage storage
Automating WPResidence API Calls: Connecting with External Systems
Why Automate WPResidence API Calls?
Automating API calls to WPResidence delivers significant advantages for real estate businesses:
- Operational Efficiency: Eliminate manual data entry and updates
- Data Accuracy: Reduce human error in property and agent information
- Real-time Synchronization: Keep external systems and WPResidence in sync
- Scalability: Manage large volumes of listings and agents efficiently
- Integrated Workflows: Connect real estate operations with marketing, CRM, and accounting systems
Connecting WPResidence API with Zapier or Integromat
While the WPResidence API doesn’t provide built-in Zapier/Integromat connectors, you can create custom integrations using webhooks and HTTP requests.
Integration Process:
- Authentication Setup
- Create a dedicated API user in WPResidence
- Generate and securely store JWT credentials
- Use these credentials in your Zapier/Integromat workflows
- Zapier HTTP Request Configuration
{ "method": "POST", "url": "https://yourdomain.com/wp-json/wpresidence/v1/properties", "headers": { "Content-Type": "application/json", "Authorization": "Bearer {{bundle.authData.jwt_token}}" }, "body": { "page": 1, "posts_per_page": 10 } }
- Trigger-Action Pairs
- Trigger: New CRM lead → Action: Create message in WPResidence
- Trigger: Updated property in external system → Action: Update WPResidence property
- Trigger: New agent in HR system → Action: Create agent in WPResidence
Using WPResidence API with Custom CRMs & Marketing Tools
CRM Integration Examples
- Lead Routing
- When a property inquiry comes in through the CRM, create a message to the appropriate agent
$message_data = [ 'to_user' => $agent_id, 'subject' => 'New Property Inquiry - ' . $property_title, 'message' => $inquiry_message ]; $response = wp_remote_post( 'https://yourdomain.com/wp-json/wpresidence/v1/message/add', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $jwt_token ], 'body' => json_encode($message_data) ] );
- Agent Performance Tracking
- Pull invoice data via API to analyze agent performance
$response = wp_remote_post( 'https://yourdomain.com/wp-json/wpresidence/v1/invoices', [ 'headers' => [ 'Authorization' => 'Bearer ' . $jwt_token ], 'body' => json_encode([ 'meta' => [ 'agent_id' => [ 'value' => $agent_id, 'compare' => '=', 'type' => 'NUMERIC' ] ], 'start_date' => $period_start, 'end_date' => $period_end ]) ] );
Marketing Tool Integration
- Email Marketing Automation
- Sync property data with email marketing platforms
- Generate targeted email campaigns based on property types/locations
- Update email lists when properties change status
- Social Media Automation
- Pull new listing data for automated social media posts
- Schedule property showcase posts based on API data
Automating Property Listing Updates via API
Batch Update Implementation
For bulk property updates from external systems:
function batch_update_properties($properties, $jwt_token) {
$results = ['success' => [], 'errors' => []];
foreach ($properties as $property) {
$property_id = $property['id'];
$response = wp_remote_request(
"https://yourdomain.com/wp-json/wpresidence/v1/property/edit/{$property_id}",
[
'method' => 'PUT',
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $jwt_token
],
'body' => json_encode($property['data'])
]
);
if (is_wp_error($response)) {
$results['errors'][] = [
'id' => $property_id,
'error' => $response->get_error_message()
];
} else {
$response_code = wp_remote_retrieve_response_code($response);
if ($response_code === 200) {
$results['success'][] = $property_id;
} else {
$results['errors'][] = [
'id' => $property_id,
'code' => $response_code,
'message' => wp_remote_retrieve_body($response)
];
}
}
}
return $results;
}
Scheduled Updates
Implement cron jobs to periodically sync data:
// Register cron event
add_action('init', function() {
if (!wp_next_scheduled('sync_external_property_data')) {
wp_schedule_event(time(), 'hourly', 'sync_external_property_data');
}
});
// Handle the sync
add_action('sync_external_property_data', function() {
// Fetch data from external system
$external_properties = fetch_properties_from_external_system();
// Get JWT token
$jwt_token = get_jwt_token();
// Update properties in WPResidence
batch_update_properties($external_properties, $jwt_token);
});
Scheduling Agent & Agency Updates
Examples for Common Automation Scenarios
- New Agent Onboarding
- When a new agent is added to HR system, create WPResidence agent profile
- Assign initial properties based on specialization/location
- Agent Status Updates
- Automatically update agent availability status
- Change featured status based on performance metrics
- Agency Roster Management
- Sync agency-agent relationships when staff changes occur
- Update agency stats and performance metrics
Implementation Example
function sync_agency_data($agency_id, $jwt_token) {
// Get agency data from external system
$external_data = get_agency_data_from_external_system($agency_id);
// Update agency in WPResidence
$response = wp_remote_request(
"https://yourdomain.com/wp-json/wpresidence/v1/agency/edit/{$agency_id}",
[
'method' => 'PUT',
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $jwt_token
],
'body' => json_encode([
'agency_name' => $external_data['name'],
'agency_description' => $external_data['description'],
'agency_email' => $external_data['email'],
'agency_phone' => $external_data['phone'],
// Additional fields...
])
]
);
// Process response...
}
Syncing WPResidence Data with External APIs
Bidirectional Sync Architecture
- Webhook Receivers
- Create custom endpoints to receive external system notifications
- Process incoming data and update WPResidence accordingly
- Change Detection
- Implement hooks to detect WPResidence data changes
- Trigger external API calls when properties/agents are modified
- Conflict Resolution Strategy
- Define master system for each data field
- Implement timestamp-based “last update wins” approach
- Create manual review process for critical conflicts
Example Webhook Receiver
add_action('rest_api_init', function() {
register_rest_route('external-sync/v1', '/property-update', [
'methods' => 'POST',
'callback' => 'handle_external_property_update',
'permission_callback' => 'verify_external_system_auth'
]);
});
function verify_external_system_auth($request) {
$api_key = $request->get_header('X-External-API-Key');
return $api_key === get_option('external_system_api_key');
}
function handle_external_property_update($request) {
$data = $request->get_json_params();
$property_id = $data['property_id'];
// Get JWT token for internal API calls
$jwt_token = get_internal_jwt_token();
// Update property via WPResidence API
$response = wp_remote_request(
"https://yourdomain.com/wp-json/wpresidence/v1/property/edit/{$property_id}",
[
'method' => 'PUT',
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $jwt_token
],
'body' => json_encode($data['property_data'])
]
);
// Return result
if (is_wp_error($response)) {
return new WP_Error('update_failed', $response->get_error_message(), ['status' => 500]);
}
return new WP_REST_Response(['status' => 'success'], 200);
}
Troubleshooting WPResidence API Errors: A Developer’s Guide
Understanding Common WPResidence API Errors
Based on the WPResidence API code review, errors typically fall into these categories:
Authentication Errors
- jwt_auth_failed: Invalid or missing JWT token
- rest_forbidden: User lacks necessary permissions
- not_logged_in: User session not authenticated
- insufficient_permissions: User role doesn’t allow the operation
Validation Errors
- rest_missing_field: Required parameters not provided
- rest_invalid_param: Parameter has incorrect format/value
- rest_invalid_email: Email address format invalid
- rest_invalid_[entity]: Requested entity doesn’t exist
Processing Errors
- rest_[entity]_not_found: Entity exists but wrong type
- [entity]_creation_failed: Failed to create requested entity
- deletion_failed: Failed to remove requested entity
- invoice_retrieval_failed: Error retrieving financial data
Debugging 400, 401, 403, and 500 Errors
400 Bad Request
Common causes in WPResidence:
- Missing required fields in a creation/update request
- Invalid field values (incorrect email format, invalid price)
- Improperly formatted JSON body
Troubleshooting steps:
- Check request data against endpoint requirements
- Validate all required fields are present
- Verify data types match what the API expects
Example from property_create.php
:
// Validate mandatory fields
foreach ($mandatory_fields as $field) {
if (empty($input_data[$field])) {
return new WP_Error(
'rest_missing_field',
__('Missing mandatory field: ' . $field),
['status' => 400]
);
}
}
401 Unauthorized
Common causes:
- Missing JWT token
- Expired token
- Invalid token
Troubleshooting steps:
- Check if token is included in Authorization header
- Verify token is properly formatted (
Bearer {token}
) - Ensure token hasn’t expired
- Re-authenticate to get a fresh token
403 Forbidden
Common causes:
- User doesn’t have permission for the operation
- Token is valid but insufficient permissions
- Attempting to modify content owned by another user
Troubleshooting steps:
- Verify the authenticated user has the required capabilities
- For entity operations, check if the user owns the content
- Review role requirements in the permission callback function
Example from invoice_functions.php
:
function wpresidence_check_permissions_all_invoices() {
// Verify token...
// Only allow administrators to access all invoices
if (current_user_can('administrator')) {
return true;
}
// Return error for all non-administrator users
return new WP_Error(
'rest_forbidden',
__('You do not have permission to view all invoices. Administrator access required.'),
['status' => 403]
);
}
500 Internal Server Error
Common causes:
- PHP errors in the API implementation
- Database issues
- Resource constraints (memory, execution time)
Troubleshooting steps:
- Check WordPress error logs
- Enable WP_DEBUG for more detailed error information
- Review server logs for PHP errors
- Test with simplified requests to isolate the issue
Fixing Invalid Authentication Issues
If encountering JWT authentication problems:
- Token Generation Issues
- Ensure credentials are correct when requesting tokens
- Check JWT plugin configuration
- Verify token endpoint is accessible
- Token Usage Problems
- Confirm header format:
Authorization: Bearer {token}
- Check for token corruption (whitespace, encoding issues)
- Verify token hasn’t expired
- Confirm header format:
- Authorization Code Example
// Function to get a fresh JWT token function get_jwt_token($username, $password) { $response = wp_remote_post( 'https://yourdomain.com/wp-json/jwt-auth/v1/token', [ 'body' => [ 'username' => $username, 'password' => $password ] ] ); if (is_wp_error($response)) { return false; } $body = json_decode(wp_remote_retrieve_body($response), true); if (isset($body['token'])) { return $body['token']; } return false; } // Use the token in requests function make_authenticated_request($endpoint, $method = 'GET', $data = null) { $token = get_stored_token(); // If no token or expired, get a new one if (!$token) { $token = get_jwt_token('api_user', 'secure_password'); store_token($token); } $args = [ 'method' => $method, 'headers' => [ 'Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json' ] ]; if ($data && ($method === 'POST' || $method === 'PUT')) { $args['body'] = json_encode($data); } return wp_remote_request($endpoint, $args); }
Handling Missing or Incorrect API Parameters
The WPResidence API has thorough parameter validation. Here’s how to address parameter issues:
- Understand Required vs. Optional Parameters
- Review endpoint documentation for required fields
- Check validation logic in
validate_callback
functions
- Implement Client-Side Validation
- Validate data before sending to the API
- Match the validation rules used by the API
- Proper Error Handling
function update_property($property_id, $data) { // Validate required fields $required_fields = ['title', 'property_price']; foreach ($required_fields as $field) { if (!isset($data[$field])) { return new WP_Error('missing_field', "Missing required field: {$field}"); } } // Make API request $response = wp_remote_request( "https://yourdomain.com/wp-json/wpresidence/v1/property/edit/{$property_id}", [ 'method' => 'PUT', 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . get_jwt_token() ], 'body' => json_encode($data) ] ); // Process response if (is_wp_error($response)) { return $response; } $status_code = wp_remote_retrieve_response_code($response); if ($status_code !== 200) { $body = json_decode(wp_remote_retrieve_body($response), true); return new WP_Error( 'api_error', $body['message'] ?? 'Unknown API error', ['status' => $status_code] ); } return json_decode(wp_remote_retrieve_body($response), true); }
- Handle Complex Parameters
- For nested properties, ensure proper structure
- Pay attention to array formats in taxonomy and meta queries
Checking WordPress & WPResidence Logs for API Errors
WordPress Debug Logging
Enable comprehensive logging in wp-config.php
:
// Enable debugging
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
// For API-specific debugging
define('SAVEQUERIES', true);
Custom API Request Logging
Implement a custom logging function for API interactions:
function log_api_request($endpoint, $method, $request_data, $response) {
if (!file_exists(WP_CONTENT_DIR . '/api-logs')) {
mkdir(WP_CONTENT_DIR . '/api-logs', 0755, true);
}
$log_file = WP_CONTENT_DIR . '/api-logs/api-' . date('Y-m-d') . '.log';
$log_data = [
'time' => current_time('mysql'),
'endpoint' => $endpoint,
'method' => $method,
'request' => $request_data,
'response_code' => is_wp_error($response) ? 'WP_Error' : wp_remote_retrieve_response_code($response),
'response_body' => is_wp_error($response) ? $response->get_error_message() : wp_remote_retrieve_body($response)
];
error_log(json_encode($log_data, JSON_PRETTY_PRINT) . "\n\n", 3, $log_file);
}
// Usage
$response = wp_remote_post(/* ... */);
log_api_request('/wpresidence/v1/property/add', 'POST', $request_data, $response);
Interpreting Log Entries
Common patterns in WPResidence API errors:
- Permission issues: Look for
rest_forbidden
errors with permission check details - Data validation: Note field names mentioned in
rest_missing_field
orrest_invalid_param
errors - Entity not found: Check IDs in
rest_[entity]_not_found
errors - PHP errors: Review stack traces for code issues in the API implementation
Best Practices for Testing API Calls
1. Use a Systematic Testing Approach
- Test in isolated environments first (development/staging)
- Start with minimal requests before adding complexity
- Test the “happy path” before edge cases
- Use consistent test data for reproducible results
2. Leverage API Testing Tools
- Postman: Create collections for all endpoints
- REST API Log plugin: Record all WordPress REST API activity
- Swagger/OpenAPI: Document and test endpoints systematically
3. Create a Test Suite
function test_create_agent() {
$test_data = [
'first_name' => 'Test',
'last_name' => 'Agent',
'agent_email' => 'test.agent.' . time() . '@example.com',
'agent_phone' => '555-1234',
'agent_position' => 'Test Position'
];
$response = wp_remote_post(
'https://yourdomain.com/wp-json/wpresidence/v1/agent/add',
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . get_test_token()
],
'body' => json_encode($test_data)
]
);
if (is_wp_error($response)) {
echo "Test failed: " . $response->get_error_message();
return false;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
$status_code = wp_remote_retrieve_response_code($response);
if ($status_code !== 200 || $body['status'] !== 'success') {
echo "Test failed: Unexpected response (" . $status_code . ")";
var_dump($body);
return false;
}
echo "Test passed: Agent created with ID " . $body['agent_id'];
return $body['agent_id']; // Return ID for cleanup or further tests
}
// Run all tests
function run_api_tests() {
// Create test data
$agent_id = test_create_agent();
if ($agent_id) {
// Test get agent
test_get_agent($agent_id);
// Test update agent
test_update_agent($agent_id);
// Test delete agent
test_delete_agent($agent_id);
}
}
4. Implement Error Handling and Retry Logic
function make_api_request_with_retry($url, $args, $max_retries = 3) {
$attempt = 0;
while ($attempt < $max_retries) {
$response = wp_remote_request($url, $args);
if (!is_wp_error($response)) {
$status_code = wp_remote_retrieve_response_code($response);
// Success or non-retryable error
if ($status_code < 500) {
return $response;
}
}
// Exponential backoff
$delay = pow(2, $attempt) * 500; // 500ms, 1s, 2s
usleep($delay * 1000);
$attempt++;
}
return $response; // Return the last response
}
5. Clean Test Data
Always implement cleanup procedures to remove test data:
function cleanup_test_data($entity_type, $entity_ids) {
foreach ($entity_ids as $id) {
wp_remote_request(
"https://yourdomain.com/wp-json/wpresidence/v1/{$entity_type}/delete/{$id}",
[
'method' => 'DELETE',
'headers' => [
'Authorization' => 'Bearer ' . get_test_token()
]
]
);
}
}
// Register shutdown function to ensure cleanup happens
register_shutdown_function(function() use ($test_entities) {
cleanup_test_data('agent', $test_entities['agents']);
cleanup_test_data('property', $test_entities['properties']);
});
By implementing these security measures, automation strategies, and troubleshooting practices, developers can create robust
By implementing these security measures, automation strategies, and troubleshooting practices, developers can create robust integrations with WPResidence that are secure, reliable, and maintainable.
Advanced Security Considerations
Endpoint-Specific Authorization
WPResidence implements granular permission checks based on the endpoint and operation. Here’s a breakdown of key authorization patterns:
- Public Access Endpoints
- Property listings (read-only)
- Agency/Agent profiles (read-only)
- Developer information (read-only)
- User-Specific Endpoints
- Message management (sender/recipient only)
- Personal invoices (owner only)
- User profile updates (self only)
- Role-Based Endpoints
- Property management (owners, agents with permissions)
- Agent/Agency management (administrators, agency owners)
- Invoice creation/management (administrators only)
Multi-Environment Security Strategy
For organizations using WPResidence across development, testing, and production environments:
- Environment-Specific Credentials
- Use separate JWT secrets for each environment
- Implement different API users with appropriate permissions
- Environment Isolation
- Prevent cross-environment API calls
- Use environment flags in log entries
- Production Hardening
- Implement stricter rate limits in production
- Enable enhanced logging and monitoring
- Use IP restrictions for sensitive operations
Automated API Integration Patterns
Event-Driven Architecture
Instead of continuous polling, implement an event-driven approach:
// Register custom hooks for entity changes
add_action('save_post_estate_property', function($post_id, $post, $update) {
// Skip auto-saves and revisions
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if ($post->post_status === 'auto-draft') return;
// Trigger integration event
do_action('wpresidence_property_updated', $post_id, $update);
}, 10, 3);
// Handle the integration event
add_action('wpresidence_property_updated', function($property_id, $is_update) {
// Get property data
$property = get_post($property_id);
$property_data = build_property_data_array($property_id);
// Send to external system
$external_system = new ExternalSystemConnector();
if ($is_update) {
$external_system->updateProperty($property_id, $property_data);
} else {
$external_system->createProperty($property_data);
}
}, 10, 2);
Bulk Data Synchronization
For large-scale initial data loads or periodic full syncs:
function bulk_sync_agents_to_external_crm($batch_size = 50) {
// Get all agents
$args = [
'post_type' => 'estate_agent',
'posts_per_page' => -1,
'fields' => 'ids', // Only get IDs for efficiency
'post_status' => 'publish',
];
$agent_ids = get_posts($args);
$total_agents = count($agent_ids);
$batches = array_chunk($agent_ids, $batch_size);
$results = [
'total' => $total_agents,
'processed' => 0,
'successful' => 0,
'failed' => 0,
'errors' => []
];
// Process in batches
foreach ($batches as $batch) {
// Get full agent data for this batch
$agent_data = [];
foreach ($batch as $agent_id) {
$agent_data[] = build_agent_data_array($agent_id);
}
// Send batch to external system
$response = wp_remote_post(
'https://external-crm.example.com/api/agents/batch',
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . get_external_system_token()
],
'body' => json_encode([
'agents' => $agent_data,
'operation' => 'upsert' // Create or update
]),
'timeout' => 60 // Longer timeout for batch operations
]
);
// Process batch results
if (!is_wp_error($response)) {
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['results'])) {
foreach ($body['results'] as $result) {
$results['processed']++;
if ($result['success']) {
$results['successful']++;
} else {
$results['failed']++;
$results['errors'][] = [
'agent_id' => $result['agent_id'],
'error' => $result['error']
];
}
}
}
} else {
// Handle batch-level error
$results['failed'] += count($batch);
$results['errors'][] = [
'batch' => true,
'error' => $response->get_error_message()
];
}
}
return $results;
}
Agent-Specific Data Synchronization
function sync_agent_specific_data() {
// Get current user
$current_user = wp_get_current_user();
// Check if user is an agent
$agent_id = get_user_meta($current_user->ID, 'user_agent_id', true);
if (!$agent_id) {
return new WP_Error('not_an_agent', 'Current user is not associated with an agent profile');
}
// Get agent's properties
$properties = get_posts([
'post_type' => 'estate_property',
'posts_per_page' => -1,
'meta_key' => 'property_agent',
'meta_value' => $agent_id
]);
// Get agent's messages
$messages = get_agent_messages($agent_id);
// Get agent's sales/revenue data
$sales_data = get_agent_sales_data($agent_id);
// Sync to agent dashboard/mobile app
return [
'agent' => get_post($agent_id),
'properties' => $properties,
'messages' => $messages,
'sales' => $sales_data
];
}
Advanced Troubleshooting Techniques
API Request/Response Logging Middleware
Implement a middleware approach to log all API interactions:
class WPR_API_Logger {
private static $instance = null;
private $log_path;
private function __construct() {
$this->log_path = WP_CONTENT_DIR . '/logs/api-requests/';
if (!file_exists($this->log_path)) {
wp_mkdir_p($this->log_path);
}
}
public static function get_instance() {
if (self::$instance == null) {
self::$instance = new WPR_API_Logger();
}
return self::$instance;
}
public function log_request($request) {
$data = [
'time' => current_time('mysql'),
'url' => $request->get_route(),
'method' => $request->get_method(),
'headers' => $request->get_headers(),
'params' => $request->get_params(),
'user_id' => get_current_user_id()
];
$filename = date('Y-m-d') . '-requests.log';
file_put_contents(
$this->log_path . $filename,
json_encode($data) . "\n",
FILE_APPEND
);
return $request; // Pass through unchanged
}
public function log_response($response, $handler, $request) {
$data = [
'time' => current_time('mysql'),
'url' => $request->get_route(),
'method' => $request->get_method(),
'status' => $response->get_status(),
'response' => $response->get_data(),
'user_id' => get_current_user_id()
];
$filename = date('Y-m-d') . '-responses.log';
file_put_contents(
$this->log_path . $filename,
json_encode($data) . "\n",
FILE_APPEND
);
return $response; // Pass through unchanged
}
}
// Hook into REST API
add_filter('rest_request_before_callbacks', [WPR_API_Logger::get_instance(), 'log_request'], 999);
add_filter('rest_request_after_callbacks', [WPR_API_Logger::get_instance(), 'log_response'], 999, 3);
Diagnostic Tools
Create targeted diagnostic endpoints for troubleshooting:
// Only available in development environments
if (defined('WP_ENVIRONMENT_TYPE') && WP_ENVIRONMENT_TYPE !== 'production') {
add_action('rest_api_init', function() {
register_rest_route('wpresidence-dev/v1', '/diagnostics', [
'methods' => 'GET',
'callback' => 'wpresidence_api_diagnostics',
'permission_callback' => function() {
return current_user_can('manage_options');
}
]);
});
}
function wpresidence_api_diagnostics() {
$diagnostics = [
'php_version' => phpversion(),
'wordpress_version' => get_bloginfo('version'),
'wpresidence_version' => WPRESIDENCE_VERSION,
'active_plugins' => get_option('active_plugins'),
'registered_routes' => rest_get_server()->get_routes(),
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'upload_max_filesize' => ini_get('upload_max_filesize'),
'post_max_size' => ini_get('post_max_size'),
'server_software' => $_SERVER['SERVER_SOFTWARE'],
'database_version' => $GLOBALS['wpdb']->db_version(),
'timezone' => wp_timezone_string(),
'locale' => get_locale()
];
return new WP_REST_Response($diagnostics, 200);
}
Creating a Comprehensive API Test Suite
class WPResidence_API_Tester {
private $base_url;
private $token;
private $test_entities = [
'agents' => [],
'properties' => [],
'agencies' => [],
'messages' => [],
'invoices' => []
];
public function __construct($base_url, $username, $password) {
$this->base_url = $base_url;
$this->token = $this->get_token($username, $password);
}
private function get_token($username, $password) {
$response = wp_remote_post($this->base_url . '/jwt-auth/v1/token', [
'body' => [
'username' => $username,
'password' => $password
]
]);
if (is_wp_error($response)) {
throw new Exception('Could not get token: ' . $response->get_error_message());
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (empty($body['token'])) {
throw new Exception('Invalid token response: ' . print_r($body, true));
}
return $body['token'];
}
private function request($endpoint, $method = 'GET', $data = null) {
$url = $this->base_url . '/wpresidence/v1/' . $endpoint;
$args = [
'method' => $method,
'headers' => [
'Authorization' => 'Bearer ' . $this->token,
'Content-Type' => 'application/json'
]
];
if ($data && ($method === 'POST' || $method === 'PUT')) {
$args['body'] = json_encode($data);
}
$response = wp_remote_request($url, $args);
if (is_wp_error($response)) {
throw new Exception('API request failed: ' . $response->get_error_message());
}
return [
'status' => wp_remote_retrieve_response_code($response),
'body' => json_decode(wp_remote_retrieve_body($response), true)
];
}
// Test functions for each entity type
public function test_agent_lifecycle() {
// 1. Create an agent
$agent_data = [
'first_name' => 'Test',
'last_name' => 'Agent',
'agent_email' => 'test.agent.' . time() . '@example.com',
'agent_phone' => '555-' . rand(1000, 9999)
];
$response = $this->request('agent/add', 'POST', $agent_data);
if ($response['status'] !== 200 || $response['body']['status'] !== 'success') {
throw new Exception('Failed to create agent: ' . print_r($response, true));
}
$agent_id = $response['body']['agent_id'];
$this->test_entities['agents'][] = $agent_id;
// 2. Get the agent
$response = $this->request('agent/' . $agent_id, 'GET');
if ($response['status'] !== 200) {
throw new Exception('Failed to get agent: ' . print_r($response, true));
}
// 3. Update the agent
$update_data = [
'first_name' => 'Updated',
'last_name' => 'Agent',
'agent_phone' => '555-' . rand(1000, 9999)
];
$response = $this->request('agent/edit/' . $agent_id, 'PUT', $update_data);
if ($response['status'] !== 200 || $response['body']['status'] !== 'success') {
throw new Exception('Failed to update agent: ' . print_r($response, true));
}
// 4. Delete the agent
$response = $this->request('agent/delete/' . $agent_id, 'DELETE');
if ($response['status'] !== 200 || $response['body']['status'] !== 'success') {
throw new Exception('Failed to delete agent: ' . print_r($response, true));
}
// Remove from cleanup list
$index = array_search($agent_id, $this->test_entities['agents']);
if ($index !== false) {
unset($this->test_entities['agents'][$index]);
}
return 'Agent lifecycle test successful';
}
// Similar functions for other entities
// Run all tests
public function run_all_tests() {
try {
$results = [
'agent_lifecycle' => $this->test_agent_lifecycle(),
// Other tests...
];
return [
'status' => 'success',
'results' => $results
];
} catch (Exception $e) {
return [
'status' => 'error',
'message' => $e->getMessage()
];
} finally {
// Cleanup any remaining test entities
$this->cleanup_test_entities();
}
}
// Cleanup function
private function cleanup_test_entities() {
foreach ($this->test_entities as $entity_type => $ids) {
foreach ($ids as $id) {
try {
$endpoint = rtrim($entity_type, 's'); // Convert plural to singular
$this->request($endpoint . '/delete/' . $id, 'DELETE');
} catch (Exception $e) {
// Log cleanup errors but continue
error_log('Cleanup error: ' . $e->getMessage());
}
}
}
}
}
// Usage
$tester = new WPResidence_API_Tester(
'https://yourdomain.com/wp-json',
'api_test_user',
'secure_test_password'
);
$results = $tester->run_all_tests();
Conclusion
The WPResidence real estate API provides powerful capabilities for managing real estate data but requires careful attention to security and proper implementation practices. Developers can build secure and reliable integrations by following the authentication requirements, understanding permission models, and implementing robust error handling.
For automated workflows, the event-driven approach provides the most efficient and maintainable solution, while comprehensive testing ensures that integrations remain functional as both WPResidence and external systems evolve.
When troubleshooting issues, systematic analysis of request/response patterns and proper logging will help identify and resolve problems quickly. The diagnostic tools and testing framework demonstrated here provide a foundation for ongoing API maintenance and monitoring.
By leveraging these advanced techniques, developers can create sophisticated real estate platforms that seamlessly connect WPResidence with external systems while maintaining security and data integrity.
Official documentation is here : https://www.postman.com/universal-eclipse-339362/wpresidence/overview