PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.24
MxChat – AI Chatbot & Content Generation for WordPress v3.2.24
3.2.25 3.2.24 3.2.23 3.2.22 3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 All 156 releases
mxchat-basic / includes / admin-api-page.php

admin-api-page.php in MxChat – AI Chatbot & Content Generation for WordPress 3.2.24, at includes/admin-api-page.php

601 lines 35.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MxChat → API Access admin page.
4 *
5 * Lets the site owner generate, view, and revoke the bearer token used by
6 * the REST endpoints in class-rest-api.php. The token is stored in
7 * wp_options under `mxchat_api_token` and is empty by default — until the
8 * owner clicks "Generate Token", all REST endpoints refuse with 401.
9 *
10 * @package MxChat
11 * @since 3.2.5
12 */
13
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 if (!defined('MXCHAT_API_TOKEN_OPTION')) {
19 define('MXCHAT_API_TOKEN_OPTION', 'mxchat_api_token');
20 }
21
22 /**
23 * Register the submenu under the existing MxChat menu.
24 * Priority 20 so it lands after the menu is already set up.
25 */
26 add_action('admin_menu', 'mxchat_register_api_admin_page', 20);
27 function mxchat_register_api_admin_page() {
28 add_submenu_page(
29 'mxchat-max',
30 esc_html__('MxChat API Access', 'mxchat'),
31 esc_html__('API Access', 'mxchat'),
32 'manage_options',
33 'mxchat-api-access',
34 'mxchat_render_api_admin_page'
35 );
36 }
37
38 /**
39 * Generate a new token and store it.
40 */
41 add_action('admin_post_mxchat_rotate_api_token', 'mxchat_handle_rotate_api_token');
42 function mxchat_handle_rotate_api_token() {
43 if (!current_user_can('manage_options')) {
44 wp_die(esc_html__('You do not have permission to do this.', 'mxchat'));
45 }
46 check_admin_referer('mxchat_rotate_api_token');
47
48 $token = wp_generate_password(48, false, false);
49 update_option(MXCHAT_API_TOKEN_OPTION, $token, false);
50 update_option('mxchat_api_token_rotated_at', time(), false);
51
52 set_transient('mxchat_api_token_just_rotated', $token, 60);
53 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-api-access', 'rotated' => 1), admin_url('admin.php')));
54 exit;
55 }
56
57 /**
58 * Revoke the existing token (sets the option to empty string).
59 */
60 add_action('admin_post_mxchat_revoke_api_token', 'mxchat_handle_revoke_api_token');
61 function mxchat_handle_revoke_api_token() {
62 if (!current_user_can('manage_options')) {
63 wp_die(esc_html__('You do not have permission to do this.', 'mxchat'));
64 }
65 check_admin_referer('mxchat_revoke_api_token');
66
67 update_option(MXCHAT_API_TOKEN_OPTION, '', false);
68 delete_option('mxchat_api_token_rotated_at');
69
70 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-api-access', 'revoked' => 1), admin_url('admin.php')));
71 exit;
72 }
73
74 /**
75 * Render the API Access admin page.
76 */
77 function mxchat_render_api_admin_page() {
78 if (!current_user_can('manage_options')) {
79 wp_die(esc_html__('You do not have permission to access this page.', 'mxchat'));
80 }
81
82 $stored_token = (string) get_option(MXCHAT_API_TOKEN_OPTION, '');
83 $token_set = $stored_token !== '';
84 $just_rotated = get_transient('mxchat_api_token_just_rotated');
85 delete_transient('mxchat_api_token_just_rotated');
86 $rotated_query = isset($_GET['rotated']) ? (bool) $_GET['rotated'] : false;
87 $revoked_query = isset($_GET['revoked']) ? (bool) $_GET['revoked'] : false;
88 $rotated_at = (int) get_option('mxchat_api_token_rotated_at', 0);
89
90 $base_url = trailingslashit(get_rest_url(null, 'mxchat/v1'));
91 $health_url = $base_url . 'health';
92 $transcripts_u = $base_url . 'transcripts';
93 $knowledge_url = $base_url . 'knowledge';
94 $leads_url = $base_url . 'leads';
95
96 $plugin_url = plugin_dir_url(dirname(__FILE__));
97 $masked = $token_set
98 ? substr($stored_token, 0, 4) . str_repeat('•', 24) . substr($stored_token, -4)
99 : '';
100 $rotated_human = $rotated_at > 0
101 ? sprintf(
102 /* translators: %s: human-readable elapsed time */
103 esc_html__('%s ago', 'mxchat'),
104 human_time_diff($rotated_at, current_time('timestamp'))
105 )
106 : esc_html__('Never', 'mxchat');
107 ?>
108 <div class="mxch-admin-wrapper mxch-api-wrapper">
109
110 <!-- Mobile Header -->
111 <header class="mxch-mobile-header">
112 <a href="#" class="mxch-mobile-logo">
113 <div class="mxch-mobile-logo-icon">
114 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
115 </div>
116 <span class="mxch-mobile-logo-text">MxChat</span>
117 </a>
118 <button type="button" class="mxch-mobile-menu-btn" aria-label="<?php esc_attr_e('Open menu', 'mxchat'); ?>">
119 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
120 </button>
121 </header>
122
123 <!-- Mobile Menu Overlay -->
124 <div class="mxch-mobile-overlay"></div>
125
126 <!-- Mobile Menu Modal -->
127 <div class="mxch-mobile-menu">
128 <div class="mxch-mobile-menu-header">
129 <span class="mxch-mobile-menu-title"><?php esc_html_e('API Access', 'mxchat'); ?></span>
130 <button type="button" class="mxch-mobile-menu-close" aria-label="<?php esc_attr_e('Close menu', 'mxchat'); ?>">
131 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
132 </button>
133 </div>
134 <nav class="mxch-mobile-menu-nav">
135 <div class="mxch-mobile-nav-section">
136 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('REST API', 'mxchat'); ?></div>
137 <button class="mxch-mobile-nav-link active" data-target="api-token">
138 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
139 <span><?php esc_html_e('Token', 'mxchat'); ?></span>
140 </button>
141 <button class="mxch-mobile-nav-link" data-target="api-endpoints">
142 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
143 <span><?php esc_html_e('Endpoints', 'mxchat'); ?></span>
144 </button>
145 <button class="mxch-mobile-nav-link" data-target="api-examples">
146 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
147 <span><?php esc_html_e('Examples', 'mxchat'); ?></span>
148 </button>
149 <button class="mxch-mobile-nav-link" data-target="api-privacy">
150 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
151 <span><?php esc_html_e('Privacy', 'mxchat'); ?></span>
152 </button>
153 </div>
154 </nav>
155 </div>
156
157 <!-- Sidebar Navigation -->
158 <aside class="mxch-sidebar">
159 <div class="mxch-sidebar-header">
160 <a href="#" class="mxch-sidebar-logo">
161 <div class="mxch-sidebar-logo-icon">
162 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
163 </div>
164 <span class="mxch-sidebar-logo-text">MxChat</span>
165 <span class="mxch-sidebar-version">v<?php echo esc_html(MXCHAT_VERSION ?? '2.7.0'); ?></span>
166 </a>
167 </div>
168
169 <nav class="mxch-sidebar-nav">
170 <div class="mxch-nav-section">
171 <div class="mxch-nav-section-title"><?php esc_html_e('REST API', 'mxchat'); ?></div>
172
173 <div class="mxch-nav-item" data-section="api-token">
174 <button class="mxch-nav-link active" data-target="api-token">
175 <span class="mxch-nav-link-icon">
176 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
177 </span>
178 <span class="mxch-nav-link-text"><?php esc_html_e('Token', 'mxchat'); ?></span>
179 <?php if ($token_set): ?>
180 <span class="mxch-nav-link-badge mxch-active-badge"><?php esc_html_e('Active', 'mxchat'); ?></span>
181 <?php endif; ?>
182 </button>
183 </div>
184
185 <div class="mxch-nav-item" data-section="api-endpoints">
186 <button class="mxch-nav-link" data-target="api-endpoints">
187 <span class="mxch-nav-link-icon">
188 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
189 </span>
190 <span class="mxch-nav-link-text"><?php esc_html_e('Endpoints', 'mxchat'); ?></span>
191 </button>
192 </div>
193
194 <div class="mxch-nav-item" data-section="api-examples">
195 <button class="mxch-nav-link" data-target="api-examples">
196 <span class="mxch-nav-link-icon">
197 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
198 </span>
199 <span class="mxch-nav-link-text"><?php esc_html_e('Examples', 'mxchat'); ?></span>
200 </button>
201 </div>
202
203 <div class="mxch-nav-item" data-section="api-privacy">
204 <button class="mxch-nav-link" data-target="api-privacy">
205 <span class="mxch-nav-link-icon">
206 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
207 </span>
208 <span class="mxch-nav-link-text"><?php esc_html_e('Privacy', 'mxchat'); ?></span>
209 </button>
210 </div>
211 </div>
212 </nav>
213 </aside>
214
215 <!-- Main Content Area -->
216 <main class="mxch-content">
217
218 <?php if ($rotated_query && $just_rotated): ?>
219 <div class="notice notice-success is-dismissible mxch-api-flash">
220 <p>
221 <strong><?php esc_html_e('New API token generated.', 'mxchat'); ?></strong>
222 <?php esc_html_e('Copy it now — for security, the full value will not be shown again after you leave this page.', 'mxchat'); ?>
223 </p>
224 <p>
225 <code class="mxch-api-token-reveal" data-mxch-copy-target><?php echo esc_html($just_rotated); ?></code>
226 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm mxch-api-copy-btn" data-mxch-copy="<?php echo esc_attr($just_rotated); ?>">
227 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
228 <span><?php esc_html_e('Copy', 'mxchat'); ?></span>
229 </button>
230 </p>
231 </div>
232 <?php endif; ?>
233
234 <?php if ($revoked_query): ?>
235 <div class="notice notice-warning is-dismissible mxch-api-flash">
236 <p>
237 <strong><?php esc_html_e('API token revoked.', 'mxchat'); ?></strong>
238 <?php esc_html_e('All REST endpoints will return 401 until a new token is generated.', 'mxchat'); ?>
239 </p>
240 </div>
241 <?php endif; ?>
242
243 <!-- Token Section -->
244 <div id="api-token" class="mxch-section active">
245 <div class="mxch-content-header">
246 <h1 class="mxch-content-title"><?php esc_html_e('REST API', 'mxchat'); ?></h1>
247 <p class="mxch-content-subtitle">
248 <?php esc_html_e('Bearer-token-authenticated endpoints for reading transcripts and pushing content into the knowledge base. Disabled until you generate a token below.', 'mxchat'); ?>
249 </p>
250 </div>
251
252 <div class="mxch-card">
253 <div class="mxch-card-header">
254 <h3 class="mxch-card-title">
255 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
256 <?php esc_html_e('Token', 'mxchat'); ?>
257 </h3>
258 <?php if ($token_set): ?>
259 <span class="mxch-status-pill mxch-status-pill-active">
260 <span class="mxch-status-dot"></span>
261 <?php esc_html_e('Active', 'mxchat'); ?>
262 </span>
263 <?php else: ?>
264 <span class="mxch-status-pill mxch-status-pill-disabled">
265 <span class="mxch-status-dot"></span>
266 <?php esc_html_e('Disabled', 'mxchat'); ?>
267 </span>
268 <?php endif; ?>
269 </div>
270 <div class="mxch-card-body">
271 <?php if ($token_set): ?>
272 <div class="mxch-field">
273 <div class="mxch-field-label"><?php esc_html_e('Current token', 'mxchat'); ?></div>
274 <code class="mxch-api-token-masked"><?php echo esc_html($masked); ?></code>
275 <p class="mxch-field-description">
276 <?php esc_html_e('Last rotated:', 'mxchat'); ?>
277 <strong><?php echo esc_html($rotated_human); ?></strong>
278 </p>
279 </div>
280 <?php else: ?>
281 <p class="mxch-field-description">
282 <?php esc_html_e('No token is currently set. Generate one below to enable the authenticated REST endpoints.', 'mxchat'); ?>
283 </p>
284 <?php endif; ?>
285
286 <div class="mxch-api-actions">
287 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="mxch-api-form">
288 <input type="hidden" name="action" value="mxchat_rotate_api_token" />
289 <?php wp_nonce_field('mxchat_rotate_api_token'); ?>
290 <button type="submit" class="mxch-btn mxch-btn-primary">
291 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/><path d="M16 16h5v5"/></svg>
292 <?php echo $token_set ? esc_html__('Regenerate Token', 'mxchat') : esc_html__('Generate Token', 'mxchat'); ?>
293 </button>
294 </form>
295
296 <?php if ($token_set): ?>
297 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="mxch-api-form"
298 onsubmit="return confirm('<?php echo esc_js(__('Revoke the API token? Any external tool using the current token will stop working immediately.', 'mxchat')); ?>');">
299 <input type="hidden" name="action" value="mxchat_revoke_api_token" />
300 <?php wp_nonce_field('mxchat_revoke_api_token'); ?>
301 <button type="submit" class="mxch-btn mxch-btn-danger">
302 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
303 <?php esc_html_e('Revoke Token', 'mxchat'); ?>
304 </button>
305 </form>
306 <?php endif; ?>
307 </div>
308
309 <p class="mxch-field-hint">
310 <?php esc_html_e('Regenerating immediately invalidates the old token. Save the new value somewhere safe (password manager) — only the masked version is shown after you leave this page.', 'mxchat'); ?>
311 </p>
312 </div>
313 </div>
314 </div>
315
316 <!-- Endpoints Section -->
317 <div id="api-endpoints" class="mxch-section">
318 <div class="mxch-content-header">
319 <h1 class="mxch-content-title"><?php esc_html_e('Endpoints', 'mxchat'); ?></h1>
320 <p class="mxch-content-subtitle"><?php esc_html_e('All endpoints live under the WordPress REST namespace mxchat/v1.', 'mxchat'); ?></p>
321 </div>
322
323 <div class="mxch-card">
324 <div class="mxch-card-body">
325 <table class="mxch-api-endpoints-table">
326 <thead>
327 <tr>
328 <th><?php esc_html_e('Method', 'mxchat'); ?></th>
329 <th><?php esc_html_e('URL', 'mxchat'); ?></th>
330 <th><?php esc_html_e('Auth', 'mxchat'); ?></th>
331 <th><?php esc_html_e('Purpose', 'mxchat'); ?></th>
332 </tr>
333 </thead>
334 <tbody>
335 <tr>
336 <td><span class="mxch-api-method mxch-api-method-get">GET</span></td>
337 <td><code><?php echo esc_html($health_url); ?></code></td>
338 <td><span class="mxch-api-auth mxch-api-auth-none"><?php esc_html_e('None', 'mxchat'); ?></span></td>
339 <td><?php esc_html_e('Connectivity check; reports plugin version and whether a token is set.', 'mxchat'); ?></td>
340 </tr>
341 <tr>
342 <td><span class="mxch-api-method mxch-api-method-get">GET</span></td>
343 <td><code><?php echo esc_html($transcripts_u); ?></code></td>
344 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
345 <td><?php esc_html_e('Read chat transcripts. Filterable by since, until, session_id, role, has_rag_context; supports limit + offset pagination.', 'mxchat'); ?></td>
346 </tr>
347 <tr>
348 <td><span class="mxch-api-method mxch-api-method-post">POST</span></td>
349 <td><code><?php echo esc_html($knowledge_url); ?></code></td>
350 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
351 <td><?php esc_html_e('Push content into the knowledge base. Body: content, source_url, optional bot_id and content_type.', 'mxchat'); ?></td>
352 </tr>
353 <tr>
354 <td><span class="mxch-api-method mxch-api-method-delete">DELETE</span></td>
355 <td><code><?php echo esc_html($knowledge_url); ?></code></td>
356 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
357 <td><?php esc_html_e('Remove knowledge entries by source_url — one, or an array of up to 50. Removing a URL that is not stored reports deleted:false rather than failing, so a sync job can re-run safely.', 'mxchat'); ?></td>
358 </tr>
359 <tr>
360 <td><span class="mxch-api-method mxch-api-method-get">GET</span></td>
361 <td><code><?php echo esc_html($leads_url); ?></code></td>
362 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
363 <td><?php esc_html_e('Read lead-capture submissions — the same rows the Leads tab shows, with consent state. Filterable by since, search and status; supports page + per_page pagination (max 200).', 'mxchat'); ?></td>
364 </tr>
365 <tr>
366 <td><span class="mxch-api-method mxch-api-method-delete">DELETE</span></td>
367 <td><code><?php echo esc_html($transcripts_u); ?></code></td>
368 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
369 <td><?php esc_html_e('Bulk-delete chat sessions by session_id, with optional cascade to translations and click-tracking. Capped at 1000 per call.', 'mxchat'); ?></td>
370 </tr>
371 </tbody>
372 </table>
373 </div>
374 </div>
375 </div>
376
377 <!-- Examples Section -->
378 <div id="api-examples" class="mxch-section">
379 <div class="mxch-content-header">
380 <h1 class="mxch-content-title"><?php esc_html_e('Examples', 'mxchat'); ?></h1>
381 <p class="mxch-content-subtitle"><?php esc_html_e('Drop-in cURL snippets — replace YOUR_TOKEN with the value from the Token tab.', 'mxchat'); ?></p>
382 </div>
383
384 <div class="mxch-card">
385 <div class="mxch-card-header">
386 <h3 class="mxch-card-title"><?php esc_html_e('Health check', 'mxchat'); ?></h3>
387 <span class="mxch-card-subtitle"><?php esc_html_e('No auth required', 'mxchat'); ?></span>
388 </div>
389 <div class="mxch-card-body">
390 <pre class="mxch-api-codeblock"><code>curl <?php echo esc_html($health_url); ?></code></pre>
391 </div>
392 </div>
393
394 <div class="mxch-card">
395 <div class="mxch-card-header">
396 <h3 class="mxch-card-title"><?php esc_html_e('Find transcripts where the bot had no knowledge to ground answers', 'mxchat'); ?></h3>
397 </div>
398 <div class="mxch-card-body">
399 <pre class="mxch-api-codeblock"><code>curl -H "Authorization: Bearer YOUR_TOKEN" \
400 "<?php echo esc_html($transcripts_u); ?>?role=user&amp;has_rag_context=no&amp;since=2026-05-01&amp;limit=50"</code></pre>
401 </div>
402 </div>
403
404 <div class="mxch-card">
405 <div class="mxch-card-header">
406 <h3 class="mxch-card-title"><?php esc_html_e('Push a Q&A entry into the knowledge base', 'mxchat'); ?></h3>
407 </div>
408 <div class="mxch-card-body">
409 <pre class="mxch-api-codeblock"><code>curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
410 -H "Content-Type: application/json" \
411 -d '{"content":"Q: How do I enable streaming?\nA: ...","source_url":"https://example.com/faq#streaming","content_type":"faq"}' \
412 "<?php echo esc_html($knowledge_url); ?>"</code></pre>
413 </div>
414 </div>
415
416 <div class="mxch-card">
417 <div class="mxch-card-header">
418 <h3 class="mxch-card-title"><?php esc_html_e('Remove documents deleted in your source system', 'mxchat'); ?></h3>
419 </div>
420 <div class="mxch-card-body">
421 <p class="mxch-field-description"><?php esc_html_e('When a file is deleted in SharePoint, Drive or your CMS, delete it here by the same source_url you pushed it with — otherwise the bot keeps answering from it. Up to 50 per call, and a URL that is already gone comes back as deleted:false instead of an error, so an automation can re-send the same batch safely.', 'mxchat'); ?></p>
422 <pre class="mxch-api-codeblock"><code>curl -X DELETE -H "Authorization: Bearer YOUR_TOKEN" \
423 -H "Content-Type: application/json" \
424 -d '{"source_url":["https://example.com/handbook.pdf","https://example.com/policy-2024.docx"]}' \
425 "<?php echo esc_html($knowledge_url); ?>"</code></pre>
426 </div>
427 </div>
428
429 <div class="mxch-card">
430 <div class="mxch-card-header">
431 <h3 class="mxch-card-title"><?php esc_html_e('Sync new leads into your CRM', 'mxchat'); ?></h3>
432 </div>
433 <div class="mxch-card-body">
434 <p class="mxch-field-description"><?php esc_html_e('Poll incrementally by passing the timestamp of the last lead you imported.', 'mxchat'); ?></p>
435 <pre class="mxch-api-codeblock"><code>curl -H "Authorization: Bearer YOUR_TOKEN" \
436 "<?php echo esc_html($leads_url); ?>?since=2026-05-01T00:00:00Z&amp;per_page=200"</code></pre>
437 </div>
438 </div>
439
440 <div class="mxch-card">
441 <div class="mxch-card-header">
442 <h3 class="mxch-card-title"><?php esc_html_e('Bulk-delete two chat sessions', 'mxchat'); ?></h3>
443 </div>
444 <div class="mxch-card-body">
445 <pre class="mxch-api-codeblock"><code>curl -X DELETE -H "Authorization: Bearer YOUR_TOKEN" \
446 -H "Content-Type: application/json" \
447 -d '{"session_ids":["sess_abc","sess_def"],"cascade":true}' \
448 "<?php echo esc_html($transcripts_u); ?>"</code></pre>
449 </div>
450 </div>
451 </div>
452
453 <!-- Privacy Section -->
454 <div id="api-privacy" class="mxch-section">
455 <div class="mxch-content-header">
456 <h1 class="mxch-content-title"><?php esc_html_e('Privacy & Safety', 'mxchat'); ?></h1>
457 <p class="mxch-content-subtitle"><?php esc_html_e('What the API exposes — and how to keep it safe.', 'mxchat'); ?></p>
458 </div>
459
460 <div class="mxch-card">
461 <div class="mxch-card-body">
462 <ul class="mxch-api-privacy-list">
463 <li>
464 <svg class="mxch-api-bullet" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
465 <span><?php esc_html_e('No data leaves your site unsolicited. Endpoints only respond to requests carrying your token.', 'mxchat'); ?></span>
466 </li>
467 <li>
468 <svg class="mxch-api-bullet" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
469 <span><?php esc_html_e('The /transcripts endpoint may return user-submitted chat data including emails and names — treat your API token as sensitive.', 'mxchat'); ?></span>
470 </li>
471 <li>
472 <svg class="mxch-api-bullet" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
473 <span><?php esc_html_e('Use HTTPS only. Do not include the token in URL query strings.', 'mxchat'); ?></span>
474 </li>
475 <li>
476 <svg class="mxch-api-bullet" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
477 <span><?php esc_html_e('Rotate the token if you suspect it was leaked. Old token is invalidated immediately.', 'mxchat'); ?></span>
478 </li>
479 </ul>
480 </div>
481 </div>
482 </div>
483
484 </main>
485 </div>
486
487 <style>
488 /* Page-specific touches that build on admin-sidebar.css */
489 .mxch-api-flash { margin: 0 0 var(--mxch-spacing-lg, 16px) 0; }
490 .mxch-api-token-reveal {
491 display: inline-block;
492 padding: 8px 12px;
493 background: var(--mxch-primary-lighter, #f0f6fc);
494 border: 1px solid var(--mxch-card-border, #c3c4c7);
495 border-radius: var(--mxch-radius-md, 6px);
496 font-size: 13px;
497 user-select: all;
498 word-break: break-all;
499 margin-right: 8px;
500 }
501 .mxch-api-token-masked {
502 display: inline-block;
503 padding: 6px 10px;
504 background: #f6f7f7;
505 border: 1px solid var(--mxch-card-border, #e2e4e9);
506 border-radius: var(--mxch-radius-sm, 4px);
507 font-size: 13px;
508 color: var(--mxch-text-primary, #1d2327);
509 }
510 /* mxch-status-pill rules moved to css/admin-sidebar.css (plan b6e4d4)
511 — it is a documented shell component and every admin page loads
512 that sheet, so it must not live in one page's inline block. */
513
514 .mxch-api-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
515 .mxch-api-form { display: inline; margin: 0; }
516
517 .mxch-api-endpoints-table { width: 100%; border-collapse: collapse; }
518 .mxch-api-endpoints-table th,
519 .mxch-api-endpoints-table td {
520 padding: 12px;
521 text-align: left;
522 border-bottom: 1px solid var(--mxch-card-border, #e2e4e9);
523 vertical-align: top;
524 font-size: 13px;
525 }
526 .mxch-api-endpoints-table th {
527 font-weight: 600;
528 color: var(--mxch-text-secondary, #50575e);
529 text-transform: uppercase;
530 letter-spacing: 0.5px;
531 font-size: 11px;
532 background: #fafbfc;
533 }
534 .mxch-api-endpoints-table tr:last-child td { border-bottom: none; }
535 .mxch-api-endpoints-table code {
536 background: #f6f7f7;
537 padding: 2px 6px;
538 border-radius: 3px;
539 font-size: 12px;
540 }
541
542 .mxch-api-method {
543 display: inline-block;
544 padding: 2px 8px;
545 border-radius: 4px;
546 font-size: 11px;
547 font-weight: 700;
548 letter-spacing: 0.3px;
549 }
550 .mxch-api-method-get { background: #e7f0fb; color: #1a56b4; }
551 .mxch-api-method-post { background: #e6f4ea; color: #1d7a3a; }
552 .mxch-api-method-delete { background: #fce4e4; color: #b32020; }
553
554 .mxch-api-auth {
555 display: inline-block;
556 padding: 2px 8px;
557 border-radius: 4px;
558 font-size: 11px;
559 font-weight: 600;
560 }
561 .mxch-api-auth-none { background: #f0f0f0; color: #50575e; }
562 .mxch-api-auth-bearer { background: #fff4e0; color: #8a5a00; }
563
564 .mxch-api-codeblock {
565 margin: 0;
566 background: #1f2328;
567 color: #e6edf3;
568 padding: 14px 16px;
569 border-radius: var(--mxch-radius-md, 6px);
570 overflow-x: auto;
571 font-size: 12.5px;
572 line-height: 1.55;
573 }
574 .mxch-api-codeblock code { background: transparent; color: inherit; padding: 0; font-size: inherit; }
575
576 .mxch-api-privacy-list { list-style: none; margin: 0; padding: 0; }
577 .mxch-api-privacy-list li {
578 display: flex;
579 align-items: flex-start;
580 gap: 12px;
581 padding: 10px 0;
582 font-size: 14px;
583 color: var(--mxch-text-primary, #1d2327);
584 line-height: 1.5;
585 }
586 .mxch-api-privacy-list li + li { border-top: 1px solid var(--mxch-card-border, #e2e4e9); }
587 .mxch-api-bullet { flex-shrink: 0; margin-top: 2px; color: var(--mxch-primary, #7873f5); }
588
589 .mxch-card-subtitle {
590 font-size: 12px;
591 color: var(--mxch-text-secondary, #50575e);
592 text-transform: uppercase;
593 letter-spacing: 0.5px;
594 }
595 </style>
596 <?php
597 // Tab switcher / mobile menu / copy-to-clipboard are wired by the shared
598 // mxchat-basic/js/admin-sidebar.js, enqueued in class-mxchat-admin.php for
599 // the mxchat-api-access screen.
600 }
601