array( label, description ). */ public static function categories() { $categories = array( 'getting-started' => array( 'label' => __( 'Getting started', 'convohub' ), 'description' => __( 'What the plugin does and the first steps.', 'convohub' ), ), 'connection' => array( 'label' => __( 'Connection', 'convohub' ), 'description' => __( 'Connection key, account and disconnecting.', 'convohub' ), ), 'widget' => array( 'label' => __( 'Chat widget', 'convohub' ), 'description' => __( 'Inbox, allowed domains, pages and looks.', 'convohub' ), ), 'identity' => array( 'label' => __( 'Identity validation', 'convohub' ), 'description' => __( 'Recognizing logged-in customers.', 'convohub' ), ), 'leads' => array( 'label' => __( 'Lead forms', 'convohub' ), 'description' => __( 'Fields, styling and retries.', 'convohub' ), ), 'webhooks' => array( 'label' => __( 'Webhooks and replies', 'convohub' ), 'description' => __( 'How ConvoHub tells your site about messages.', 'convohub' ), ), 'privacy' => array( 'label' => __( 'Privacy and data', 'convohub' ), 'description' => __( 'What is sent, what is stored.', 'convohub' ), ), 'troubleshooting' => array( 'label' => __( 'Troubleshooting', 'convohub' ), 'description' => __( 'Common problems and fixes.', 'convohub' ), ), ); /** * Filters the help topics. * * @param array[] $categories Id => array( 'label' => string, 'description' => string ). */ $filtered = apply_filters( 'convohub_help_categories', $categories ); if ( ! is_array( $filtered ) ) { return $categories; } $clean = array(); foreach ( $filtered as $id => $category ) { $id = sanitize_key( $id ); if ( '' === $id || ! is_array( $category ) || empty( $category['label'] ) ) { continue; } $clean[ $id ] = array( 'label' => sanitize_text_field( $category['label'] ), 'description' => sanitize_text_field( $category['description'] ?? '' ), ); } return $clean ? $clean : $categories; } /** * The help articles, after add-ons had their say and everything was cleaned. * * @return array[] Each: id, category, title, keywords (text), body (limited HTML). */ public static function articles() { $defaults = self::default_articles(); /** * Filters the help articles. Add yours to the list. * * Each article is an array with: `id` (unique slug), `category` (a topic id), `title` (plain text), * `keywords` (text, or a list of words, that search matches in addition to the title and body) and * `body` (HTML limited to p, ul, ol, li, strong, em, code, h4, br and links; anything else is removed). * Entries that are invalid, or that reuse an existing id, are dropped. * * @param array[] $articles Articles. */ $filtered = apply_filters( 'convohub_help_articles', $defaults ); if ( ! is_array( $filtered ) ) { $filtered = $defaults; } $categories = self::categories(); $allowed = self::allowed_tags(); $seen = array(); $articles = array(); foreach ( $filtered as $article ) { if ( ! is_array( $article ) ) { continue; } $id = sanitize_key( $article['id'] ?? '' ); $category = sanitize_key( $article['category'] ?? '' ); $title = sanitize_text_field( is_scalar( $article['title'] ?? null ) ? (string) $article['title'] : '' ); $body = trim( wp_kses( is_scalar( $article['body'] ?? null ) ? (string) $article['body'] : '', $allowed ) ); if ( '' === $id || isset( $seen[ $id ] ) || ! isset( $categories[ $category ] ) || '' === $title || '' === trim( wp_strip_all_tags( $body ) ) ) { continue; } $keywords = $article['keywords'] ?? ''; $keywords = is_array( $keywords ) ? implode( ' ', array_filter( $keywords, 'is_scalar' ) ) : ( is_scalar( $keywords ) ? (string) $keywords : '' ); $seen[ $id ] = true; $articles[] = array( 'id' => $id, 'category' => $category, 'title' => $title, 'keywords' => mb_substr( sanitize_text_field( $keywords ), 0, 300 ), 'body' => $body, ); }//end foreach return $articles; } /** * Markup that article bodies may use. * * @return array */ private static function allowed_tags() { return array( 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), 'strong' => array(), 'em' => array(), 'code' => array(), 'h4' => array(), 'br' => array(), 'a' => array( 'href' => true, 'target' => true, 'rel' => true, ), ); } /** * Address of the documentation, or an empty string when none is configured. Set it with the * CONVOHUB_DOCS_URL constant or the `convohub_docs_url` filter. * * @return string */ public static function docs_url() { return self::clean_url( apply_filters( 'convohub_docs_url', defined( 'CONVOHUB_DOCS_URL' ) ? CONVOHUB_DOCS_URL : '' ) ); } /** * Address of the support page, or an empty string when none is configured. Set it with the * CONVOHUB_SUPPORT_URL constant or the `convohub_support_url` filter. * * @return string */ public static function support_url() { return self::clean_url( apply_filters( 'convohub_support_url', defined( 'CONVOHUB_SUPPORT_URL' ) ? CONVOHUB_SUPPORT_URL : '' ) ); } /** * Keeps a configured web address only when it is a plain http(s) URL. * * @param mixed $url Value from a filter. * @return string */ private static function clean_url( $url ) { return is_string( $url ) ? esc_url_raw( trim( $url ), array( 'http', 'https' ) ) : ''; } /** * Adds a short help tab to the WordPress Help drawer of the ConvoHub screen. */ public static function add_screen_help() { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen ) { return; } $content = '

' . esc_html__( 'ConvoHub connects this site to your ConvoHub account: the chat widget, recognized customers, lead forms and webhooks.', 'convohub' ) . '

'; $content .= '

' . sprintf( /* translators: %s: link to the Help tab, with its text. */ esc_html__( 'Search the guides, follow the setup guide and copy diagnostics for support on the %s.', 'convohub' ), '' . esc_html__( 'ConvoHub Help tab', 'convohub' ) . '' ) . '

'; $screen->add_help_tab( array( 'id' => 'convohub-help', 'title' => __( 'ConvoHub help', 'convohub' ), 'content' => $content, ) ); } /** * Prints the Help tab: a search field, the setup list, the articles by topic, then diagnostics. * * @param array $settings Plugin settings. */ public static function render( array $settings ) { $categories = self::categories(); $articles = self::articles(); $grouped = array(); foreach ( $articles as $article ) { $grouped[ $article['category'] ][] = $article; } $steps = ConvoHub_Setup::steps( $settings ); $rows = ConvoHub_Diagnostics::rows( $settings, ConvoHub_Diagnostics::environment() ); $docs = self::docs_url(); $help = self::support_url(); ?>

'help-guide', 'title' => __( 'Setup', 'convohub' ), 'description' => __( 'Each step is checked from your saved settings.', 'convohub' ), 'wide' => true, ) ); ConvoHub_Admin_UI::checklist( $steps ); ConvoHub_Admin_UI::section_close(); ?>
$category ) : ?>

