# SearchJet Plugin API Documentation

## Overview

SearchJet is an AI-powered instant search plugin for WordPress and WooCommerce that provides blazing-fast, typo-tolerant search functionality with real-time analytics, automation capabilities, and AI agents.

**Version:** 1.1.9  
**Namespace:** `SearchJet`  
**REST API Base:** `/wp-json/searchjet/v1/`

## Table of Contents

1. [Authentication](#authentication)
2. [Core REST API Endpoints](#core-rest-api-endpoints)
3. [AI Agents API](#ai-agents-api)
4. [Automations API](#automations-api)
5. [Analytics API](#analytics-api)
6. [Events API](#events-api)
7. [Index Management API](#index-management-api)
8. [WordPress Hooks & Filters](#wordpress-hooks--filters)
9. [JavaScript API](#javascript-api)
10. [Configuration Options](#configuration-options)
11. [Error Handling](#error-handling)
12. [Security Considerations](#security-considerations)

## Authentication

### API Key Authentication
SearchJet uses Bearer token authentication for external API calls:

```php
$headers = [
    'Authorization' => 'Bearer ' . get_option('searchjet_hosted_api_key'),
    'Content-Type' => 'application/json',
    'Accept' => 'application/json'
];
```

### WordPress Permissions
Most admin endpoints require `manage_options` capability:

```php
public static function can_manage(): bool {
    return current_user_can('manage_options');
}
```

## Core REST API Endpoints

### Health Check
**Endpoint:** `GET /wp-json/searchjet/v1/ping`  
**Permission:** Public  
**Description:** Simple health check endpoint

**Response:**
```json
{
    "ok": true,
    "time": 1640995200,
    "version": "1.1.9",
    "routes": [
        "/ping",
        "/automations",
        "/automations/definitions",
        "/automations/settings",
        "/automations/run-daily",
        "/automations/test-dispatch",
        "/ai-agents"
    ]
}
```

## AI Agents API

### Get AI Agents
**Endpoint:** `GET /wp-json/searchjet/v1/ai-agents`  
**Permission:** `manage_options`  
**Description:** Retrieve all configured AI agents

**Response:**
```json
{
    "agent_id": {
        "name": "Agent Name",
        "description": "Agent description",
        "action": "agent_action",
        "enabled": true
    }
}
```

### Save AI Agents
**Endpoint:** `POST /wp-json/searchjet/v1/ai-agents`  
**Permission:** `manage_options`  
**Description:** Save AI agent configurations

**Request Body:**
```json
{
    "agents": {
        "agent_id": {
            "name": "Agent Name",
            "description": "Agent description",
            "enabled": true
        }
    }
}
```

**Response:**
```json
{
    "success": true
}
```

## Automations API

### Get Automation Status
**Endpoint:** `GET /wp-json/searchjet/v1/automations`  
**Permission:** `manage_options`  
**Description:** Get current automation status and configuration

**Response:**
```json
{
    "enabled": true,
    "filters": ["zero_result_burst", "daily_health"],
    "has_webhook": true,
    "has_zapier": false,
    "scheduled": true,
    "next_run": 1640995200
}
```

### Get Automation Definitions
**Endpoint:** `GET /wp-json/searchjet/v1/automations/definitions`  
**Permission:** `manage_options`  
**Description:** Get available automation definitions

**Response:**
```json
{
    "definitions": [
        {
            "id": "zero_result_burst",
            "name": "Zero Result Burst Detection",
            "description": "Detects when multiple zero-result searches occur",
            "triggers": ["search.zero_result"]
        }
    ]
}
```

### Save Automation Settings
**Endpoint:** `POST /wp-json/searchjet/v1/automations/settings`  
**Permission:** `manage_options`  
**Description:** Update automation settings

**Request Body:**
```json
{
    "enabled": true,
    "filters": ["zero_result_burst"],
    "webhook_url": "https://example.com/webhook",
    "webhook_secret": "secret_key",
    "zapier_webhook_url": "https://hooks.zapier.com/...",
    "zapier_secret": "zapier_secret"
}
```

**Response:**
```json
{
    "ok": true,
    "changed": {
        "enabled": true,
        "webhook_url": "https://example.com/webhook"
    }
}
```

### Run Daily Automations
**Endpoint:** `POST /wp-json/searchjet/v1/automations/run-daily`  
**Permission:** `manage_options`  
**Description:** Manually trigger daily automation run

**Response:**
```json
{
    "ok": true
}
```

### Test Automation Dispatch
**Endpoint:** `POST /wp-json/searchjet/v1/automations/test-dispatch`  
**Permission:** `manage_options`  
**Description:** Test automation webhook dispatch

**Response:**
```json
{
    "ok": true,
    "results": {
        "n8n": {
            "success": true,
            "response_code": 200
        },
        "zapier": {
            "success": true,
            "response_code": 200
        }
    }
}
```

## Analytics API

### Log Search Analytics
**Endpoint:** `POST /wp-json/searchjet/v1/log`  
**Permission:** Public  
**Description:** Log search analytics data

**Request Body:**
```json
[
    {
        "query": "search term",
        "results": 5,
        "ts": 1640995200,
        "filters": {"category": "products"},
        "referer": "https://example.com/page",
        "user_agent": "Mozilla/5.0...",
        "session_id": "session_123",
        "ip": "192.168.1.1"
    }
]
```

**Response:**
```json
{
    "success": true,
    "count": 1
}
```

## Events API

### Record Events
**Endpoint:** `POST /wp-json/searchjet/v1/event`  
**Permission:** Configurable (public by default)  
**Description:** Record search events for automation triggers

**Request Body:**
```json
{
    "type": "zero_result",
    "q": "search query",
    "total": 0
}
```

**Response:**
```json
{
    "ok": true
}
```

**Supported Event Types:**
- `zero_result`: Zero search results event

## Index Management API

### Remote Client Methods

The `SearchJetRemoteClient` class provides methods for index management:

#### Fetch Client Info
```php
$client_info = SearchJetRemoteClient::fetchClientInfo($api_key);
```

#### Index Document
```php
$result = SearchJetRemoteClient::indexDocument([
    'documents' => [$document_data],
    'full_reindex' => false
]);
```

#### Delete Document
```php
$result = SearchJetRemoteClient::deleteDocument([
    'id' => $post_id
]);
```

#### Fetch Document Count
```php
$count = SearchJetRemoteClient::fetchDocumentCount();
```

#### Fetch Search Analytics
```php
$analytics = SearchJetRemoteClient::fetchSearchAnalytics();
```

## WordPress Hooks & Filters

### Actions

#### Plugin Initialization
```php
// Plugin activation
do_action('searchjet_activate');

// Plugin deactivation  
do_action('searchjet_deactivate');

// Index completion
do_action('searchjet_index_completed', $summary);

// Zero result search
do_action('searchjet_zero_result', $query, $total);

// Manual automation dispatch
do_action('searchjet_dispatch_automation', $event, $payload);
```

#### Post Indexing
```php
// Triggered on post save/update
add_action('save_post', [IndexSync::class, 'handleIndex'], 10, 3);

// Triggered on post deletion
add_action('delete_post', [IndexSync::class, 'handleDelete']);
```

### Filters

#### Indexable Content
```php
// Filter indexable post types
$post_types = apply_filters('searchjet_indexable_post_types', $post_types);

// Filter indexable post statuses
$statuses = apply_filters('searchjet_indexable_post_statuses', $statuses);

// Filter document before indexing
$document = apply_filters('searchjet_index_document', $document, $post);
```

#### Admin Configuration
```php
// Filter admin parent menu slug
$parent_slug = apply_filters('searchjet/admin_parent_slug', 'searchjet');
```

## JavaScript API

### SearchJet Settings Object
The plugin exposes a global `SearchJetSettings` object:

```javascript
window.SearchJetSettings = {
    logApiUrl: '/wp-json/searchjet/v1/log',
    eventsEndpoint: '/wp-json/searchjet/v1/event',
    restNonce: 'wp_rest_nonce',
    requireEventNonce: 0,
    searchApiBaseUrl: 'https://api.searchjet.com/api/v1/search',
    key: 'public_key',
    attributes: ['title', 'content', 'price'],
    language: 'en',  // Current site language (ISO 639-1). Empty string if no multilingual plugin.
    ui: {
        showThumbnail: true,
        showPrice: true,
        showRating: true,
        showStock: true,
        showCategory: true,
        highlightTerms: true,
        viewAllLink: true
    },
    integrations: {
        ga4: false
    }
};
```

### Frontend Search Functions
```javascript
// Perform search
searchJet.search(query, options);

// Log search event
searchJet.logSearch(query, results, metadata);

// Record zero result event
searchJet.recordZeroResult(query);
```

## Configuration Options

### Core Settings
- `searchjet_api_key`: API key for authentication
- `searchjet_project_id`: Project identifier
- `searchjet_hosted_api_key`: Hosted API key
- `searchjet_search_mode`: Search mode ('js' or 'php')
- `searchjet_disable_search_mode`: Disable search override

### Multilingual / WPML Support
SearchJet automatically detects and indexes the `language` field for every document. This works with WPML, Polylang, or any multilingual setup.

**How it works:**
- Every indexed document includes a `language` field (ISO 639-1 code, e.g. `en`, `fr`, `cs`)
- The `language` field is filterable via the Search API
- When a user visits `?lang=fr`, the JS automatically passes `filters=language = "fr"` to the search API
- Without a multilingual plugin, the field defaults to the site locale (e.g. `en`)

**Filtering by language via API:**
```
GET /v1/search?q=hello&filters=language = "en"
GET /v1/search?q=hello&filters=language = "fr" AND type = "post"
```

**Indexed document example:**
```json
{
    "id": "post-123",
    "title": "Hello World",
    "content": "Welcome to our site...",
    "type": "post",
    "language": "en",
    "url": "https://example.com/hello-world"
}
```

### UI Options
- `searchjet_ui_options`: Array of UI configuration
  - `show_thumbnail`: Show product thumbnails
  - `show_price`: Show product prices
  - `show_rating`: Show product ratings
  - `show_stock`: Show stock status
  - `show_category`: Show categories
  - `highlight_term`: Highlight search terms
  - `view_all_link`: Show "view all" link
  - `font_size`: Font size in pixels
  - `title_color`: Title color
  - `highlight_color`: Highlight color
  - `background_color`: Background color
  - `border_radius`: Border radius in pixels
  - `image_size`: Image size in pixels

### Automation Settings
- `searchjet_automations_enabled`: Enable/disable automations
- `searchjet_automation_filters`: Array of enabled automation filters
- `searchjet_webhook_url`: Webhook URL for automations
- `searchjet_webhook_secret`: Webhook secret key
- `searchjet_zapier_webhook_url`: Zapier webhook URL
- `searchjet_zapier_secret`: Zapier secret key
- `searchjet_require_event_nonce`: Require nonce for events
- `searchjet_zr_threshold`: Zero result threshold
- `searchjet_zr_window`: Zero result time window

### Content Exclusion
- `searchjet_exclude_post_types`: Excluded post types
- `searchjet_exclude_sticky`: Exclude sticky posts
- `searchjet_exclude_protected`: Exclude password-protected posts
- `searchjet_exclude_mime_types`: Excluded MIME types
- `searchjet_include_status`: Included post statuses

## Error Handling

### API Error Responses
```json
{
    "code": "searchjet_error_code",
    "message": "Error description",
    "data": {
        "status": 400
    }
}
```

### Common Error Codes
- `searchjet_invalid_agents`: Invalid agent data
- `searchjet_missing_permission`: Insufficient permissions
- `searchjet_api_error`: External API error
- `searchjet_invalid_request`: Invalid request format

### PHP Exception Handling
```php
try {
    $result = $client->indexPost($post);
} catch (Exception $e) {
    error_log('[SearchJet] Error: ' . $e->getMessage());
}
```

## Security Considerations

### Input Sanitization
All user inputs are sanitized using WordPress functions:
```php
$query = sanitize_text_field($_POST['query']);
$filters = array_map('sanitize_key', $_POST['filters']);
```

### Nonce Verification
Admin actions require nonce verification:
```php
check_admin_referer('searchjet_action_nonce');
```

### Rate Limiting
Events API includes IP-based rate limiting:
```php
$rate_limit = get_transient('sj_evt_rl_' . md5($ip . '|' . $type));
if ($rate_limit > 240) {
    return new WP_REST_Response(['error' => 'rate_limited'], 429);
}
```

### Capability Checks
Admin endpoints verify user capabilities:
```php
if (!current_user_can('manage_options')) {
    wp_die('Insufficient permissions');
}
```

## Integration Examples

### Custom AI Agent
```php
// Register custom agent
add_filter('searchjet_ai_agents', function($agents) {
    $agents['custom_agent'] = [
        'name' => 'Custom Agent',
        'description' => 'Custom functionality',
        'action' => 'custom_action',
        'enabled' => true
    ];
    return $agents;
});
```

### Custom Automation
```php
// Register custom automation
add_filter('searchjet_automations', function($automations) {
    $automations['custom_automation'] = [
        'name' => 'Custom Automation',
        'description' => 'Custom automation logic',
        'triggers' => ['custom_event']
    ];
    return $automations;
});
```

### Frontend Integration
```javascript
// Custom search handler
document.addEventListener('DOMContentLoaded', function() {
    const searchInput = document.querySelector('#search-input');
    searchInput.addEventListener('input', function(e) {
        const query = e.target.value;
        if (query.length > 2) {
            searchJet.search(query, {
                limit: 10,
                attributes: ['title', 'content', 'price']
            });
        }
    });
});
```

### WPML Language Filtering
The plugin automatically reads `?lang=xx` from the URL and passes it as a filter to the search API. For custom implementations:

```javascript
// Get current language from URL
const lang = new URLSearchParams(window.location.search).get('lang') || '';

// Pass language filter to search API
fetch(`${searchApiUrl}?q=${query}&filters=language:${encodeURIComponent('"' + lang + '"')}`, {
    headers: { 'Authorization': `Bearer ${apiKey}` }
});
```

The `language` field is always indexed. With WPML, it contains the post's language code (e.g. `en`, `fr`). Without WPML, it defaults to the site locale prefix (e.g. `en` from `en_US`).

## Support and Resources

- **Plugin URI:** https://searchjetengine.com/
- **Documentation:** https://searchjetengine.com/docs
- **Live Demo:** https://demo.searchjetengine.com/
- **Support:** Contact through WordPress.org plugin page

---

*This documentation covers SearchJet Plugin v1.1.9. For the latest updates and features, please refer to the official documentation.*