PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.21
MxChat – AI Chatbot & Content Generation for WordPress v3.2.21
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.21, at includes/admin-api-page.php

564 lines 32.2 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 rules moved to css/admin-sidebar.css (plan b6e4d4)
474 — it is a documented shell component and every admin page loads
475 that sheet, so it must not live in one page's inline block. */
476
477 .mxch-api-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
478 .mxch-api-form { display: inline; margin: 0; }
479
480 .mxch-api-endpoints-table { width: 100%; border-collapse: collapse; }
481 .mxch-api-endpoints-table th,
482 .mxch-api-endpoints-table td {
483 padding: 12px;
484 text-align: left;
485 border-bottom: 1px solid var(--mxch-card-border, #e2e4e9);
486 vertical-align: top;
487 font-size: 13px;
488 }
489 .mxch-api-endpoints-table th {
490 font-weight: 600;
491 color: var(--mxch-text-secondary, #50575e);
492 text-transform: uppercase;
493 letter-spacing: 0.5px;
494 font-size: 11px;
495 background: #fafbfc;
496 }
497 .mxch-api-endpoints-table tr:last-child td { border-bottom: none; }
498 .mxch-api-endpoints-table code {
499 background: #f6f7f7;
500 padding: 2px 6px;
501 border-radius: 3px;
502 font-size: 12px;
503 }
504
505 .mxch-api-method {
506 display: inline-block;
507 padding: 2px 8px;
508 border-radius: 4px;
509 font-size: 11px;
510 font-weight: 700;
511 letter-spacing: 0.3px;
512 }
513 .mxch-api-method-get { background: #e7f0fb; color: #1a56b4; }
514 .mxch-api-method-post { background: #e6f4ea; color: #1d7a3a; }
515 .mxch-api-method-delete { background: #fce4e4; color: #b32020; }
516
517 .mxch-api-auth {
518 display: inline-block;
519 padding: 2px 8px;
520 border-radius: 4px;
521 font-size: 11px;
522 font-weight: 600;
523 }
524 .mxch-api-auth-none { background: #f0f0f0; color: #50575e; }
525 .mxch-api-auth-bearer { background: #fff4e0; color: #8a5a00; }
526
527 .mxch-api-codeblock {
528 margin: 0;
529 background: #1f2328;
530 color: #e6edf3;
531 padding: 14px 16px;
532 border-radius: var(--mxch-radius-md, 6px);
533 overflow-x: auto;
534 font-size: 12.5px;
535 line-height: 1.55;
536 }
537 .mxch-api-codeblock code { background: transparent; color: inherit; padding: 0; font-size: inherit; }
538
539 .mxch-api-privacy-list { list-style: none; margin: 0; padding: 0; }
540 .mxch-api-privacy-list li {
541 display: flex;
542 align-items: flex-start;
543 gap: 12px;
544 padding: 10px 0;
545 font-size: 14px;
546 color: var(--mxch-text-primary, #1d2327);
547 line-height: 1.5;
548 }
549 .mxch-api-privacy-list li + li { border-top: 1px solid var(--mxch-card-border, #e2e4e9); }
550 .mxch-api-bullet { flex-shrink: 0; margin-top: 2px; color: var(--mxch-primary, #7873f5); }
551
552 .mxch-card-subtitle {
553 font-size: 12px;
554 color: var(--mxch-text-secondary, #50575e);
555 text-transform: uppercase;
556 letter-spacing: 0.5px;
557 }
558 </style>
559 <?php
560 // Tab switcher / mobile menu / copy-to-clipboard are wired by the shared
561 // mxchat-basic/js/admin-sidebar.js, enqueued in class-mxchat-admin.php for
562 // the mxchat-api-access screen.
563 }
564