'help-diagnostics', 'title' => __( 'Diagnostics', 'convohub' ), 'description' => __( 'Facts about this site for support. Tokens and secrets are never included.', 'convohub' ), 'wide' => true, ) ); ?>
$id, 'category' => $category, 'title' => $title, 'keywords' => $keywords, 'body' => $body, ); }; return array( // Getting started. $article( 'setup-overview', 'getting-started', __( 'Set up ConvoHub in five steps', 'convohub' ), 'setup start first steps begin checklist install quick', '

' . __( 'Work through the tabs from left to right. The setup list on the Connection tab checks each step from your saved settings.', 'convohub' ) . '

' . '
    ' . '
  1. ' . __( 'Connect. In ConvoHub open Settings > Integrations > WordPress and generate a key. On the Connection tab paste it and press Connect.', 'convohub' ) . '
  2. ' . '
  3. ' . __( 'Choose the chat widget inbox. On the Widget tab pick a website inbox, or create one for this site.', 'convohub' ) . '
  4. ' . '
  5. ' . __( 'Turn the widget on. Switch on "Show the chat widget on this site" and save.', 'convohub' ) . '
  6. ' . '
  7. ' . __( 'Decide on identity validation. Switch on "Recognize logged-in customers in the chat" if visitors have accounts on your site. It is optional.', 'convohub' ) . '
  8. ' . '
  9. ' . __( 'Register the webhook. On the Connection tab, so ConvoHub can tell your site about new customer messages.', 'convohub' ) . '
  10. ' . '
' . '

' . __( 'To collect leads with a form, also choose an API inbox on the Leads tab and add the form to a page.', 'convohub' ) . '

' ), $article( 'what-it-does', 'getting-started', __( 'What this plugin does', 'convohub' ), 'overview features about widget identity leads webhook commerce woocommerce', '

' . __( 'ConvoHub for WordPress connects your site to your ConvoHub account.', 'convohub' ) . '

' . '' . '

' . __( 'The ConvoHub Commerce add-on builds on this plugin for WooCommerce cart recovery and order-aware support. It needs this plugin to be connected first.', 'convohub' ) . '

' ), $article( 'tabs-tour', 'getting-started', __( 'Where to find things', 'convohub' ), 'tabs menu navigation connection widget leads logs help where', '' . '

' . __( 'Use the arrow keys to move between the tabs when one has focus.', 'convohub' ) . '

' ), $article( 'requirements', 'getting-started', __( 'What you need before you start', 'convohub' ), 'requirements account api access administrator https php wordpress version prerequisites signup trial create', '' ), // Connection. $article( 'connection-key', 'connection', __( 'Where to find your connection key', 'convohub' ), 'key connection key generate settings integrations wordpress administrator secret credentials paste', '

' . __( 'In ConvoHub open Settings > Integrations > WordPress and generate a key. Copy the whole key and paste it into "Connection key" on the Connection tab.', 'convohub' ) . '

' . '' ), $article( 'connect-site', 'connection', __( 'Connect this site', 'convohub' ), 'connect connection key paste account save test connection signup trial create', /* translators: %s: URL of the ConvoHub signup page. */ '

' . sprintf( __( 'No ConvoHub account yet? Create one first. New accounts include a 7-day trial.', 'convohub' ), esc_url( ConvoHub_Settings::signup_url() ) ) . '

' . '
    ' . '
  1. ' . __( 'In ConvoHub open Settings > Integrations > WordPress and generate a key.', 'convohub' ) . '
  2. ' . '
  3. ' . __( 'Paste it into "Connection key" on the Connection tab.', 'convohub' ) . '
  4. ' . '
  5. ' . __( 'Press "Connect".', 'convohub' ) . '
  6. ' . '
' . '

' . __( 'The account is the one the key was made for. There is nothing to choose and nothing else to save. "Test connection" checks later that the connection still works.', 'convohub' ) . '

' . '

' . __( 'To use another account, disconnect this site and connect again with a key from that account. Connecting to a different account clears the inbox, identity validation and webhook settings, because they belong to the old account. Connecting again to the same account keeps them.', 'convohub' ) . '

' ), $article( 'key-address', 'connection', __( 'This plugin connects to app.convohub.io', 'convohub' ), 'key address url https app.convohub.io invalid', '

' . __( 'This plugin always connects to https://app.convohub.io. There is no address to type, and a saved address is not used.', 'convohub' ) . '

' . '

' . __( 'If you see "This plugin only connects to app.convohub.io. Generate a new key there and paste it again.", the key was made for a different address. Generate a new key at https://app.convohub.io.', 'convohub' ) . '

' ), $article( 'replace-token', 'connection', __( 'Replace an access token with a connection key', 'convohub' ), 'token legacy replace access token upgrade older version reconnect migrate', '

' . __( 'Versions before 0.4.0 asked for an access token. A site connected that way keeps working, and the Connection tab shows a notice until you replace the token. The plugin always connects to app.convohub.io: there is no address to enter.', 'convohub' ) . '

' . '
    ' . '
  1. ' . __( 'In ConvoHub open Settings > Integrations > WordPress and generate a key.', 'convohub' ) . '
  2. ' . '
  3. ' . __( 'Paste it into "Connection key" on the Connection tab and press Connect.', 'convohub' ) . '
  4. ' . '
' . '

' . __( 'If the key belongs to the same account, the inbox, identity validation and webhook settings stay as they are.', 'convohub' ) . '

' ), $article( 'disconnect-site', 'connection', __( 'What Disconnect does', 'convohub' ), 'disconnect remove connection forget revoke leave account', '

' . __( 'Disconnect, at the bottom of the Connection tab, makes this site forget the connection: the access it was given, the account, the chosen inboxes and the webhook settings. The chat widget, lead forms and webhook stop working until you connect again.', 'convohub' ) . '

' . '

' . __( 'Nothing is deleted in ConvoHub and ConvoHub is not contacted. The webhook stays registered there, so press "Remove" on the webhook first if you want it gone, and remove the access in ConvoHub if you want it revoked.', 'convohub' ) . '

' ), $article( 'api-access', 'connection', __( 'API access is not enabled for this account', 'convohub' ), '403 api access enabled account error forbidden permission', '

' . __( 'This message means ConvoHub accepted the connection but the account does not have API access turned on. The plugin cannot work around that.', 'convohub' ) . '

' . '

' . __( 'Ask the person who runs your ConvoHub account to enable API access for it, then test the connection again.', 'convohub' ) . '

' ), // Chat widget. $article( 'choose-inbox', 'widget', __( 'Choose or create the website inbox', 'convohub' ), 'inbox website create choose widget select', '

' . __( 'The chat widget belongs to a website inbox in your ConvoHub account. On the Widget tab, pick an existing one and press "Use this inbox", or press "Create an inbox for this site" to make one named after your site.', 'convohub' ) . '

' . '

' . __( 'The widget cannot be switched on until an inbox is chosen. Colors and the welcome text are edited on the inbox in ConvoHub, not here.', 'convohub' ) . '

' ), $article( 'allowed-domains', 'widget', __( 'Allowed domains: why the chat may not show', 'convohub' ), 'allowed domains blocked cors origin browser inbox domain add this site not showing', '

' . __( 'A website inbox can be limited to certain websites. If your site is not on its list, the browser refuses to load the chat and nothing appears.', 'convohub' ) . '

' . '

' . __( 'The Widget tab checks this for you. When your site is not allowed it shows a warning with the button "Add this site to the inbox", which adds your site\'s address to the inbox in ConvoHub.', 'convohub' ) . '

