PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.17
MxChat – AI Chatbot & Content Generation for WordPress v3.2.17
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 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / includes / admin-api-page.php

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

579 lines 32.7 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
95 $plugin_url = plugin_dir_url(dirname(__FILE__));
96 $masked = $token_set
97 ? substr($stored_token, 0, 4) . str_repeat('', 24) . substr($stored_token, -4)
98 : '';
99 $rotated_human = $rotated_at > 0
100 ? sprintf(
101 /* translators: %s: human-readable elapsed time */
102 esc_html__('%s ago', 'mxchat'),
103 human_time_diff($rotated_at, current_time('timestamp'))
104 )
105 : esc_html__('Never', 'mxchat');
106 ?>
107 <div class="mxch-admin-wrapper mxch-api-wrapper">
108
109 <!-- Mobile Header -->
110 <header class="mxch-mobile-header">
111 <a href="#" class="mxch-mobile-logo">
112 <div class="mxch-mobile-logo-icon">
113 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
114 </div>
115 <span class="mxch-mobile-logo-text">MxChat</span>
116 </a>
117 <button type="button" class="mxch-mobile-menu-btn" aria-label="<?php esc_attr_e('Open menu', 'mxchat'); ?>">
118 <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>
119 </button>
120 </header>
121
122 <!-- Mobile Menu Overlay -->
123 <div class="mxch-mobile-overlay"></div>
124
125 <!-- Mobile Menu Modal -->
126 <div class="mxch-mobile-menu">
127 <div class="mxch-mobile-menu-header">
128 <span class="mxch-mobile-menu-title"><?php esc_html_e('API Access', 'mxchat'); ?></span>
129 <button type="button" class="mxch-mobile-menu-close" aria-label="<?php esc_attr_e('Close menu', 'mxchat'); ?>">
130 <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>
131 </button>
132 </div>
133 <nav class="mxch-mobile-menu-nav">
134 <div class="mxch-mobile-nav-section">
135 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('REST API', 'mxchat'); ?></div>
136 <button class="mxch-mobile-nav-link active" data-target="api-token">
137 <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>
138 <span><?php esc_html_e('Token', 'mxchat'); ?></span>
139 </button>
140 <button class="mxch-mobile-nav-link" data-target="api-endpoints">
141 <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>
142 <span><?php esc_html_e('Endpoints', 'mxchat'); ?></span>
143 </button>
144 <button class="mxch-mobile-nav-link" data-target="api-examples">
145 <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>
146 <span><?php esc_html_e('Examples', 'mxchat'); ?></span>
147 </button>
148 <button class="mxch-mobile-nav-link" data-target="api-privacy">
149 <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>
150 <span><?php esc_html_e('Privacy', 'mxchat'); ?></span>
151 </button>
152 </div>
153 </nav>
154 </div>
155
156 <!-- Sidebar Navigation -->
157 <aside class="mxch-sidebar">
158 <div class="mxch-sidebar-header">
159 <a href="#" class="mxch-sidebar-logo">
160 <div class="mxch-sidebar-logo-icon">
161 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
162 </div>
163 <span class="mxch-sidebar-logo-text">MxChat</span>
164 <span class="mxch-sidebar-version">v<?php echo esc_html(MXCHAT_VERSION ?? '2.7.0'); ?></span>
165 </a>
166 </div>
167
168 <nav class="mxch-sidebar-nav">
169 <div class="mxch-nav-section">
170 <div class="mxch-nav-section-title"><?php esc_html_e('REST API', 'mxchat'); ?></div>
171
172 <div class="mxch-nav-item" data-section="api-token">
173 <button class="mxch-nav-link active" data-target="api-token">
174 <span class="mxch-nav-link-icon">
175 <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>
176 </span>
177 <span class="mxch-nav-link-text"><?php esc_html_e('Token', 'mxchat'); ?></span>
178 <?php if ($token_set): ?>
179 <span class="mxch-nav-link-badge mxch-active-badge"><?php esc_html_e('Active', 'mxchat'); ?></span>
180 <?php endif; ?>
181 </button>
182 </div>
183
184 <div class="mxch-nav-item" data-section="api-endpoints">
185 <button class="mxch-nav-link" data-target="api-endpoints">
186 <span class="mxch-nav-link-icon">
187 <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>
188 </span>
189 <span class="mxch-nav-link-text"><?php esc_html_e('Endpoints', 'mxchat'); ?></span>
190 </button>
191 </div>
192
193 <div class="mxch-nav-item" data-section="api-examples">
194 <button class="mxch-nav-link" data-target="api-examples">
195 <span class="mxch-nav-link-icon">
196 <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>
197 </span>
198 <span class="mxch-nav-link-text"><?php esc_html_e('Examples', 'mxchat'); ?></span>
199 </button>
200 </div>
201
202 <div class="mxch-nav-item" data-section="api-privacy">
203 <button class="mxch-nav-link" data-target="api-privacy">
204 <span class="mxch-nav-link-icon">
205 <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>
206 </span>
207 <span class="mxch-nav-link-text"><?php esc_html_e('Privacy', 'mxchat'); ?></span>
208 </button>
209 </div>
210 </div>
211 </nav>
212 </aside>
213
214 <!-- Main Content Area -->
215 <main class="mxch-content">
216
217 <?php if ($rotated_query && $just_rotated): ?>
218 <div class="notice notice-success is-dismissible mxch-api-flash">
219 <p>
220 <strong><?php esc_html_e('New API token generated.', 'mxchat'); ?></strong>
221 <?php esc_html_e('Copy it now — for security, the full value will not be shown again after you leave this page.', 'mxchat'); ?>
222 </p>
223 <p>
224 <code class="mxch-api-token-reveal" data-mxch-copy-target><?php echo esc_html($just_rotated); ?></code>
225 <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); ?>">
226 <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>
227 <span><?php esc_html_e('Copy', 'mxchat'); ?></span>
228 </button>
229 </p>
230 </div>
231 <?php endif; ?>
232
233 <?php if ($revoked_query): ?>
234 <div class="notice notice-warning is-dismissible mxch-api-flash">
235 <p>
236 <strong><?php esc_html_e('API token revoked.', 'mxchat'); ?></strong>
237 <?php esc_html_e('All REST endpoints will return 401 until a new token is generated.', 'mxchat'); ?>
238 </p>
239 </div>
240 <?php endif; ?>
241
242 <!-- Token Section -->
243 <div id="api-token" class="mxch-section active">
244 <div class="mxch-content-header">
245 <h1 class="mxch-content-title"><?php esc_html_e('REST API', 'mxchat'); ?></h1>
246 <p class="mxch-content-subtitle">
247 <?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'); ?>
248 </p>
249 </div>
250
251 <div class="mxch-card">
252 <div class="mxch-card-header">
253 <h3 class="mxch-card-title">
254 <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>
255 <?php esc_html_e('Token', 'mxchat'); ?>
256 </h3>
257 <?php if ($token_set): ?>
258 <span class="mxch-status-pill mxch-status-pill-active">
259 <span class="mxch-status-dot"></span>
260 <?php esc_html_e('Active', 'mxchat'); ?>
261 </span>
262 <?php else: ?>
263 <span class="mxch-status-pill mxch-status-pill-disabled">
264 <span class="mxch-status-dot"></span>
265 <?php esc_html_e('Disabled', 'mxchat'); ?>
266 </span>
267 <?php endif; ?>
268 </div>
269 <div class="mxch-card-body">
270 <?php if ($token_set): ?>
271 <div class="mxch-field">
272 <div class="mxch-field-label"><?php esc_html_e('Current token', 'mxchat'); ?></div>
273 <code class="mxch-api-token-masked"><?php echo esc_html($masked); ?></code>
274 <p class="mxch-field-description">
275 <?php esc_html_e('Last rotated:', 'mxchat'); ?>
276 <strong><?php echo esc_html($rotated_human); ?></strong>
277 </p>
278 </div>
279 <?php else: ?>
280 <p class="mxch-field-description">
281 <?php esc_html_e('No token is currently set. Generate one below to enable the authenticated REST endpoints.', 'mxchat'); ?>
282 </p>
283 <?php endif; ?>
284
285 <div class="mxch-api-actions">
286 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="mxch-api-form">
287 <input type="hidden" name="action" value="mxchat_rotate_api_token" />
288 <?php wp_nonce_field('mxchat_rotate_api_token'); ?>
289 <button type="submit" class="mxch-btn mxch-btn-primary">
290 <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>
291 <?php echo $token_set ? esc_html__('Regenerate Token', 'mxchat') : esc_html__('Generate Token', 'mxchat'); ?>
292 </button>
293 </form>
294
295 <?php if ($token_set): ?>
296 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="mxch-api-form"
297 onsubmit="return confirm('<?php echo esc_js(__('Revoke the API token? Any external tool using the current token will stop working immediately.', 'mxchat')); ?>');">
298 <input type="hidden" name="action" value="mxchat_revoke_api_token" />
299 <?php wp_nonce_field('mxchat_revoke_api_token'); ?>
300 <button type="submit" class="mxch-btn mxch-btn-danger">
301 <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>
302 <?php esc_html_e('Revoke Token', 'mxchat'); ?>
303 </button>
304 </form>
305 <?php endif; ?>
306 </div>
307
308 <p class="mxch-field-hint">
309 <?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'); ?>
310 </p>
311 </div>
312 </div>
313 </div>
314
315 <!-- Endpoints Section -->
316 <div id="api-endpoints" class="mxch-section">
317 <div class="mxch-content-header">
318 <h1 class="mxch-content-title"><?php esc_html_e('Endpoints', 'mxchat'); ?></h1>
319 <p class="mxch-content-subtitle"><?php esc_html_e('All endpoints live under the WordPress REST namespace mxchat/v1.', 'mxchat'); ?></p>
320 </div>
321
322 <div class="mxch-card">
323 <div class="mxch-card-body">
324 <table class="mxch-api-endpoints-table">
325 <thead>
326 <tr>
327 <th><?php esc_html_e('Method', 'mxchat'); ?></th>
328 <th><?php esc_html_e('URL', 'mxchat'); ?></th>
329 <th><?php esc_html_e('Auth', 'mxchat'); ?></th>
330 <th><?php esc_html_e('Purpose', 'mxchat'); ?></th>
331 </tr>
332 </thead>
333 <tbody>
334 <tr>
335 <td><span class="mxch-api-method mxch-api-method-get">GET</span></td>
336 <td><code><?php echo esc_html($health_url); ?></code></td>
337 <td><span class="mxch-api-auth mxch-api-auth-none"><?php esc_html_e('None', 'mxchat'); ?></span></td>
338 <td><?php esc_html_e('Connectivity check; reports plugin version and whether a token is set.', 'mxchat'); ?></td>
339 </tr>
340 <tr>
341 <td><span class="mxch-api-method mxch-api-method-get">GET</span></td>
342 <td><code><?php echo esc_html($transcripts_u); ?></code></td>
343 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
344 <td><?php esc_html_e('Read chat transcripts. Filterable by since, until, session_id, role, has_rag_context; supports limit + offset pagination.', 'mxchat'); ?></td>
345 </tr>
346 <tr>
347 <td><span class="mxch-api-method mxch-api-method-post">POST</span></td>
348 <td><code><?php echo esc_html($knowledge_url); ?></code></td>
349 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
350 <td><?php esc_html_e('Push content into the knowledge base. Body: content, source_url, optional bot_id and content_type.', 'mxchat'); ?></td>
351 </tr>
352 <tr>
353 <td><span class="mxch-api-method mxch-api-method-delete">DELETE</span></td>
354 <td><code><?php echo esc_html($transcripts_u); ?></code></td>
355 <td><span class="mxch-api-auth mxch-api-auth-bearer"><?php esc_html_e('Bearer', 'mxchat'); ?></span></td>
356 <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>
357 </tr>
358 </tbody>
359 </table>
360 </div>
361 </div>
362 </div>
363
364 <!-- Examples Section -->
365 <div id="api-examples" class="mxch-section">
366 <div class="mxch-content-header">
367 <h1 class="mxch-content-title"><?php esc_html_e('Examples', 'mxchat'); ?></h1>
368 <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>
369 </div>
370
371 <div class="mxch-card">
372 <div class="mxch-card-header">
373 <h3 class="mxch-card-title"><?php esc_html_e('Health check', 'mxchat'); ?></h3>
374 <span class="mxch-card-subtitle"><?php esc_html_e('No auth required', 'mxchat'); ?></span>
375 </div>
376 <div class="mxch-card-body">
377 <pre class="mxch-api-codeblock"><code>curl <?php echo esc_html($health_url); ?></code></pre>
378 </div>
379 </div>
380
381 <div class="mxch-card">
382 <div class="mxch-card-header">
383 <h3 class="mxch-card-title"><?php esc_html_e('Find transcripts where the bot had no knowledge to ground answers', 'mxchat'); ?></h3>
384 </div>
385 <div class="mxch-card-body">
386 <pre class="mxch-api-codeblock"><code>curl -H "Authorization: Bearer YOUR_TOKEN" \
387 "<?php echo esc_html($transcripts_u); ?>?role=user&amp;has_rag_context=no&amp;since=2026-05-01&amp;limit=50"</code></pre>
388 </div>
389 </div>
390
391 <div class="mxch-card">
392 <div class="mxch-card-header">
393 <h3 class="mxch-card-title"><?php esc_html_e('Push a Q&A entry into the knowledge base', 'mxchat'); ?></h3>
394 </div>
395 <div class="mxch-card-body">
396 <pre class="mxch-api-codeblock"><code>curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
397 -H "Content-Type: application/json" \
398 -d '{"content":"Q: How do I enable streaming?\nA: ...","source_url":"https://example.com/faq#streaming","content_type":"faq"}' \
399 "<?php echo esc_html($knowledge_url); ?>"</code></pre>
400 </div>
401 </div>
402
403 <div class="mxch-card">
404 <div class="mxch-card-header">
405 <h3 class="mxch-card-title"><?php esc_html_e('Bulk-delete two chat sessions', 'mxchat'); ?></h3>
406 </div>
407 <div class="mxch-card-body">
408 <pre class="mxch-api-codeblock"><code>curl -X DELETE -H "Authorization: Bearer YOUR_TOKEN" \
409 -H "Content-Type: application/json" \
410 -d '{"session_ids":["sess_abc","sess_def"],"cascade":true}' \
411 "<?php echo esc_html($transcripts_u); ?>"</code></pre>
412 </div>
413 </div>
414 </div>
415
416 <!-- Privacy Section -->
417 <div id="api-privacy" class="mxch-section">
418 <div class="mxch-content-header">
419 <h1 class="mxch-content-title"><?php esc_html_e('Privacy & Safety', 'mxchat'); ?></h1>
420 <p class="mxch-content-subtitle"><?php esc_html_e('What the API exposes — and how to keep it safe.', 'mxchat'); ?></p>
421 </div>
422
423 <div class="mxch-card">
424 <div class="mxch-card-body">
425 <ul class="mxch-api-privacy-list">
426 <li>
427 <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>
428 <span><?php esc_html_e('No data leaves your site unsolicited. Endpoints only respond to requests carrying your token.', 'mxchat'); ?></span>
429 </li>
430 <li>
431 <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>
432 <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>
433 </li>
434 <li>
435 <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>
436 <span><?php esc_html_e('Use HTTPS only. Do not include the token in URL query strings.', 'mxchat'); ?></span>
437 </li>
438 <li>
439 <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>
440 <span><?php esc_html_e('Rotate the token if you suspect it was leaked. Old token is invalidated immediately.', 'mxchat'); ?></span>
441 </li>
442 </ul>
443 </div>
444 </div>
445 </div>
446
447 </main>
448 </div>
449
450 <style>
451 /* Page-specific touches that build on admin-sidebar.css */
452 .mxch-api-flash { margin: 0 0 var(--mxch-spacing-lg, 16px) 0; }
453 .mxch-api-token-reveal {
454 display: inline-block;
455 padding: 8px 12px;
456 background: var(--mxch-primary-lighter, #f0f6fc);
457 border: 1px solid var(--mxch-card-border, #c3c4c7);
458 border-radius: var(--mxch-radius-md, 6px);
459 font-size: 13px;
460 user-select: all;
461 word-break: break-all;
462 margin-right: 8px;
463 }
464 .mxch-api-token-masked {
465 display: inline-block;
466 padding: 6px 10px;
467 background: #f6f7f7;
468 border: 1px solid var(--mxch-card-border, #e2e4e9);
469 border-radius: var(--mxch-radius-sm, 4px);
470 font-size: 13px;
471 color: var(--mxch-text-primary, #1d2327);
472 }
473 .mxch-status-pill {
474 display: inline-flex;
475 align-items: center;
476 gap: 6px;
477 padding: 4px 10px;
478 border-radius: 999px;
479 font-size: 12px;
480 font-weight: 600;
481 }
482 .mxch-status-pill .mxch-status-dot {
483 width: 8px;
484 height: 8px;
485 border-radius: 50%;
486 }
487 .mxch-status-pill-active { background: #e6f4ea; color: #1d7a3a; }
488 .mxch-status-pill-active .mxch-status-dot { background: #1d7a3a; }
489 .mxch-status-pill-disabled { background: #fef3e2; color: #a04a00; }
490 .mxch-status-pill-disabled .mxch-status-dot { background: #a04a00; }
491
492 .mxch-api-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
493 .mxch-api-form { display: inline; margin: 0; }
494
495 .mxch-api-endpoints-table { width: 100%; border-collapse: collapse; }
496 .mxch-api-endpoints-table th,
497 .mxch-api-endpoints-table td {
498 padding: 12px;
499 text-align: left;
500 border-bottom: 1px solid var(--mxch-card-border, #e2e4e9);
501 vertical-align: top;
502 font-size: 13px;
503 }
504 .mxch-api-endpoints-table th {
505 font-weight: 600;
506 color: var(--mxch-text-secondary, #50575e);
507 text-transform: uppercase;
508 letter-spacing: 0.5px;
509 font-size: 11px;
510 background: #fafbfc;
511 }
512 .mxch-api-endpoints-table tr:last-child td { border-bottom: none; }
513 .mxch-api-endpoints-table code {
514 background: #f6f7f7;
515 padding: 2px 6px;
516 border-radius: 3px;
517 font-size: 12px;
518 }
519
520 .mxch-api-method {
521 display: inline-block;
522 padding: 2px 8px;
523 border-radius: 4px;
524 font-size: 11px;
525 font-weight: 700;
526 letter-spacing: 0.3px;
527 }
528 .mxch-api-method-get { background: #e7f0fb; color: #1a56b4; }
529 .mxch-api-method-post { background: #e6f4ea; color: #1d7a3a; }
530 .mxch-api-method-delete { background: #fce4e4; color: #b32020; }
531
532 .mxch-api-auth {
533 display: inline-block;
534 padding: 2px 8px;
535 border-radius: 4px;
536 font-size: 11px;
537 font-weight: 600;
538 }
539 .mxch-api-auth-none { background: #f0f0f0; color: #50575e; }
540 .mxch-api-auth-bearer { background: #fff4e0; color: #8a5a00; }
541
542 .mxch-api-codeblock {
543 margin: 0;
544 background: #1f2328;
545 color: #e6edf3;
546 padding: 14px 16px;
547 border-radius: var(--mxch-radius-md, 6px);
548 overflow-x: auto;
549 font-size: 12.5px;
550 line-height: 1.55;
551 }
552 .mxch-api-codeblock code { background: transparent; color: inherit; padding: 0; font-size: inherit; }
553
554 .mxch-api-privacy-list { list-style: none; margin: 0; padding: 0; }
555 .mxch-api-privacy-list li {
556 display: flex;
557 align-items: flex-start;
558 gap: 12px;
559 padding: 10px 0;
560 font-size: 14px;
561 color: var(--mxch-text-primary, #1d2327);
562 line-height: 1.5;
563 }
564 .mxch-api-privacy-list li + li { border-top: 1px solid var(--mxch-card-border, #e2e4e9); }
565 .mxch-api-bullet { flex-shrink: 0; margin-top: 2px; color: var(--mxch-primary, #7873f5); }
566
567 .mxch-card-subtitle {
568 font-size: 12px;
569 color: var(--mxch-text-secondary, #50575e);
570 text-transform: uppercase;
571 letter-spacing: 0.5px;
572 }
573 </style>
574 <?php
575 // Tab switcher / mobile menu / copy-to-clipboard are wired by the shared
576 // mxchat-basic/js/admin-sidebar.js, enqueued in class-mxchat-admin.php for
577 // the mxchat-api-access screen.
578 }
579