PageSourceSearch

https://burst-statistics.com/docs/assets/js/d1626603.c02b3c57.js

js burst-statistics.com collected 2026-09-25 03:56:47 UTC 64,471 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkburst_statistics=self.webpackChunkburst_statistics||[]).push([["2222"],{2661(e,s,i){i.r(s),i.d(s,{metadata:()=>r,default:()=>h,frontMatter:()=>n,contentTitle:()=>c,toc:()=>a,assets:()=>l});var r=JSON.parse('{"id":"rest-api/admin-endpoints","title":"Admin endpoints","description":"These endpoints power the Burst Statistics admin dashboard. They are not intended as a primary integration API \u2014 use burst/v1/data/ for analytics data instead.","source":"@site/docs/04-rest-api/02-admin-endpoints.md","sourceDirName":"04-rest-api","slug":"/rest-api/admin-endpoints","permalink":"/docs/rest-api/admin-endpoints","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":2,"frontMatter":{},"sidebar":"docs","previous":{"title":"authentication","permalink":"/docs/rest-api/authentication"},"next":{"title":"Data endpoints","permalink":"/docs/rest-api/data-endpoints"}}'),t=i(4848),d=i(8453);let n={},c="Admin endpoints",l={},a=[{value:"Settings and fields",id:"settings-and-fields",level:2},{value:"<code>GET burst/v1/fields/get</code>",id:"get-burstv1fieldsget",level:3},{value:"<code>POST burst/v1/fields/set</code>
1",id:"post-burstv1fieldsset",level:3},{value:"<code>POST burst/v1/options/set</code>",id:"post-burstv1optionsset",level:3},{value:"Goals",id:"goals",level:2},{value:"<code>GET burst/v1/goals/get</code>",id:"get-burstv1goalsget",level:3},{value:"<code>POST burst/v1/goals/add</code>",id:"post-burstv1goalsadd",level:3},{value:"<code>POST burst/v1/goals/set</code>",id:"post-burstv1goalsset",level:3},{value:"<code>POST burst/v1/goals/upsert_for_block</code>",id:"post-burstv1goalsupsert_for_block",level:3},{value:"<code>POST burst/v1/goals/delete</code>",id:"post-burstv1goalsdelete",level:3},{value:"<code>POST burst/v1/goals/add_predefined</code>",id:"post-burstv1goalsadd_predefined",level:3},{value:"Posts search",id:"posts-search",level:2},{value:"<code>GET burst/v1/posts/</code>",id:"get-burstv1posts",level:3},{value:"Generic action endpoints",id:"generic-action-endpoints",level:2},{value:"<code>POST burst/v1/do_action/{action}</code>",id:"post-burstv1do_actionaction",level:3},{value:"<code>GET burst/v1/get_action/{action}</code>",id:"get-burstv1get_actionaction",level:3},{value:"<code>GET burst/v1/get_action/ecommerce/{action}</code>",id:"get-burstv1get_actionecommerceaction",level:3},{value:"Extending <code>do_action</code> and <code>get_action</code>",id:"extending-do_action-and-get_action",level:2},{value:"<code>burst_do_action</code>",id:"burst_do_action",level:3},{value:"<code>burst_get_action</code>",id:"burst_get_action",level:3},{value:"Abilities API",id:"abilities-api",level:2},{value:"Prerequisites",id:"prerequisites",level:3},{value:"Registered abilities",id:"registered-abilities",level:3},{value:"Querying datatables with <code>burst/data</code>",id:"querying-datatables-with-burstdata",level:3},{value:"Permission and rate limiting",id:"permission-and-rate-limiting",level:3},{value:"Error codes",id:"error-codes",level:3},{value:"Chat",id:"chat",level:2},{value:"Prerequisites",id:"prerequisites-1",level:3},{value:"<code>POST burst/v1/chat</code>",id:"post-burstv1chat",level:3},{value:"<code>GET burst/v1/chat/models</code>",id:"get-burstv1chatmodels",level:3},{value:"<code>GET burst/v1/chat/status</code>",id:"get-burstv1chatstatus",level:3},{value:"MainWP integration",id:"mainwp-integration",level:2},{value:"<code>GET burst/v1/mainwp-auth</code>",id:"get-burstv1mainwp-auth",level:3},{value:"Public proxy pattern",id:"public-proxy-pattern",level:2}];function o(e){let s={a:"a",admonition:"admonition",code:"code",details:"details",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",summary:"summary",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,d.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(s.header,{children:(0,t.jsx)(s.h1,{id:"admin-endpoints",children:"Admin endpoints"})}),"\n",(0,t.jsxs)(s.p,{children:["These endpoints power the Burst Statistics admin dashboard. They are not intended as a primary integration API \u2014 use ",(0,t.jsx)(s.a,{href:"/docs/rest-api/data-endpoints",children:(0,t.jsx)(s.code,{children:"burst/v1/data/{type}"})})," for analytics data instead."]}),"\n",(0,t.jsxs)(s.p,{children:["All routes require a valid ",(0,t.jsx)(s.code,{children:"burst_nonce"})," nonce on write requests. Pass it via the ",(0,t.jsx)(s.code,{children:"X-WP-Nonce"})," header."]}),"\n",(0,t.jsx)(s.h2,{id:"settings-and-fields",children:"Settings and fields"}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1fieldsget",children:(0,t.jsx)(s.code,{children:"GET burst/v1/fields/get"})}),"\n",(0,t.jsxs)(s.p,{children:["Returns all field definitions and the full menu configuration in a single response. Menu data is not exposed via a dedicated route \u2014 read it from the ",(0,t.jsx)(s.code,{children:"menu"})," field of this response."]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1fieldsset",children:(0,t.jsx)(s.code,{children:"POST burst/v1/fields/set"})}),"\n",(0,t.jsx)(s.p,{children:"Saves multiple settings fields at once."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required"]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1optionsset",children:(0,t.jsx)(s.code,{children:"POST burst/v1/options/set"})}),"\n",(0,t.jsx)(s.p,{children:"Saves a single option value."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required"]}),"\n",(0,t.jsxs)(s.p,{children:["Saving an option does not trigger the ",(0,t.jsx)(s.code,{children:"maybe_redirect_to_settings_page"})," hook from this route; that side effect lives on the frontend ",(0,t.jsx)(s.code,{children:"admin_init"})," flow."]}),"\n",(0,t.jsx)(s.h2,{id:"goals",children:"Goals"}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1goalsget",children:(0,t.jsx)(s.code,{children:"GET burst/v1/goals/get"})}),"\n",(0,t.jsx)(s.p,{children:"Returns all configured goals, the list of predefined goals available to add, the goal field definitions and the current goal-limit state."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"view_burst_statistics"})]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Response on success:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Field"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"request_success"})}),(0,t.jsx)(s.td,{children:"bool"}),(0,t.jsx)(s.td,{children:"True on success"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"goals"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsx)(s.td,{children:"All configured goals"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"predefinedGoals"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsx)(s.td,{children:"Predefined goals available to add"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"goalFields"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsx)(s.td,{children:"Goal field definitions"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"goal_limit"})}),(0,t.jsx)(s.td,{children:"int"}),(0,t.jsxs)(s.td,{children:["Maximum number of active goals allowed. ",(0,t.jsx)(s.code,{children:"-1"})," when a valid Pro license unlocks unlimited goals, otherwise the free limit of ",(0,t.jsx)(s.code,{children:"3"})]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"active_goals_count"})}),(0,t.jsx)(s.td,{children:"int"}),(0,t.jsxs)(s.td,{children:["Number of goals currently in the ",(0,t.jsx)(s.code,{children:"active"})," status"]})]})]})]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1goalsadd",children:(0,t.jsx)(s.code,{children:"POST burst/v1/goals/add"})}),"\n",(0,t.jsx)(s.p,{children:"Creates a new goal."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required"]}),"\n",(0,t.jsxs)(s.p,{children:["Returns ",(0,t.jsx)(s.code,{children:"400"})," with ",(0,t.jsx)(s.code,{children:"success: false"})," and a message when the goal could not be saved \u2014 for example when the active goal limit has been reached on a free install."]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1goalsset",children:(0,t.jsx)(s.code,{children:"POST burst/v1/goals/set"})}),"\n",(0,t.jsx)(s.p,{children:"Updates an existing goal."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required"]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1goalsupsert_for_block",children:(0,t.jsx)(s.code,{children:"POST burst/v1/goals/upsert_for_block"})}),"\n",(0,t.jsxs)(s.p,{children:["Creates or updates a goal from the block editor. Blocks store a unique identifier (",(0,t.jsx)(s.code,{children:"uid"}),") in a block attribute; this endpoint resolves the goal idempotently by that ",(0,t.jsx)(s.code,{children:"uid"})," (via its ",(0,t.jsx)(s.code,{children:'[data-burst-goal="<uid>"]'})," selector), falling back to an explicit ",(0,t.jsx)(s.code,{children:"id"}),"/",(0,t.jsx)(s.code,{children:"goal_id"}),". The ",(0,t.jsx)(s.code,{children:"uid"})," owns the selector \u2014 a client-supplied ",(0,t.jsx)(s.code,{children:"selector"}
1)," is ignored whenever a ",(0,t.jsx)(s.code,{children:"uid"})," is present. Goals created here are marked as block goals and default to the ",(0,t.jsx)(s.code,{children:"active"})," status."]}),"\n",(0,t.jsxs)(s.p,{children:["Use this route instead of ",(0,t.jsx)(s.code,{children:"goals/add"})," for block-editor integrations: it enforces the server-side active-goal limit, so a new goal is rejected on a free install once the limit is reached rather than being silently created."]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required (",(0,t.jsx)(s.code,{children:"burst_nonce"}),", passed in the request body)"]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Body parameters:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Parameter"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Required"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"uid"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"Conditional"}),(0,t.jsxs)(s.td,{children:["Unique block identifier. Sanitised with ",(0,t.jsx)(s.code,{children:"sanitize_key"}),". Resolves an existing goal and builds the ",(0,t.jsx)(s.code,{children:'[data-burst-goal="<uid>"]'})," selector. Required to create a new block/element goal \u2014 a create with no resolvable goal and no ",(0,t.jsx)(s.code,{children:"uid"})," is rejected with ",(0,t.jsx)(s.code,{children:"400"})]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"id"})," / ",(0,t.jsx)(s.code,{children:"goal_id"})]}),(0,t.jsx)(s.td,{children:"int"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsxs)(s.td,{children:["Existing goal id, used only when no ",(0,t.jsx)(s.code,{children:"uid"})," match is found"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"title"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"Conditional"}),(0,t.jsx)(s.td,{children:"Goal title. Required when creating a new goal"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"status"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"active"})," (default), ",(0,t.jsx)(s.code,{children:"inactive"}),", or ",(0,t.jsx)(s.code,{children:"delete"})," to remove the goal. A create is only allowed when ",(0,t.jsx)(s.code,{children:"status"})," is ",(0,t.jsx)(s.code,{children:"active"})," and a ",(0,t.jsx)(s.code,{children:"title"})," is present"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"type"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsx)(s.td,{children:"Goal type"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"page_or_website"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"website"})," or ",(0,t.jsx)(s.code,{children:"page"})]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"specific_page"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsxs)(s.td,{children:["Relative page URL when ",(0,t.jsx)(s.code,{children:"page_or_website"})," is ",(0,t.jsx)(s.code,{children:"page"})]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"conversion_metric"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsx)(s.td,{children:"Conversion metric"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"page_id"})}),(0,t.jsx)(s.td,{children:"int"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsx)(s.td,{children:"Post ID the goal is attached to"})]})]})]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Response on success:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Field"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"success"})}),(0,t.jsx)(s.td,{children:"bool"}),(0,t.jsx)(s.td,{children:"True on success"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"goal_id"})}),(0,t.jsx)(s.td,{children:"int"}),(0,t.jsx)(s.td,{children:"The created or updated goal id"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"goal"})}),(0,t.jsx)(s.td,{children:"object"}),(0,t.jsx)(s.td,{children:"The saved goal"})]})]})]}),"\n",(0,t.jsxs)(s.p,{children:["A create requires a ",(0,t.jsx)(s.code,{children:"uid"}),": when no goal resolves (no ",(0,t.jsx)(s.code,{children:"uid"})," match and no ",(0,t.jsx)(s.code,{children:"id"}),"/",(0,t.jsx)(s.code,{children:"goal_id"})," match) and no ",(0,t.jsx)(s.code,{children:"uid"})," is supplied, the endpoint returns ",(0,t.jsx)(s.code,{children:"400"})," with ",(0,t.jsx)(s.code,{children:"success: false"})," and ",(0,t.jsx)(s.code,{children:"Goal UID is required."}),". When the requested goal does not exist and the payload is a partial update (no ",(0,t.jsx)(s.code,{children:"active"})," status or no title), the endpoint returns ",(0,t.jsx)(s.code,{children:"404"})," with ",(0,t.jsx)(s.code,{children:"success: false"}),", a ",(0,t.jsx)(s.code,{children:"goal_not_found"})," code and ",(0,t.jsx)(s.code,{children:"Goal not found."}),". When a new goal would exceed the free active-goal limit, it returns ",(0,t.jsx)(s.code,{children:"success: false"})," with an upgrade message."]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1goalsdelete",children:(0,t.jsx)(s.code,{children:"POST burst/v1/goals/delete"})}),"\n",(0,t.jsxs)(s.p,{children:["Deletes a goal. Requires a non-zero integer ",(0,t.jsx)(s.code,{children:"id"}),";
1 an invalid or missing id returns ",(0,t.jsx)(s.code,{children:"success: false"})," with ",(0,t.jsx)(s.code,{children:"Invalid goal ID."}),"."]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required"]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1goalsadd_predefined",children:(0,t.jsx)(s.code,{children:"POST burst/v1/goals/add_predefined"})}),"\n",(0,t.jsxs)(s.p,{children:["Adds one of the built-in predefined goals. Returns ",(0,t.jsx)(s.code,{children:"400"})," when no valid predefined goal id resolves \u2014 for example when the active goal limit has been reached."]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required"]}),"\n",(0,t.jsx)(s.h2,{id:"posts-search",children:"Posts search"}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1posts",children:(0,t.jsx)(s.code,{children:"GET burst/v1/posts/"})}),"\n",(0,t.jsx)(s.p,{children:"Searches WordPress posts for use in goal or filter selectors."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Query parameters:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Parameter"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Required"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsx)(s.tbody,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"search_input"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsxs)(s.td,{children:["Search term. Sanitised with ",(0,t.jsx)(s.code,{children:"sanitize_title"})]})]})})]}),"\n",(0,t.jsx)(s.h2,{id:"generic-action-endpoints",children:"Generic action endpoints"}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1do_actionaction",children:(0,t.jsx)(s.code,{children:"POST burst/v1/do_action/{action}"})}),"\n",(0,t.jsxs)(s.p,{children:["Generic write-action endpoint. The ",(0,t.jsx)(s.code,{children:"{action}"})," segment selects the operation. Use ",(0,t.jsx)(s.code,{children:"burst_do_action"})," to register custom write operations."]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required\n",(0,t.jsx)(s.strong,{children:"Route parameter:"})," ",(0,t.jsx)(s.code,{children:"{action}"})," \u2014 ",(0,t.jsx)(s.code,{children:"[a-z_-]+"})]}),"\n",(0,t.jsx)(s.p,{children:"Built-in actions:"}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:(0,t.jsx)(s.code,{children:"{action}"})}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"plugin_actions"})}),(0,t.jsx)(s.td,{children:"Install or activate companion plugins"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"fix_task"})}),(0,t.jsx)(s.td,{children:"Apply an automated fix for a dashboard task"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"dismiss_task"})}),(0,t.jsx)(s.td,{children:"Dismiss a task from the dashboard"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"save_pinned_filters"})}),(0,t.jsxs)(s.td,{children:["Persist the current user's pinned dashboard filters to user meta. Accepts a ",(0,t.jsx)(s.code,{children:"filters"})," object in the request body; each key is sanitised with ",(0,t.jsx)(s.code,{children:"sanitize_key"})," and kept only when it matches a registered filter key (plus ",(0,t.jsx)(s.code,{children:"source_category"}),"), each value is sanitised with ",(0,t.jsx)(s.code,{children:"sanitize_text_field"}),". An empty or missing ",(0,t.jsx)(s.code,{children:"filters"})," payload clears the stored value. Returns ",(0,t.jsx)(s.code,{children:"success"})," and the resolved ",(0,t.jsx)(s.code,{children:"pinned_filters"})," (an object, empty when none are stored)"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"chat"})}),(0,t.jsxs)(s.td,{children:["AJAX fallback for the chat endpoint. Accepts the same ",(0,t.jsx)(s.code,{children:"message"}),", ",(0,t.jsx)(s.code,{children:"history"})," and ",(0,t.jsx)(s.code,{children:"model"})," payload as ",(0,t.jsx)(s.code,{children:"POST burst/v1/chat"})," and returns the same response shape."]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"gsc_connect"})}),(0,t.jsxs)(s.td,{children:["Start a Google Search Console connect attempt. Returns the relay ",(0,t.jsx)(s.code,{children:"/start"})," URL for the OAuth popup, or an error such as ",(0,t.jsx)(s.code,{children:"locked"})," when another admin is mid-connect"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"gsc_cancel"})}),(0,t.jsx)(s.td,{children:"Release the current user's in-flight connect lock"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"gsc_disconnect"})}),(0,t.jsx)(s.td,{children:"Revoke and delete the stored Google Search Console tokens"})]})]})]}),"\n",(0,t.jsxs)(s.p,{children:["The ",(0,t.jsx)(s.code,{children:"gsc_*"})," actions are only registered when the ",(0,t.jsx)(s.code,{children:"enable_search_console"})," setting is on, and each additionally requires the ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})," capability."]}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1get_actionaction",children:(0,t.jsx)(s.code,{children:"GET burst/v1/get_action/{action}"})}),"\n",(0,t.jsxs)(s.p,{children:["Generic read-action endpoint. Use ",(0,t.jsx)(s.code,{children:"burst_get_action"})," to register custom read operations."]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"view_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required\n",(0,t.jsx)(s.strong,{children:"Route parameter:"})," ",(0,t.jsx)(s.code,{children:"{action}"})," \u2014 ",(0,t.jsx)(s.code,{children:"[a-z_-]+"})]}),"\n",(0,t.jsx)(s.p,{children:"Built-in actions:"}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:(0,t.jsx)(s.code,{children:"{action}"})}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"tasks"})}),(0,t.jsx)(s.td,{children:"Returns the list of plugin tasks"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"tracking"})}),(0,t.jsxs)(s.td,{children:["Returns tracking status and last-test timestamp. The status comes from the loopback probe, but that probe is unreliable (a server cannot al
1ways reach itself, and firewalls, CDNs or staging auth can block it), so a probe ",(0,t.jsx)(s.code,{children:"error"})," is downgraded to ",(0,t.jsx)(s.code,{children:"recording"})," whenever hits are still being recorded \u2014 only a genuine absence of recent hits is reported as an error"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"get_article_data"})}),(0,t.jsx)(s.td,{children:"Returns per-article statistics"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"get_filter_options"})}),(0,t.jsx)(s.td,{children:"Returns available filter values (devices, browsers, countries, etc.)"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"otherpluginsdata"})}),(0,t.jsx)(s.td,{children:"Returns data from integrated third-party plugins"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"story-report-data"})}),(0,t.jsx)(s.td,{children:"Returns the report payload for a shared story link. Requires a valid share token."})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"chat_status"})}),(0,t.jsxs)(s.td,{children:["Returns the chat availability payload (",(0,t.jsx)(s.code,{children:"enabled"}),", ",(0,t.jsx)(s.code,{children:"abilities_enabled"}),", ",(0,t.jsx)(s.code,{children:"ai_client_loaded"}),", ",(0,t.jsx)(s.code,{children:"has_configured_provider"}),", ",(0,t.jsx)(s.code,{children:"missing_approvals"}),")."]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"chat_models"})}),(0,t.jsxs)(s.td,{children:["Returns the available AI provider models for the chat endpoint (",(0,t.jsx)(s.code,{children:"models"}),", ",(0,t.jsx)(s.code,{children:"default"}),")."]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"get_gsc_status"})}),(0,t.jsxs)(s.td,{children:["Returns the Google Search Console connection state (",(0,t.jsx)(s.code,{children:"status"}),", ",(0,t.jsx)(s.code,{children:"connect_failed"}),", and when connected ",(0,t.jsx)(s.code,{children:"property"})," and ",(0,t.jsx)(s.code,{children:"property_status"}),"). Lives on the view-gated endpoint so a viewer can poll the connection state without the manage capability the connect/disconnect actions require."]})]})]})]}),"\n",(0,t.jsxs)(s.p,{children:["The ",(0,t.jsx)(s.code,{children:"get_gsc_status"})," action is only registered when the ",(0,t.jsx)(s.code,{children:"enable_search_console"})," setting is on."]}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1get_actionecommerceaction",children:(0,t.jsx)(s.code,{children:"GET burst/v1/get_action/ecommerce/{action}"})}),"\n",(0,t.jsxs)(s.p,{children:["Ecommerce-scoped read-action endpoint. Mirrors ",(0,t.jsx)(s.code,{children:"GET burst/v1/get_action/{action}"})," but gates access on the sales capability so ecommerce integrations can expose read operations without widening the generic route."]}),"\n",(0,t.jsx)(s.admonition,{type:"pro",proTier:"business",children:(0,t.jsxs)(s.p,{children:["Ecommerce endpoints require a ",(0,t.jsx)(s.a,{href:"https://burst-statistics.com/pricing/",children:"Business tier"})," license."]})}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"view_sales_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required\n",(0,t.jsx)(s.strong,{children:"Route parameter:"})," ",(0,t.jsx)(s.code,{children:"{action}"})," \u2014 ",(0,t.jsx)(s.code,{children:"[a-z_-]+"})]}),"\n",(0,t.jsxs)(s.p,{children:["Use ",(0,t.jsx)(s.code,{children:"burst_get_action"})," to register handlers \u2014 ",(0,t.jsx)(s.code,{children:"{action}"})," is passed through to the same filter used by the generic read endpoint, so existing handlers work as-is when the action name is unique."]}),"\n",(0,t.jsxs)(s.details,{className:"code-collapse",children:[(0,t.jsx)(s.summary,{children:"Show code"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-php",children:"add_action( 'burst_get_action', function( array $output, string $action ): array {\n    if ( $action === 'my_ecommerce_report' ) {\n        $output = [ 'items' => get_my_ecommerce_report() ];\n    }\n    return $output;\n}, 10, 2 );\n"})})]}),"\n",(0,t.jsxs)(s.h2,{id:"extending-do_action-and-get_action",children:["Extending ",(0,t.jsx)(s.code,{children:"do_action"})," and ",(0,t.jsx)(s.code,{children:"get_action"})]}),"\n",(0,t.jsx)(s.h3,{id:"burst_do_action",children:(0,t.jsx)(s.code,{children:"burst_do_action"})}),"\n",(0,t.jsxs)(s.p,{children:["Fires inside ",(0,t.jsx)(s.code,{children:"POST burst/v1/do_action/{action}"}),". Use it to handle custom write operations."]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Parameters:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Parameter"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"$output"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsx)(s.td,{children:"Current response array. Modify and return it"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"$action"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsxs)(s.td,{children:["The ",(0,t.jsx)(s.code,{children:"{action}"})," segment from the route"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"$data"})}),(0,t.jsx)(s.td,{children:"array / null"}),(0,t.jsx)(s.td,{children:"Decoded JSON body of the request"})]})]})]}),"\n",(0,t.jsxs)(s.details,{className:"code-collapse",children:[(0,t.jsx)(s.summary,{children:"Show code"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-php",children:"add_action( 'burst_do_action', function( array $output, string $action, ?array $data ): array {\n    if ( $action === 'my_custom_action' ) {\n        // Perform write operation.\n        $output = [ 'success' => true, 'message' => 'Done.' ];\n    }\n    return $output;\n}, 10, 3 );\n"})})]}),"\n",(0,t.jsx)(s.h3,{id:"burst_get_action",children:(0,t.jsx)(s.code,{children:"burst_get_action"})}),"\n",(0,t.jsxs)(s.p,{children:["Fires inside ",(0,t.jsx)(s.code,{children:"GET burst/v1/get_action/{action}"})," and ",(0,t.jsx)(s.code,{children:"GET burst/v1/get_action/ecommerce/{action}"}),". Use it to handle custom read operations."]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Parameters:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Parameter"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"$output"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsx)(s.td,{children:"Current response array. Modify and return it"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"$action"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsxs)(s.td,{children:["The ",(0,t.jsx)(s.code,{children:"{action}"})," segment from the route"]})]})]})]}),"\n",(0,t.jsxs)(s.details,{className:"code-collapse",children:[(0,t.jsx)(s.summary,{children:"Show code"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-php",children:"add_action( 'burst_get_action', function( array $output, string $action ): array {\n    if ( $action === 'my_custom_data' ) {\n        $output = [ 'items' => get_my_custom_data() ];\n    }\n    return $output;\n}, 10, 2 );\n"})})]}),"\n",(0,t.jsx)(s.h2,{id:"abilities-api",children:"Abilities API"}),"\n",(0,t.jsxs)(s.p,{children:["Burst registers a set of read-only abilities through WordPress's ",(0,t.jsx)(s.a,{href:"https://developer.wordpress.org/",children:"Abilities API"})," (",(0,t.jsx)(s.code,{children:"wp_register_ability"}
1),") so trusted AI agents and automation tools can query analytics without going through the dashboard REST routes. Abilities are opt-in and disabled by default."]}),"\n",(0,t.jsx)(s.h3,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(s.ul,{children:["\n",(0,t.jsxs)(s.li,{children:["WordPress build that exposes ",(0,t.jsx)(s.code,{children:"wp_register_ability"})," and ",(0,t.jsx)(s.code,{children:"wp_register_ability_category"})]}),"\n",(0,t.jsxs)(s.li,{children:["The ",(0,t.jsx)(s.code,{children:"enable_abilities_api"})," setting must be turned on under ",(0,t.jsx)(s.strong,{children:"Burst \u2192 Settings \u2192 Advanced"})]}),"\n",(0,t.jsxs)(s.li,{children:["A logged-in user with ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})]}),"\n"]}),"\n",(0,t.jsxs)(s.p,{children:["When the Abilities API functions are not available, the ",(0,t.jsx)(s.code,{children:"enable_abilities_api"})," field is hidden from the settings UI."]}),"\n",(0,t.jsx)(s.h3,{id:"registered-abilities",children:"Registered abilities"}),"\n",(0,t.jsxs)(s.p,{children:["All abilities live under the ",(0,t.jsx)(s.code,{children:"burst-statistics"})," ability category and are flagged ",(0,t.jsx)(s.code,{children:"readonly"}),", ",(0,t.jsx)(s.code,{children:"idempotent"}),", and non-destructive."]}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Ability"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/live-visitors"})}),(0,t.jsx)(s.td,{children:"Current number of live visitors"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/live-traffic"})}),(0,t.jsxs)(s.td,{children:["Active visitors and pages from the live traffic feed (accepts ",(0,t.jsx)(s.code,{children:"limit"}),", max 100)"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/today-summary"})}),(0,t.jsxs)(s.td,{children:["Summary of key metrics for an optional ",(0,t.jsx)(s.code,{children:"date_start"}),"/",(0,t.jsx)(s.code,{children:"date_end"})," range"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/tasks"})}),(0,t.jsx)(s.td,{children:"Current Burst task list and status"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/tracking-status"})}),(0,t.jsx)(s.td,{children:"Tracking transport status and last test timestamp"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/license-notices"})}),(0,t.jsx)(s.td,{children:"License state and notices for Burst Pro"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/data"})}),(0,t.jsxs)(s.td,{children:["Pages overview (",(0,t.jsx)(s.code,{children:"type=insights"}),") or datatable data (",(0,t.jsx)(s.code,{children:"type=datatable"}),") with metrics, filters, group-by and limit"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/sales-data"})}),(0,t.jsx)(s.td,{children:"Ecommerce sales metrics (Pro only)"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst/subscriptions-data"})}),(0,t.jsx)(s.td,{children:"Ecommerce subscriptions metrics (Pro only)"})]})]})]}),"\n",(0,t.jsx)(s.admonition,{type:"pro",proTier:"business",children:(0,t.jsxs)(s.p,{children:["The ",(0,t.jsx)(s.code,{children:"burst/sales-data"})," and ",(0,t.jsx)(s.code,{children:"burst/subscriptions-data"})," abilities require a ",(0,t.jsx)(s.a,{href:"https://burst-statistics.com/pricing/",children:"Business tier"})," license. They return ",(0,t.jsx)(s.code,{children:"503 burst_abilities_pro_required"})," when called on a free install."]})}),"\n",(0,t.jsxs)(s.h3,{id:"querying-datatables-with-burstdata",children:["Querying datatables with ",(0,t.jsx)(s.code,{children:"burst/data"})]}),"\n",(0,t.jsxs)(s.p,{children:["The ",(0,t.jsx)(s.code,{children:"burst/data"})," ability returns either a pages overview or a registered datatable, selected with the ",(0,t.jsx)(s.code,{children:"type"})," argument:"]}),"\n",(0,t.jsxs)(s.ul,{children:["\n",(0,t.jsxs)(s.li,{children:[(0,t.jsx)(s.code,{children:"type=insights"})," returns the pages overview (defaults to the ",(0,t.jsx)(s.code,{children:"pageviews"})," metric grouped by ",(0,t.jsx)(s.code,{children:"page_url"}),")"]}),"\n",(0,t.jsxs)(s.li,{children:[(0,t.jsx)(s.code,{children:"type=datatable"})," returns a registered datatable, selected with the required ",(0,t.jsx)(s.code,{children:"datatable_id"})," argument"]}),"\n"]}),"\n",(0,t.jsxs)(s.p,{children:["When ",(0,t.jsx)(s.code,{children:"type=datatable"}),", ",(0,t.jsx)(s.code,{children:"datatable_id"})," must be one of the registered IDs below. Each datatable has a default metric set and group-by that are applied when ",(0,t.jsx)(s.code,{children:"metrics"})," or ",(0,t.jsx)(s.code,{children:"group_by"})," are omitted:"]}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:(0,t.jsx)(s.code,{children:"datatable_id"})}),(0,t.jsx)(s.th,{children:"Default metrics"}),(0,t.jsx)(s.th,{children:"Default group-by"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"statistics_pages"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"pageviews"}),", ",(0,t.jsx)(s.code,{children:"visitors"}),", ",(0,t.jsx)(s.code,{children:"sessions"}),", ",(0,t.jsx)(s.code,{children:"bounce_rate"}),", ",(0,t.jsx)(s.code,{children:"avg_time_on_page"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"page_url"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"statistics_parameters"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"visitors"}),", ",(0,t.jsx)(s.code,{children:"sessions"}),", ",(0,t.jsx)(s.code,{children:"bounce_rate"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"parameter"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"statistics_referrers"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"visitors"}),", ",(0,t.jsx)(s.code,{children:"sessions"}),", ",(0,t.jsx)(s.code,{children:"bounce_rate"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"referrer"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"sources_countries"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"visitors"}),", ",(0,t.jsx)(s.code,{children:"sessions"}),", ",(0,t.jsx)(s.code,{children:"bounce_rate"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"country_code"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"sources_campaigns"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"visitors"}),", ",(0,t.jsx)(s.code,{children:"bounce_rate"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"campaign"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"sources_referrers"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"visitors"}),", ",(0,t.jsx)(s.code,{children:"sessions"}),", ",(0,t.jsx)(s.code,{children:"bounce_rate"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"referrer"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"outgoing-links"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"clicks"}),", ",(0,t.jsx)(s.code,{children:"previous_clicks"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"url"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"search-terms"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"volume"}),", ",(0,t.jsx)(s.code,{children:"results"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"term"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"forms"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"submissions"}),", ",(0,t.jsx)(s.code,{children:"pageviews"}),", ",(0,t.jsx)(s.code,{children:"conversion_rate"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"form_id"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"sales_products"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"sales"}),", ",(0,t.jsx)(s.code,{children:"revenue"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"product"})})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"subscription_products"})}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"active_subscribers"}),", ",(0,t.jsx)(s.code,{children:"monthly_recurring_revenue"})]}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"plan"})})]})]})]}),"\n",(0,t.jsxs)(s.p,{children:["Datatable access is gated per user. The ability resolves access through the same ",(0,t.jsx)(s.code,{children:"App::user_can_access_datatable()"})," check that drives the data REST endpoints, so a caller that lacks the capability for the requested datatable receives ",(0,t.jsx)(s.code,{children:"403 rest_forbidden"}),". Any ",(0,t.jsx)(s.code,{children:"metrics"})," passed by the caller are intersected with the datatable's allow-list, so unknown or unauthorized metrics are dropped rather than queried."]}),"\n",(0,t.jsx)(s.admonition,{type:"pro",proTier:"business",children:(0,t.jsxs)(s.p,{children:["The ",(0,t.jsx)(s.code,{children:"sales_products"})," and ",(0,t.jsx)(s.code,{children:"subscription_products"})," datatables require a ",(0,t.jsx)(s.a,{href:"https://burst-statistics.com/pricing/",children:"Business tier"})," license and resolve through the sales capability."]})}),"\n",(0,t.jsx)(s.h3,{id:"permission-and-rate-limiting",children:"Permission and rate limiting"}),"\n",(0,t.jsxs)(s.p,{children:["Every ability runs through a single permission callback that requires the current user to hold ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),". Anonymous callers and users without the capability receive ",(0,t.jsx)(s.code,{children:"403 burst_abilities_forbidden"}),"."]}),"\n",(0,t.jsxs)(s.p,{children:["The permission callback was tightened from ",(0,t.jsx)(s.code,{children:"view_burst_statistics"})," to ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),". Update integration users so they hold the manage capability before upgrading."]}),"\n",(0,t.jsxs)(s.p,{children:["A per-user, per-ability rate limit caps requests at 30 per 60-second window by default. When the limit is exceeded the ability returns ",(0,t.jsx)(s.code,{children:"429 burst_abilities_rate_limited"}
1),". Both bounds are filterable."]}),"\n",(0,t.jsxs)(s.details,{className:"code-collapse",children:[(0,t.jsx)(s.summary,{children:"Show code"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-php",children:"// Allow 60 requests per 30-second window for the live-visitors ability.\nadd_filter( 'burst_abilities_rate_limit_max', function( int $max, string $ability ): int {\n    return $ability === 'live-visitors' ? 60 : $max;\n}, 10, 2 );\n\nadd_filter( 'burst_abilities_rate_limit_window', function( int $window, string $ability ): int {\n    return $ability === 'live-visitors' ? 30 : $window;\n}, 10, 2 );\n"})})]}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Filter"}),(0,t.jsx)(s.th,{children:"Default"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_rate_limit_max"})}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"30"})}),(0,t.jsx)(s.td,{children:"Maximum requests per window. Receives the ability slug as the second argument"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_rate_limit_window"})}),(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"60"})}),(0,t.jsx)(s.td,{children:"Window length in seconds. Receives the ability slug as the second argument"})]})]})]}),"\n",(0,t.jsx)(s.h3,{id:"error-codes",children:"Error codes"}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Code"}),(0,t.jsx)(s.th,{children:"HTTP status"}),(0,t.jsx)(s.th,{children:"Cause"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_forbidden"})}),(0,t.jsx)(s.td,{children:"403"}),(0,t.jsxs)(s.td,{children:["User is not logged in or lacks ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"rest_forbidden"})}),(0,t.jsx)(s.td,{children:"403"}),(0,t.jsxs)(s.td,{children:["Caller lacks the capability for the requested ",(0,t.jsx)(s.code,{children:"datatable_id"})]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_invalid_input"})}),(0,t.jsx)(s.td,{children:"400"}),(0,t.jsx)(s.td,{children:"Input failed schema/type validation"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_rate_limited"})}),(0,t.jsx)(s.td,{children:"429"}),(0,t.jsx)(s.td,{children:"Per-user rate limit exceeded"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_pro_required"})}),(0,t.jsx)(s.td,{children:"503"}),(0,t.jsx)(s.td,{children:"Pro-only ability invoked on a free install"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_unavailable"})}),(0,t.jsx)(s.td,{children:"503"}),(0,t.jsx)(s.td,{children:"Burst admin services could not be bootstrapped for the ability call"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_abilities_execution_failed"})}),(0,t.jsx)(s.td,{children:"500"}),(0,t.jsx)(s.td,{children:"Underlying statistics call threw \u2014 check server logs"})]})]})]}),"\n",(0,t.jsx)(s.h2,{id:"chat",children:"Chat"}),"\n",(0,t.jsxs)(s.p,{children:["The chat endpoints expose the Burst Analytics assistant \u2014 a WordPress AI Client integration that resolves ability calls (live visitors, today summary, datatables, etc.) into natural-language answers. The same handlers are reachable from REST and from the ",(0,t.jsx)(s.code,{children:"do_action"})," / ",(0,t.jsx)(s.code,{children:"get_action"})," fallback channel so the dashboard can call them through both transports."]}),"\n",(0,t.jsx)(s.h3,{id:"prerequisites-1",children:"Prerequisites"}),"\n",(0,t.jsxs)(s.ul,{children:["\n",(0,t.jsxs)(s.li,{children:["Abilities API enabled (",(0,t.jsx)(s.code,{children:"enable_abilities_api"})," setting turned on)"]}),"\n",(0,t.jsx)(s.li,{children:"WordPress AI Client active and at least one provider configured (Anthropic, OpenAI, or Google)"}),"\n",(0,t.jsxs)(s.li,{children:["A logged-in user with ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})]}),"\n"]}),"\n",(0,t.jsxs)(s.p,{children:["When any prerequisite is missing, the chat endpoints return a structured error and ",(0,t.jsx)(s.code,{children:"GET burst/v1/chat/status"})," reports the blocking flags so the dashboard can explain why the chat is unavailable."]}),"\n",(0,t.jsx)(s.h3,{id:"post-burstv1chat",children:(0,t.jsx)(s.code,{children:"POST burst/v1/chat"})}),"\n",(0,t.jsx)(s.p,{children:"Sends a user message to the assistant, optionally with prior conversation history. The handler primes the configured AI provider, lets the model issue function calls into the registered Burst abilities, and returns the final assistant reply along with the serialized conversation history so the dashboard can persist it client-side."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),"\n",(0,t.jsx)(s.strong,{children:"Nonce:"})," required\n",(0,t.jsx)(s.strong,{children:"Rate limit:"})," 20 requests per 60-second window per user (",(0,t.jsx)(s.code,{children:"burst_chat_rate_limit_max"})," / ",(0,t.jsx)(s.code,{children:"burst_chat_rate_limit_window"})," filters)"]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Body parameters:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Parameter"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Required"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"message"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"Yes"}),(0,t.jsxs)(s.td,{children:["User message. Sanitised with ",(0,t.jsx)(s.code,{children:"sanitize_textarea_field"})," and clamped to 8000 characters (",(0,t.jsx)(s.code,{children:"burst_chat_prompt_max_length"})," filter)"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"history"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsx)(s.td,{children:"No"}
1),(0,t.jsxs)(s.td,{children:["Prior conversation. Each item must be an object with ",(0,t.jsx)(s.code,{children:"role"})," (",(0,t.jsx)(s.code,{children:"user"})," or ",(0,t.jsx)(s.code,{children:"model"}),") and either ",(0,t.jsx)(s.code,{children:"content"})," or ",(0,t.jsx)(s.code,{children:"parts"}),". Capped at 40 items by default (",(0,t.jsx)(s.code,{children:"burst_chat_history_max_items"})," filter)"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"model"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"No"}),(0,t.jsxs)(s.td,{children:["Provider model id to use for this request. Sanitised with ",(0,t.jsx)(s.code,{children:"sanitize_text_field"}),". When empty, the configured provider preference is used. Retrieve valid ids from ",(0,t.jsx)(s.code,{children:"GET burst/v1/chat/models"})]})]})]})]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Response on success:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Field"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"reply"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"Assistant reply text. Any internal telemetry block the model appends is stripped before the reply is returned"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"history"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsx)(s.td,{children:"Updated conversation history including the new user and model messages, serialized via the AI Client DTOs"})]})]})]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Error codes:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Code"}),(0,t.jsx)(s.th,{children:"HTTP status"}),(0,t.jsx)(s.th,{children:"Cause"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_chat_forbidden"})}),(0,t.jsx)(s.td,{children:"403"}),(0,t.jsx)(s.td,{children:"No logged-in user resolved for rate limiting"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_chat_invalid_prompt"})}),(0,t.jsx)(s.td,{children:"400"}),(0,t.jsxs)(s.td,{children:[(0,t.jsx)(s.code,{children:"message"})," was empty after sanitisation"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_chat_invalid_history"})}),(0,t.jsx)(s.td,{children:"400"}),(0,t.jsx)(s.td,{children:"A history item had an unsupported role or malformed parts"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_chat_unavailable"})}),(0,t.jsx)(s.td,{children:"403"}),(0,t.jsx)(s.td,{children:"Chat is unavailable \u2014 the Abilities API is disabled, no AI provider is configured, or required connector approvals are missing"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_chat_rate_limited"})}),(0,t.jsx)(s.td,{children:"429"}),(0,t.jsx)(s.td,{children:"Per-user chat rate limit exceeded"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_ai_client_unavailable"})}),(0,t.jsx)(s.td,{children:"503"}),(0,t.jsx)(s.td,{children:"WordPress AI Client classes or functions are missing"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_ai_client_empty_response"})}),(0,t.jsx)(s.td,{children:"502"}),(0,t.jsx)(s.td,{children:"Provider returned no usable text after tool resolution"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"burst_ai_client_exception"})}),(0,t.jsx)(s.td,{children:"500 / 503"}),(0,t.jsxs)(s.td,{children:["Provider call threw. The ",(0,t.jsx)(s.code,{children:"diagnostics"})," field on the error data lists which providers are loaded and configured. Set the ",(0,t.jsx)(s.code,{children:"burst_chat_expose_exception"})," filter to ",(0,t.jsx)(s.code,{children:"true"})," to include the exception message"]})]})]})]}),"\n",(0,t.jsxs)(s.details,{className:"code-collapse",children:[(0,t.jsx)(s.summary,{children:"Show code"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-php",children:"// Allow a higher chat rate for trusted automations.\nadd_filter( 'burst_chat_rate_limit_max', function( int $max ): int {\n    return 60;\n} );\n\nadd_filter( 'burst_chat_rate_limit_window', function( int $window ): int {\n    return 30;\n} );\n"})})]}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1chatmodels",children:(0,t.jsx)(s.code,{children:"GET burst/v1/chat/models"})}),"\n",(0,t.jsx)(s.p,{children:"Returns the AI provider models available for the chat endpoint so the dashboard can render a model picker. Only providers with an API key configured are listed, and only models that support text generation are included."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Response fields:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Field"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"models"})}),(0,t.jsx)(s.td,{children:"array"}),(0,t.jsxs)(s.td,{children:["Available models. Each item is an object with ",(0,t.jsx)(s.code,{children:"id"}
1)," (provider model id), ",(0,t.jsx)(s.code,{children:"label"})," (display name) and ",(0,t.jsx)(s.code,{children:"provider"})," (provider name such as ",(0,t.jsx)(s.code,{children:"Anthropic"}),", ",(0,t.jsx)(s.code,{children:"OpenAI"})," or ",(0,t.jsx)(s.code,{children:"Google"}),")"]})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"default"})}),(0,t.jsx)(s.td,{children:"object / null"}),(0,t.jsx)(s.td,{children:"The default model, resolved from the configured provider preference list and falling back to the first available model. Null when no model is available"})]})]})]}),"\n",(0,t.jsxs)(s.p,{children:["Pass a returned ",(0,t.jsx)(s.code,{children:"id"})," as the ",(0,t.jsx)(s.code,{children:"model"})," parameter on ",(0,t.jsx)(s.code,{children:"POST burst/v1/chat"})," to pin a request to a specific model. The same payload is returned by the ",(0,t.jsx)(s.code,{children:"chat_models"})," action over the ",(0,t.jsx)(s.code,{children:"get_action"})," fallback channel."]}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1chatstatus",children:(0,t.jsx)(s.code,{children:"GET burst/v1/chat/status"})}),"\n",(0,t.jsx)(s.p,{children:"Returns whether the chat feature is currently usable. The dashboard polls this endpoint to decide whether to render the chat UI and to surface configuration hints."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Permission:"})," ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"})]}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Response fields:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Field"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"enabled"})}),(0,t.jsx)(s.td,{children:"bool"}),(0,t.jsx)(s.td,{children:"True when the Abilities API is on, the WordPress AI Client is loaded, at least one provider is configured and all required connector approvals are granted"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"abilities_enabled"})}),(0,t.jsx)(s.td,{children:"bool"}),(0,t.jsx)(s.td,{children:"Whether the Abilities API setting is on"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"ai_client_loaded"})}),(0,t.jsx)(s.td,{children:"bool"}),(0,t.jsx)(s.td,{children:"Whether the WordPress AI Client classes are available"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"has_configured_provider"})}),(0,t.jsx)(s.td,{children:"bool"}),(0,t.jsx)(s.td,{children:"True when at least one registered AI provider has an API key present"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"missing_approvals"})}),(0,t.jsx)(s.td,{children:"string[]"}),(0,t.jsx)(s.td,{children:"Names of connectors still pending approval when connector approvals are enforced. Empty when nothing is blocking"})]})]})]}),"\n",(0,t.jsxs)(s.p,{children:["The same payload is returned by the ",(0,t.jsx)(s.code,{children:"chat_status"})," action over the ",(0,t.jsx)(s.code,{children:"get_action"})," fallback channel and can be extended through the ",(0,t.jsx)(s.code,{children:"burst_chat_availability"})," filter:"]}),"\n",(0,t.jsxs)(s.details,{className:"code-collapse",children:[(0,t.jsx)(s.summary,{children:"Show code"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-php",children:"add_filter( 'burst_chat_availability', function( array $payload ): array {\n    $payload['custom_provider'] = defined( 'MY_CUSTOM_AI_KEY' );\n    return $payload;\n} );\n"})})]}),"\n",(0,t.jsx)(s.h2,{id:"mainwp-integration",children:"MainWP integration"}),"\n",(0,t.jsxs)(s.p,{children:["This endpoint is only available in Burst when the ",(0,t.jsx)(s.a,{href:"https://github.com/Burst-Statistics/burst-mainwp",children:"MainWP integration"})," is active."]}),"\n",(0,t.jsx)(s.h3,{id:"get-burstv1mainwp-auth",children:(0,t.jsx)(s.code,{children:"GET burst/v1/mainwp-auth"})}),"\n",(0,t.jsx)(s.p,{children:"Issues an Application Password token for the MainWP dashboard to authenticate subsequent REST API calls. This is the bootstrap handshake that the dashboard performs once per session before calling any other Burst route with Basic auth."}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.strong,{children:"Method:"})," GET\n",(0,t.jsx)(s.strong,{children:"Permission callback:"})," validates a MainWP RSA-signed body or an already logged-in user with ",(0,t.jsx)(s.code,{children:"manage_burst_statistics"}),". Subscribers and unauthenticated unsigned callers are rejected before the route callback runs."]}),"\n",(0,t.jsx)(s.p,{children:"Signature verification, capability check, user-switching and dashboard-origin persistence have moved into the permission callback. The route callback now only mints (or reuses) the Application Password."}),"\n",(0,t.jsx)(s.p,{children:(0,t.jsx)(s.strong,{children:"Response on success:"})}),"\n",(0,t.jsxs)(s.table,{children:[(0,t.jsx)(s.thead,{children:(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.th,{children:"Field"}),(0,t.jsx)(s.th,{children:"Type"}),(0,t.jsx)(s.th,{children:"Description"})]})}),(0,t.jsxs)(s.tbody,{children:[(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"token"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"Base64-encoded Application Password token"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"root_url"})}),(0,t.jsx)(s.td,{children:"string"}),(0,t.jsx)(s.td,{children:"WordPress REST API root URL"})]}),(0,t.jsxs)(s.tr,{children:[(0,t.jsx)(s.td,{children:(0,t.jsx)(s.code,{children:"localization_data"})}),(0,t.jsx)(s.td,{children:"object"}),(0,t.jsx)(s.td,{children:"Dashboard localisation strings and settings"})]})]})]}),"\n",(0,t.jsxs)(s.p,{children:["When the MainWP integration is active, permissive CORS headers are added to all ",(0,t.jsx)(s.code,{children:"burst/v1"})," routes. The ",(0,t.jsx)(s.code,{children:"Origin"})," header is reflected, credentials are allowed, and the ",(0,t.jsx)(s.code,{children:"Authorization"}),", ",(0,t.jsx)(s.code,{children:"X-WP-Nonce"}),", ",(0,t.jsx)(s.code,{children:"X-Burst-Share-Token"})," and ",(0,t.jsx)(s.code,{children:"X-BurstMainWP"})," request headers are permitted."]}),"\n",(0,t.jsxs)(s.p,{children:["During the unpaired bootstrap call to ",(0,t.jsx)(s.code,{children:"burst/v1/mainwp-auth"}),", CORS responses no longer set ",(0,t.jsx)(s.code,{children:"Access-Control-Allow-Credentials: true"}),". Credentials are only echoed back once the request origin matches the persisted dashboard origin, so the very first signed handshake cannot ride on a logged-in cookie."]}),"\n",(0,t.jsx)(s.h2,{id:"public-proxy-pattern",children:"Public proxy pattern"}),"\n",(0,t.jsx)(s.p,{children:"Burst's data endpoints are intentionally protected. If you need public access to analytics data (for example, for a public-facing dashboard widget), implement a minimal proxy rather than loosening Burst's own permissions."}),"\n",(0,t.jsxs)(s.details,{className:"code-collapse",children:[(0,t.jsx)(s.summary,{children:"Show code"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-php",children:"add_action( 'rest_api_init', function() {\
1n    register_rest_route(\n        'burst-public/v1',\n        '/data/(?P<type>[a-z_\\-]+)',\n        [\n            'methods'             => 'GET',\n            'permission_callback' => '__return_true',\n            'callback'            => function( WP_REST_Request $request ) {\n                // Validate a custom API key before proxying.\n                $api_key = $request->get_param( 'key' );\n                if ( ! hash_equals( BURST_PUBLIC_API_KEY, (string) $api_key ) ) {\n                    return new WP_REST_Response( [ 'success' => false, 'message' => 'Invalid key' ], 403 );\n                }\n\n                // Only allow a strict subset of data types.\n                $allowed_types = [ 'today', 'insights', 'datatable' ];\n                $type          = sanitize_key( $request->get_param( 'type' ) );\n                if ( ! in_array( $type, $allowed_types, true ) ) {\n                    return new WP_REST_Response( [ 'success' => false, 'message' => 'Type not allowed' ], 400 );\n                }\n\n                $url = add_query_arg(\n                    [\n                        'date_start' => $request->get_param( 'date_start' ),\n                        'date_end'   => $request->get_param( 'date_end' ),\n                    ],\n                    rest_url( 'burst/v1/data/' . $type )\n                );\n\n                $response = wp_remote_get(\n                    $url,\n                    [\n                        'headers' => [\n                            'Authorization' => 'Basic ' . base64_encode( BURST_REST_USER . ':' . BURST_REST_APP_PASSWORD ),\n                        ],\n                        'timeout' => 15,\n                    ]\n                );\n\n                if ( is_wp_error( $response ) ) {\n                    return new WP_REST_Response( [ 'success' => false, 'message' => $response->get_error_message() ], 500 );\n                }\n\n                return new WP_REST_Response(\n                    json_decode( wp_remote_retrieve_body( $response ), true ),\n                    wp_remote_retrieve_response_code( $response )\n                );\n            },\n        ]\n    );\n} );\n"})})]}),"\n",(0,t.jsx)(s.admonition,{type:"caution",children:(0,t.jsx)(s.p,{children:"Do not expose all Burst routes publicly. Restrict endpoint types, require a key or signature, and keep responses read-only."})})]})}function h(e={}){let{wrapper:s}={...(0,d.R)(),...e.components};return s?(0,t.jsx)(s,{...e,children:(0,t.jsx)(o,{...e})}):o(e)}},8453(e,s,i){i.d(s,{R:()=>n,x:()=>c});var r=i(6540);let t={},d=r.createContext(t);function n(e){let s=r.useContext(d);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function c(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:n(e.components),r.createElement(d.Provider,{value:s},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.