' . '' ), $article( 'display-rules', 'widget', __( 'Show or hide the chat on certain pages', 'convohub' ), 'display rules pages paths show hide checkout logged in exclude include pattern wildcard', '

' . __( 'Under Display on the Widget tab, "Show on" chooses between all pages, only the pages you list, or all pages except the ones you list.', 'convohub' ) . '

' . '' ), $article( 'appearance', 'widget', __( 'Position, launcher and other appearance options', 'convohub' ), 'appearance position left right launcher bubble style dark mode color scheme language file upload emoji', '

' . __( 'Each option can be left on "ConvoHub default", which uses whatever the inbox is set to in ConvoHub, or set here for this site only.', 'convohub' ) . '

' . '

' . __( 'You can choose the side the chat sits on, the launcher style, a label for the expanded launcher, the color scheme, and whether visitors can upload files, use the emoji picker, end the conversation, or get the chat in their browser\'s language. Brand colors and the welcome message are set on the inbox in ConvoHub.', 'convohub' ) . '

' ), // Identity validation. $article( 'identity-validation', 'identity', __( 'How identity validation works', 'convohub' ), 'identity validation verify hmac secret logged in customers recognize signature security', '

' . __( 'When a logged-in customer opens a page with the chat, your site signs the customer\'s ID on the server with the inbox identity secret and gives the chat the ID, name, email address and signature. ConvoHub checks the signature, so it can link the chat to that customer and show their conversation history on any device.', 'convohub' ) . '

