Hooks and Filters Reference
You want a new ticket to post into your own tool, a custom field to reach Chat or your analytics script to survive on the docs pages. Editing the plugin works until the next update wipes it out.
Core Support exposes 55 hooks for this: 13 actions and 42 filters, all named core_support_. This page lists each one with the arguments the code passes and what it changes. Put your code in a small plugin or a code snippets plugin, never in Core Support’s own files.
How to Read This Page
- Action hooks run your code at a moment. Use
add_action(). - Filter hooks pass a value through your code and use what you return. Use
add_filter(), and always return a value of the same type. - Arguments are listed in order. When a hook passes more than 1 argument, set the accepted-args count in
add_action()oradd_filter(). - Defaults are the values Core Support passes before any filter runs.
Tickets
core_support_ticket_created(action):int $ticket_id. Fires after a ticket is opened by anyone other than an agent: a customer in the portal, an email, a form, a connected site or a Chat handoff. Team alerts and the FluentCRM sync run on this hook.core_support_customer_replied(action):int $ticket_id,int $message_id. Fires after a customer’s reply is added to a ticket. Agent replies and notes don’t fire it.
add_action('core_support_ticket_created', function (int $ticket_id): void {
error_log('New support ticket #' . $ticket_id);
});
Forms and Team Alerts
core_support_form_ticket_fields(filter):array $fields,string $plugin,string $form_id. The submitted fields before Core Support picks the email, name, subject and message. Each field is an array withkey,label,valueandtype.$pluginiscf7,wpforms,gravityformsorfluentforms. Core Forms uses its own action and doesn’t pass through this filter.core_support_alert_channels(filter):array $channels. The team alert channels, as objects implementingGatilab\CoreSupport\Notifications\Channel. The built-in 5 are Telegram, Pushover, Discord, Slack and Webhook. A channel key must be 2 to 31 lowercase letters and digits starting with a letter, and every setting the channel owns starts with that key. Invalid or duplicate keys are skipped.
Open Tickets from Contact Forms and Team Alerts on Telegram, Pushover, Discord, Slack or a Webhook cover the screens these hooks sit behind.
Support, Docs and Changelog Pages
core_support_header_links(filter):array $links,string $page. The header links and buttons from Settings → Header and footer. Each link haslabel,urlandstyle(link,buttonorprimary).$pageisdocs,changelogsorportal. Links without a label or address are dropped.core_support_footer_links(filter):array $links,string $page. The same for the footer links.core_support_standalone_handles(filter):array $handles, ashandle => true. On portal and changelog pages, which load only Core Support’s styles and scripts, this is the full list of handles kept. Add yours, such as an analytics script. On docs pages it starts empty, and a handle you add stays even when it looks like the theme’s.core_support_theme_handle_prefixes(filter):array $prefixes,array $themes. Handle prefixes treated as the active theme’s on docs pages.$themesholds the active theme’s folder name and its parent’s. A prefix ending in-or_matches the start of a handle.
add_filter('core_support_standalone_handles', function (array $handles): array {
$handles['my-analytics'] = true;
return $handles;
});
Docs Pages, Your Theme and Other Plugins explains which styles and scripts each page type keeps.
Changelogs
core_support_changelog_category(filter):string $category,string $label. The badge a change label maps to, such asfixed, orotherwhen nothing matches.$labelis the label as written. Custom badges from Settings → Changelogs use this filter and come back ascustom-{slug}.core_support_changelog_tone(filter):string $tone,string $category. A badge’s color:green,blue,orange,red,yellowormuted, or your own color as a hex code,rgb(),hsl(),oklch()orvar(--name).core_support_changelog_intro(filter):string $intro. The line under the heading on/changelogs/, also used as that page’s description. Default: “Every release across our products, newest first.”
Admin Screens
core_support_docs_url(filter):string $url,string $topic. The guide a question mark beside an admin section opens, such ashttps://gauravtiwari.org/docs/core-support/email/imap/.$topicnames the section, such asimap,team-alertsorchangelogs. Return anotherhttps://address to send the links to your own docs, or an empty string to hide them.
// White-label: point every guide link at your own docs.
add_filter('core_support_docs_url', function (string $url, string $topic): string {
return 'https://docs.example.com/help-desk/' . $topic . '/';
}, 10, 2);
// Or hide the question marks.
add_filter('core_support_docs_url', '__return_empty_string');
Chat: Placement and Page Context
core_support_chat_show_launcher(filter):bool $shown. Whether the chat button loads on the current page, after the Where the button shows settings are applied. Returnfalseto keep it off a page ortrueto add it. Docs shown inside a connected site’s frame turn it off through this filter.core_support_chat_page_context(filter):array $context,string $origin_url. The page the visitor is on, as sent to the model:url,title,contentandpost_id.
Chat: Settings
core_support_chat_default_settings(filter):array $defaults. Every Chat setting with its default. Saved values and theCORE_SUPPORT_CHATconstant override these.core_support_chat_settings_sanitize(filter):array $output,array $input. The cleaned Chat settings just before they are saved, with the raw form input.
Chat: Providers and Models
core_support_chat_pre_llm_provider(filter):null $override,string $provider,string $task. Return anLLMProviderobject to replace the chat model completely.$taskisanswers,followupsortitles.core_support_chat_llm_provider(filter):LLMProvider $llm,string $task. The built-in chat provider for a task. The built-in provider is created first, so its API key must already be set.core_support_chat_pre_embedding_provider(filter):null $override,string $provider. Return anEmbeddingProviderobject to replace the search model completely.core_support_chat_embedding_provider(filter):EmbeddingProvider $embeddings. The built-in search model.core_support_chat_embedding_dimensions(filter):int $dimensions,string $provider,string $model. The vector size stored for a search model. Default512;0uses the model’s full size.$providerisopenaiorvoyage. Changing it learns every post again in the background.core_support_chat_moderation_provider(filter):ModerationProvider $moderation. The message check that runs before an answer. It uses OpenAI moderation when an OpenAI key is saved, and no check otherwise.core_support_chat_vector_store(filter):VectorStore $store. Where learned passages are stored and searched. Default: the site’s database.core_support_chat_model_catalog(filter):array $catalog. The providers, models, search models and thinking levels shown in Chat settings.core_support_chat_is_reasoning_model(filter):bool $is_reasoning,string $model. Runs only for a model the built-in list doesn’t recognize, withfalse. Returntruefor a model that takesmax_completion_tokensand refuses custom sampling values.core_support_chat_http_timeout(filter):int $seconds. The timeout for provider requests. Default60, minimum 1.
Chat: What It Learns
core_support_chat_post_html(filter):string $html,WP_Post $post. A post’s HTML afterthe_content, before it’s split into passages. Changelogs use it to add their kept release history.core_support_chat_chunk_context(filter):string $context,WP_Post $post. The line every passage of a post starts with. Default: the title, after the docs product when there is one.core_support_chat_post_fields(filter):array $fields,WP_Post $post. The custom fields Chat learns from a post. Each hasname,labelandvalue. Values you add are cleaned the same way, and empty ones are dropped.core_support_chat_product_facts(filter):array $lines,WP_Post $post. The “Product facts” lines learned with a product, asLabel: valuestrings. Add lines for your own product data or post types. Each line is cut at 400 characters and duplicates are dropped.core_support_chat_watched_meta(filter):array $keys. Post meta keys whose changes mark a post for learning again. Changelogs add their release dates, pre-releases and kept history.core_support_chat_max_queued_posts(filter):int $max,string $post_type. The most posts one reindex request queues. Default5000.core_support_chat_sweep_batch(filter):int $batch. The most posts one 30-minute check learns. Default25, minimum 1.core_support_chat_sweep_checks(filter):int $checks. How many unchanged learned posts each check also compares, oldest first, for changes no hook reported. Default10;0turns it off.core_support_chat_sweep_seconds(filter):int $seconds. How long one check may spend learning. Default20, minimum 5.
add_filter('core_support_chat_product_facts', function (array $lines, WP_Post $post): array {
$warranty = get_post_meta($post->ID, 'warranty_months', true);
if ($warranty !== '') {
$lines[] = 'Warranty: ' . $warranty . ' months';
}
return $lines;
}, 10, 2);
Chat: Search and the Prompt
core_support_chat_retrieved_chunks(filter):array $chunks,string $message. The passages chosen for an answer, before they are sent to the model. Each hasid,scoreandmetadata.core_support_chat_full_scan_max_chunks(filter):int $max,string $embedding_model,int $dimensions. The most passages compared with every question. Default 5,000 at 512 dimensions, proportionally fewer for longer vectors. Above it, search scores a shortlist from word search.core_support_chat_retrieval_candidates(filter):array $rows,string $text_query,string $embedding_model. The shortlist rows before they are scored. It runs only when search works from a shortlist instead of every passage.core_support_chat_fallback_candidates(filter):int $limit. How many passages are scanned when word search finds nothing, above the full-scan limit. Default400.core_support_chat_rrf_k(filter):int $k. The constant used to combine meaning and word rankings. Default60, minimum 1. Lower values give the top of each ranking more weight.core_support_chat_max_prompt_faqs(filter):int $max,string $message. How many curated Q&A entries go into the prompt. Default12;0sends every enabled entry.
These filters run while the visitor waits for an answer, so a higher limit costs response time.
Chat: Visitors and Limits
core_support_chat_trust_cloudflare(filter):bool $trust. Whether Cloudflare’s published edge ranges count as trusted proxies when Chat reads the visitor’s address. Defaulttrue.core_support_chat_trusted_proxies(filter):array $proxies. Proxy addresses and CIDR ranges whose forwarding headers are trusted, afterCORE_SUPPORT_TRUSTED_PROXIESand the Cloudflare ranges are added.core_support_chat_rate_limit(filter):array $limits,string $bucket. The limit for one Chat bucket as[limit, window in seconds]. A limit or window of0turns that bucket off. The buckets and their defaults: –chat_session: 20 messages a minute per conversation,[20, 60]–chat_ip: 60 messages in 10 minutes per IP address,[60, 600]–chat_global: 240 messages a minute for the whole site,[240, 60]–contact_ip: 5 tickets an hour per IP address from “Talk to a person”,[5, 3600]
add_filter('core_support_chat_rate_limit', function (array $limits, string $bucket): array {
return $bucket === 'chat_ip' ? [120, 600] : $limits;
}, 10, 2);
Chat: Actions
core_support_chat_message_answered(action):int $thread_id,string $message,string $reply,array $sources. After Chat answers a question.core_support_chat_unanswered_question(action):int $thread_id,string $message. After an answer for which search found no passages. Useful for finding gaps in your docs.core_support_chat_message_flagged(action):string $message,array $moderation. When moderation flags a message.$moderationhasflagged,categoriesandscores. The visitor gets the fallback message.core_support_chat_handoff(action):int $thread_id,int $ticket_id. After a conversation becomes a ticket through “Talk to a person”.core_support_chat_thread_titled(action):int $thread_id,string $title. After the model names a conversation. It never fires while Conversation titles is set to No model: first words of the question.core_support_chat_post_indexed(action):int $post_id,int $chunk_count. After a post is learned.core_support_chat_post_index_skipped(action):int $post_id. When a post’s passages haven’t changed, so no embedding request is made.core_support_chat_post_index_busy(action):int $post_id. When another run is already learning the post. Core Support marks it to learn again.core_support_chat_post_forgotten(action):int $post_id. After a post’s passages are removed.core_support_chat_search_model_switched(action):string $from,string $to. When search switches to a newly learned model. The old model’s passages are deleted next.core_support_chat_sweep_done(action):array $result. After a 30-minute check, withtime,learned,unchanged,forgotten,waitingandfailed.
add_action('core_support_chat_unanswered_question', function (int $thread_id, string $message): void {
error_log('Chat had no docs for: ' . $message);
}, 10, 2);
Good to Know
- Agent actions such as a reply, a private note, a status change or an assignment have no hooks in 0.9.4.
- Scheduled event hooks such as
core_support_deliverare WP-Cron events, not extension points. Diagnostics and WP-CLI Commands lists them. - Return the type a filter expects. The header and footer link filters, for example, fall back to the original links when you don’t return an array.