' . '' . '

' . __( 'Switch it on with "Recognize logged-in customers in the chat" on the Widget tab.', 'convohub' ) . '

' ), $article( 'identity-mandatory', 'identity', __( 'Why mandatory identity validation matters', 'convohub' ), 'mandatory enforce identity validation impersonate spoof security hmac inbox settings required', '

' . __( 'Signing customers on your site is only half of the protection. The other half is telling ConvoHub to insist on it.', 'convohub' ) . '

' . '

' . __( 'If identity validation is not required for the inbox, a curious visitor can open their browser tools, claim another customer\'s ID or email address, and be shown that person\'s conversation history. Turning validation on for the inbox in ConvoHub makes it reject any identity not signed with your secret.', 'convohub' ) . '

' . '' ), $article( 'identity-cookie', 'identity', __( 'The convohub_logged_in cookie and page caching', 'convohub' ), 'cookie cache caching logged in marker convohub_logged_in login logout browser storage', '

' . __( 'The plugin sets a small cookie named convohub_logged_in when someone logs in and removes it when they log out. The chat uses it to know when to ask your site who is signed in, so full-page caching cannot show one person\'s identity to another.', 'convohub' ) . '

' . '

' . __( 'The reply to that question is never cached. If a cache plugin or CDN caches it anyway, exclude the WordPress admin-ajax.php action convohub_identity from caching.', 'convohub' ) . '

' ), // Lead forms. $article( 'lead-form-add', 'leads', __( 'Add the lead form to a page', 'convohub' ), 'form shortcode block add page insert convohub_form heading button editor', '

' . __( 'In the block editor, add the "ConvoHub Lead Form" block. In the classic editor or any text area that supports shortcodes, paste:', 'convohub' ) . '

' . '

[convohub_form heading="Contact us" button="Send"]

' . '

' . __( 'Both attributes are optional: heading is the title above the form and button is the text on the send button. Which fields appear is chosen on the Leads tab under Form fields.', 'convohub' ) . '

' ), $article( 'lead-inbox', 'leads', __( 'Where leads go', 'convohub' ), 'inbox leads api create destination contact conversation', '

' . __( 'Each submission creates a contact and a conversation in an API inbox of your ConvoHub account. On the Leads tab choose an existing API inbox, or press "Create an inbox for leads".', 'convohub' ) . '

' . '

' . __( 'Until an inbox is chosen, submitted leads cannot be delivered and wait in the Logs tab.', 'convohub' ) . '

' ), $article( 'lead-fields', 'leads', __( 'Choose and add form fields', 'convohub' ), 'fields editor custom field company budget required dropdown checkbox type name label order', '

' . __( 'Under Form fields on the Leads tab you can show or hide each field, rename it, mark it required and change the order. Name, Email, Phone and Message always exist; you can hide them but not delete them.', 'convohub' ) . '

' . '' ), $article( 'default-country-code', 'leads', __( 'Default country code and phone numbers', 'convohub' ), 'phone country code default number international e164 plus prefix 91', '

' . __( 'ConvoHub needs phone numbers in international format, which starts with + and the country code. The form converts what visitors type.', 'convohub' ) . '

' . '

' . __( 'Numbers typed without a + use the default country code from the Leads tab. Enter only the digits of the calling code, for example 91 for India or 1 for the United States. If it is empty, visitors must type the + and their country code themselves.', 'convohub' ) . '

' ), $article( 'form-appearance', 'leads', __( 'Change how the form looks', 'convohub' ), 'appearance style design colors button color corners radius spacing width css additional class form look', '

' . __( 'The Appearance section on the Leads tab sets the button colour, the corners, the space between fields and the maximum width. What you leave on "Follow the theme" keeps the look of your theme. It is only loaded on pages that show the form.', 'convohub' ) . '

' . '

' . __( 'For anything else, use WordPress\'s own Additional CSS: Appearance > Customize > Additional CSS, or Styles in the Site Editor. The form has these class names:', 'convohub' ) . '

' . '' ), $article( 'retry-queue', 'leads', __( 'Undelivered leads, retries and the Logs tab', 'convohub' ), 'retry queue logs undelivered failed resend discard attempts backoff cron lost', '

' . __( 'If ConvoHub cannot be reached when a visitor sends the form, the lead is not lost. It is kept on your site and sent again automatically.', 'convohub' ) . '

' . '' . '

' . __( 'The Logs tab only lists leads that are not delivered. Delivered leads are removed from your site, and anything left is deleted after 30 days.', 'convohub' ) . '

' ), $article( 'spam-protection', 'leads', __( 'Spam protection and the submission limit', 'convohub' ), 'spam honeypot rate limit too many submissions proxy cdn ip captcha bot', '

' . __( 'The form has a hidden field that people never see but simple bots fill in; those submissions are ignored. Each visitor address may send 5 submissions every 10 minutes, and each submission may be at most 32 KB.', 'convohub' ) . '

' . '

' . __( 'The limit uses the visitor\'s IP address. Behind a proxy or CDN every visitor can look the same, so real visitors may see "too many submissions". Developers can return the real address with the convohub_client_ip filter.', 'convohub' ) . '

' ), // Webhooks and replies. $article( 'webhook-overview', 'webhooks', __( 'What the webhook does', 'convohub' ), 'webhook events receiver message_incoming conversation_status_changed overview register', '

' . __( 'A webhook lets ConvoHub call your site when something happens. This plugin registers one that ConvoHub uses to report new customer messages (message_incoming) and changes in a conversation\'s status (conversation_status_changed).', 'convohub' ) . '

' . '

' . __( 'On its own the core plugin only checks each event and passes it on to other code on your site through the convohub_webhook_event action. Add-ons such as ConvoHub Commerce use it. Without an add-on, nothing visible happens on your site; your conversations still live in ConvoHub.', 'convohub' ) . '

' ), $article( 'webhook-signing', 'webhooks', __( 'How events are signed and checked', 'convohub' ), 'signature signing hmac sha256 timestamp replay security verify secret 401 rejected duplicate', '

' . __( 'Anyone can send a request to a public address, so every event carries a signature that only ConvoHub and your site can produce. The plugin accepts an event only when:', 'convohub' ) . '

' . '' . '

' . __( 'Anything else is answered with a plain "Rejected" (HTTP 401) and no reason, so a caller cannot probe the check. The secret is created when you register the webhook and stays on your server.', 'convohub' ) . '

' ), $article( 'webhook-register', 'webhooks', __( 'Register, update or remove the webhook', 'convohub' ), 'register webhook update remove check connection tab receiver address public https', '

' . __( 'On the Connection tab, under Webhook, "Register webhook" creates the webhook in ConvoHub for this site and saves its secret.', 'convohub' ) . '

' . '' ), $article( 'stop-replies', 'webhooks', __( 'What STOP and customer replies mean for this plugin', 'convohub' ), 'stop opt out unsubscribe reply replies incoming message whatsapp sms keyword', '

' . __( 'The core plugin does not read or act on message text. A customer\'s reply, including a word such as STOP, is delivered to ConvoHub, shown in your inbox, and reported to your site as a message_incoming event.', 'convohub' ) . '

' . '

' . __( 'What that event means is up to the code that listens to it. Add-ons, such as ConvoHub Commerce, can decide that a STOP reply means "send this person no more messages". Check that add-on\'s own help for what it does. If nothing listens, the event is simply received and ignored.', 'convohub' ) . '

' . '

' . __( 'Opt-out rules for WhatsApp and SMS are also enforced by ConvoHub and the messaging provider, not by this plugin.', 'convohub' ) . '

' ), // Privacy and data. $article( 'data-sent', 'privacy', __( 'What data is sent to ConvoHub', 'convohub' ), 'data sent privacy gdpr personal information external service token email phone', '' ), $article( 'data-stored', 'privacy', __( 'What this plugin stores on your site', 'convohub' ), 'stored database options table cookie local storage transient cron data', '' ), $article( 'privacy-tools', 'privacy', __( 'Privacy policy text, data export and erasure', 'convohub' ), 'privacy policy export erase personal data request gdpr tools delete', '

' . __( 'The plugin adds suggested text to Settings, Privacy, Policy Guide, which you can edit and use in your privacy policy.', 'convohub' ) . '

' . '

' . __( 'WordPress\'s Export Personal Data and Erase Personal Data tools include leads that are still waiting to be delivered, matched by email address. Leads that already reached ConvoHub live in your ConvoHub account, so export or erase those there.', 'convohub' ) . '

' ), $article( 'uninstall', 'privacy', __( 'What happens when you uninstall', 'convohub' ), 'uninstall delete remove deactivate data cleanup', '

' . __( 'Deactivating the plugin keeps everything. Deleting it from the Plugins screen removes its settings (including the token and secrets), undelivered leads, scheduled jobs and temporary data from your database.', 'convohub' ) . '

' . '' ), // Troubleshooting. $article( 'widget-not-showing', 'troubleshooting', __( 'The chat widget does not appear', 'convohub' ), 'widget not showing missing hidden chat bubble blank invisible', '

' . __( 'Check these in order:', 'convohub' ) . '

' . '
    ' . '
  1. ' . __( 'On the Widget tab, an inbox is chosen and "Show the chat widget on this site" is on.', 'convohub' ) . '
  2. ' . '
  3. ' . __( 'The page is not excluded by "Show on", and you are not hiding the chat for logged-in users while testing as one.', 'convohub' ) . '
  4. ' . '
  5. ' . __( 'Your site is in the inbox\'s allowed domains. The Widget tab warns you if it is not.', 'convohub' ) . '
  6. ' . '
  7. ' . __( 'You are not previewing in the Customizer, where the chat is never loaded.', 'convohub' ) . '
  8. ' . '
  9. ' . __( 'A caching or optimization plugin is not delaying or blocking the script. Clear the cache and try a private window.', 'convohub' ) . '
  10. ' . '
' ), $article( 'key-not-valid', 'troubleshooting', __( 'This key is not valid', 'convohub' ), 'key not valid invalid used already revoked expired connect failed paste wrong generate new', '

' . __( '"This key is not valid. It may have been used already, revoked or expired." ConvoHub gives the same answer in every one of these cases, so it cannot say which one it was.', 'convohub' ) . '

' . '' . '

' . __( 'Other messages: "Too many attempts" is this site limiting tries (10 every 10 minutes); "ConvoHub received too many connection attempts" comes from ConvoHub; "read-only because of its billing status" means the ConvoHub account needs its billing fixed; "did not accept this site\'s address" means the WordPress Address and Site Address in Settings > General are not a public address; "newer way of signing in" means this plugin should be updated.', 'convohub' ) . '

' ), $article( 'connection-errors', 'troubleshooting', __( 'Connection test errors and what they mean', 'convohub' ), 'error test connection rejected access 401 402 404 429 500 unreachable timeout billing rate limit message', '' ), $article( 'webhook-not-arriving', 'troubleshooting', __( 'Events from ConvoHub are not arriving', 'convohub' ), 'webhook not working events missing not arriving firewall security plugin clock stale rest api blocked', '

' . __( 'Check these in order:', 'convohub' ) . '

' . '
    ' . '
  1. ' . __( 'The Connection tab says the webhook is registered. If not, register it. If the receiver address looks wrong, press "Check and update".', 'convohub' ) . '
  2. ' . '
  3. ' . __( 'The site is reachable from the internet over HTTPS. Staging sites behind a password, an IP allow-list or a maintenance page block ConvoHub.', 'convohub' ) . '
  4. ' . '
  5. ' . __( 'A firewall or security plugin is not blocking POST requests to /wp-json/convohub/v1/webhook, and the REST API is not disabled for visitors.', 'convohub' ) . '
  6. ' . '
  7. ' . __( 'Your server\'s clock is right. Events whose timestamp differs by more than five minutes are rejected.', 'convohub' ) . '
  8. ' . '
' ), $article( 'lead-not-delivered', 'troubleshooting', __( 'A lead did not reach ConvoHub', 'convohub' ), 'lead missing not delivered form submission error failed logs inbox access', '

' . __( 'Open the Logs tab. A lead that could not be delivered is listed with its last error.', 'convohub' ) . '

' . '' ), $article( 'cron-not-running', 'troubleshooting', __( 'Retries do not happen (WP-Cron)', 'convohub' ), 'cron wp-cron retries scheduled not running disable_wp_cron action scheduler low traffic', '

' . __( 'Automatic retries run on WP-Cron, which WordPress triggers when someone visits your site. On a very quiet site retries can be late.', 'convohub' ) . '

' . '

' . __( 'If your site sets DISABLE_WP_CRON to true, WordPress no longer triggers it by itself and a real cron job must run wp-cron.php, otherwise retries never happen. The Diagnostics panel shows which case you are in. Resend on the Logs tab works either way.', 'convohub' ) . '

' ), $article( 'identity-not-recognized', 'troubleshooting', __( 'Logged-in customers are not recognized in the chat', 'convohub' ), 'identity not recognized logged in customer anonymous history missing secret switch greyed disabled', '
    ' . '
  1. ' . __( '"Recognize logged-in customers in the chat" on the Widget tab is on. If the switch is greyed out, the inbox identity secret is missing: connect with a key made by a ConvoHub administrator and choose the inbox again.', 'convohub' ) . '
  2. ' . '
  3. ' . __( 'You are testing as a logged-in user, not a guest or an administrator on a "hide for logged-in users" setup.', 'convohub' ) . '
  4. ' . '
  5. ' . __( 'ConvoHub enforces identity validation for the inbox, and the secret in your inbox settings has not been regenerated since you chose it. If it was, choose the inbox again.', 'convohub' ) . '
  6. ' . '
  7. ' . __( 'A cache or CDN is not caching the reply to the convohub_identity request.', 'convohub' ) . '
  8. ' . '
' ), $article( 'too-many-submissions', 'troubleshooting', __( 'Visitors see "too many submissions"', 'convohub' ), 'too many submissions rate limit blocked proxy cdn cloudflare ip shared', '

' . __( 'The form allows 5 submissions per visitor address every 10 minutes. If your site sits behind a proxy or CDN, every visitor can share one address and hit the limit together.', 'convohub' ) . '

' . '

' . __( 'Ask your developer to return the real visitor address with the convohub_client_ip filter, using the header your proxy sets.', 'convohub' ) . '

' ), $article( 'contact-support', 'troubleshooting', __( 'How to ask for help', 'convohub' ), 'support contact help diagnostics copy report problem ticket', '
    ' . '
  1. ' . __( 'Press "Copy for support" in the Diagnostics panel on this page. It copies facts about your site and settings, never your connection key, access token or secrets.', 'convohub' ) . '
  2. ' . '
  3. ' . __( 'Paste it into your message.', 'convohub' ) . '
  4. ' . '
  5. ' . __( 'Say what you did, what you expected and what happened instead. For a lead problem, add the "Last error" text from the Logs tab.', 'convohub' ) . '
  6. ' . '
' ), ); } }