For administrators deploying ThunderAI across an organization. It covers pre-configuring settings, enforcing them so users cannot change them, distributing a shared set of prompts, replacing the prompts of the special features and choosing their connection, limiting the automatic features to some accounts, and restricting what users can do with prompts and the setup wizard.
No add-on setting needs to be touched on each machine: everything here is driven by Thunderbird's own enterprise policy file.
Enterprise configuration is available from ThunderAI 5.1.0.
Before you start
⚠️ Policies are read only at Thunderbird startup
There is no live reload. Any change to policies.json — including
rotating an API key or token — takes effect only after Thunderbird is fully restarted. Users
who leave Thunderbird running for days will not pick up a change until they restart.
If you rotate a credential, plan for the window in which running clients still hold the old one.
⚠️ Policy files are world-readable
policies.json must be readable by every user account on the machine, so
any local user can read any API key you put in it. It is not a secret store.
Prefer pointing ThunderAI at an internal gateway (via openai_comp_host)
that holds the real provider credentials server-side, and put a revocable per-organization token
in the policy — not a raw provider key. If you must deploy a provider key directly, use one scoped
and rate-limited to this purpose, and rotate it on the assumption it is exposed.
Where policies.json goes
| OS | Path |
|---|---|
| Linux | /etc/thunderbird/policies/policies.json (recommended) |
or <install dir>/distribution/policies.json | |
| Windows | C:\Program Files\Mozilla Thunderbird\distribution\policies.json |
| macOS | /Applications/Thunderbird.app/Contents/Resources/distribution/policies.json |
On Linux, use /etc/thunderbird/policies/. It lives outside the
application directory, so it survives application updates. The
distribution directory — the only option on Windows and macOS — sits inside the
installation and may be removed or replaced when Thunderbird updates, silently
dropping your configuration.
On Windows you can avoid that by using Group Policy / the registry instead of the file, under
HKLM\SOFTWARE\Policies\Mozilla\Thunderbird. On macOS, a configuration profile
(.mobileconfig) is the equivalent durable option. Both express the same
3rdparty structure shown below.
To verify a policy has loaded, open about:policies in Thunderbird.
Structure
Every ThunderAI setting lives under 3rdparty → Extensions → thunderai@micz.it:
{
"policies": {
"3rdparty": {
"Extensions": {
"thunderai@micz.it": {
"connection_type": "openai_comp_api"
}
}
}
}
}Policy key names are exactly ThunderAI's own setting names. There is no mapping table and no renaming — see the key reference.
Enforced vs. initial values
Every key you set is enforced by default. The user sees it in the settings, greyed out with a padlock badge reading Managed by <_org_name> (or just Managed), and cannot change it. A banner at the top of the page explains that some settings are managed by the organization.
To supply a starting value the user may then change, add a sibling "<key>:locked": false:
"reply_type": "reply_all",
"reply_type:locked": false| Enforced (default) | "<key>:locked": false | |
|---|---|---|
| User can change it | no | yes |
| Control in settings | disabled, marked "Managed" with a padlock | normal |
| If the user already set it | policy value wins | user's value wins |
| Written to the user's profile | never | yes, once they change it |
That third row matters. An unlocked value is only an initial suggestion: a user who already configured that setting keeps their own value and never sees yours. Use an enforced value if you need it to actually apply everywhere.
Nothing enforced is ever written into the user's profile, so removing a key from the policy cleanly restores whatever the user had before, or the ThunderAI default.
Both kinds of value are shown where the user looks for them. Enforced and initial values appear in the options page and in every feature settings page — including the list fields and the spam filter's address book option, which are locked on their pages like any other control — and they also apply in the API chat window and in the popup.
Policy connection values are not copied into prompts. When a user turns on a specific integration for a single feature (Add Tags, Summarize, Spam Filter…), or sets a connection on a custom prompt, those fields start from the ThunderAI defaults, not from the host, model or key in your policy. A copied value would outlive the policy.
API keys set by policy
An API key supplied by the policy is never displayed in the settings. The field
shows a masked placeholder, and from the settings pages the user cannot reveal the key, run the
connection test with it, or fetch the model list with it. ThunderAI itself still uses the key
normally, in the API chat window and in every feature. Because the model list cannot be fetched
with your key, set the model explicitly in the policy (for example openai_comp_model).
An unlocked key ("<key>:locked": false) stays masked until the user types a key
of their own, which is then stored and wins, like any other unlocked value.
This keeps the key out of the add-on's pages. It does not make it secret: it is
still readable in policies.json — see Before you start.
Strict mode: _lock_unlisted and _user_editable
By default a policy controls only the settings it names. Everything else stays the user's choice, including settings added by future ThunderAI versions.
Strict mode reverses that. With "_lock_unlisted": true, every setting
your policy does not set is locked at ThunderAI's built-in default. In the settings pages it looks and
behaves exactly like a setting you lock explicitly: disabled, and marked as managed with the padlock.
Settings added by a future version are locked at their default from the first start after the update.
| Key | Type | Meaning |
|---|---|---|
_lock_unlisted | boolean (default false) | true turns strict mode on. |
_user_editable | array of setting names (default []) | The settings that stay user-editable in strict mode. Only used when
_lock_unlisted is true. |
How each setting is resolved
In strict mode, in order of precedence:
- Per-machine settings are never affected. They stay user-editable: the API chat window size and position, the chat font zoom, the layout of the custom prompts page, and the per-profile account selection of the spam filter and Add Tags (see Keys that cannot be set by policy).
- A setting your policy sets keeps its usual behaviour: enforced, or only an
initial value with
"<key>:locked": false, exactly as without strict mode. The settings implied by_special_prompts_connection(the feature's Use a specific integration switch and its connection type) count as set by the policy. If such a setting also appears in_user_editable, the policy value wins and a warning is logged. - A setting listed in
_user_editablestays the user's: their own value, or the default if they never changed it. - Every other setting is locked at its default.
Without _lock_unlisted, or with it set to false, nothing changes compared
to earlier versions.
The account lists are never locked by strict mode:
spamfilter_enabled_accounts_match and add_tags_enabled_accounts_match.
Their default means "not managed", and locking them would stop those automatic features on every
account. The features themselves (spamfilter, add_tags) are locked at their
default, which is off.
Users' own values are never deleted. If you turn strict mode off, they come back at the next start.
Example
{
"policies": {
"3rdparty": {
"Extensions": {
"thunderai@micz.it": {
"_org_name": "ACME Corp",
"_lock_unlisted": true,
"_user_editable": ["default_sign_name", "default_chatgpt_lang", "do_debug"],
"connection_type": "openai_comp_api",
"openai_comp_host": "https://ai-gateway.acme.example",
"openai_comp_api_key": "sk-acme-...",
"openai_comp_model": "gpt-4o-mini",
"translate": true,
"summarize": true
}
}
}
}
}The connection, Translate and Summarize are enforced as written. Users can still change their signature name, default language and debug logging. Every other setting, including any added by future versions, is locked at its default.
Before you turn it on
⚠️ Set a connection
The default is "no connection". A strict policy must set connection_type and the
matching provider settings, or list them in _user_editable. Otherwise ThunderAI stays
unconfigured, and the setup wizard cannot save a connection.
- Some defaults are on. Translate (
translate), Create calendar event (get_calendar_event), Create task (get_task), showing token usage in the chat (chat_show_usage_data) and hiding the model's thinking (hide_thinking) are on by default. If you don't want them, set them tofalseexplicitly. - Prompts are not settings. Strict mode does not lock custom prompts, menu order,
custom placeholders, per-feature connections or special prompt texts. To control those, use
_disable_prompt_management,_special_prompts_textand_special_prompts_connection. - Debug logging is locked off (
do_debug) unless you list it in_user_editable. List it if your support team needs logs from users. - Restart required. As for every policy change, strict mode takes effect at the next Thunderbird start.
Validation
Problems are reported as warnings in the Thunderbird Error Console, even with debug logging off (see Troubleshooting):
_lock_unlistedmust betrueorfalse. Any other value is ignored with a warning, and strict mode stays off._user_editablemust be an array. Otherwise it is ignored with a warning, and strict mode is off.- An entry of
_user_editablethat is not a string, or that names an unknown setting, is skipped with a warning. - An entry naming a per-machine setting is skipped with a warning: those settings are always editable.
_user_editablewithout"_lock_unlisted": trueis ignored with a warning.
Worked example
An organization routing all AI traffic through an internal gateway, enforcing the connection and the privacy-relevant settings, suggesting a couple of defaults, and shipping two shared prompts.
Example policies.json
{
"policies": {
"3rdparty": {
"Extensions": {
"thunderai@micz.it": {
"_schema_version": 1,
"_org_name": "ACME Corporation",
"_org_id": "acme",
"connection_type": "openai_comp_api",
"openai_comp_host": "https://ai-gateway.acme.example/v1",
"openai_comp_api_key": "acme-gateway-token-REPLACE-ME",
"openai_comp_model": "gpt-4o",
"openai_comp_chat_name": "ACME AI",
"spamfilter": false,
"add_tags_auto": false,
"_disable_setup_wizard": true,
"default_chatgpt_lang": "English",
"default_chatgpt_lang:locked": false,
"reply_type": "reply_all",
"reply_type:locked": false,
"_org_prompts": [
{
"id": "support_reply",
"name": "Draft support reply",
"text": "Draft a reply to this customer email. Follow ACME tone of voice: concise, factual, no promises about delivery dates.\n\n{%mail_text_body_or_selected%}",
"type": "1",
"action": "1",
"need_signature": "1"
},
{
"id": "legal_review",
"name": "Flag legal risk",
"text": "Review the following text and list any statement that could create a contractual obligation.\n\n{%mail_text_body_or_selected%}",
"type": "0",
"action": "0",
"api_type": "anthropic_api"
}
]
}
}
}
}
}What the user sees: the connection is pre-configured and locked, so they are never asked to run the setup wizard — and cannot open it either. The spam filter and auto-tagging are off and cannot be turned on. Language and reply style start at the organization's preference but can be changed. The two ACME prompts appear on the Manage Prompts page with an Organization badge, read-only, and can be Duplicated to make an editable personal version.
Organization prompts
_org_prompts is an array of prompt objects that becomes a shared, read-only prompt set
alongside the user's own.
Requires _org_id — lowercase letters, digits and hyphens only, no
underscore. It namespaces your prompt ids so they can never clash with ThunderAI's built-ins or with
another organization's. Choose it once and never change it: it is part of every
prompt id, and changing it makes users lose the menu positions they set.
The final id becomes org_<_org_id>_<id> — so "id": "support_reply"
with "_org_id": "acme" is stored as org_acme_support_reply.
Fields
| Field | Required | Notes |
|---|---|---|
id | yes | unique within the array, no whitespace; prefixed automatically |
name | yes | shown in menus |
text | yes | the prompt; may contain {%placeholder%} tokens |
type | no | "0" always, "1" reading, "2" composing |
action | no | "0" close, "1" reply, "2" substitute text |
need_selected | no | "1" requires a text selection |
need_signature | no | "1" includes the signature |
need_custom_text | no | "1" prompts for extra input |
define_response_lang | no | "1" appends a response-language instruction |
use_diff_viewer | no | "1" offers a diff of the result |
api_type | no | route this prompt to a specific provider |
api_type accepts chatgpt_web, chatgpt_api,
google_gemini_api, anthropic_api, ollama_api,
openai_comp_api. Empty or absent means "use the configured connection".
Behaviour
- Fully declarative. The policy is the only source of truth. Add, change or remove a prompt and it is reflected at the next Thunderbird start. Users' own prompts are never touched.
- Read-only, but duplicable. Users cannot edit or delete them, but
Duplicate (or Duplicate and edit) makes an ordinary editable
personal prompt — unless
_disable_prompt_managementis on. - Users control placement. Menu position and visibility are per-user, set in Menu Order like any other prompt.
- If a user already has a prompt with the same id, yours takes precedence and theirs is disabled, with an explanation shown on the Manage Prompts page. Their prompt is not deleted and works again if you remove yours.
- Ship only your prompts. Combine
_org_promptswith_disable_default_promptsto take the built-in prompts out of the menus, so users see only the organization set.
If a prompt is rejected
Each prompt is validated independently — one bad entry is skipped, the rest still load. A prompt is
skipped if id, name or text is missing or empty, if
id is duplicated, or if api_type is not a valid connection type. If
_org_id is missing or malformed the whole set is skipped.
Rejections are reported in the Thunderbird Error Console (Tools → Developer Tools → Error
Console), prefixed [ThunderAI Logger | mzta-managed]. Check it after changing a
policy — these warnings appear whether or not debug logging is on.
Special prompt texts
The special features (Add Tags, Spam Filter, Summarize, Translate, Calendar Event, Task) each run a
prompt whose text users can edit on the feature's settings page. _special_prompts_text
replaces that text with yours, for every user. For example, you can make the spam filter apply your
organization's own criteria:
"_special_prompts_text": {
"prompt_spamfilter": "Decide whether this email is spam for ACME. Treat any request to change bank details as spam. Answer only with a JSON object: {\"spamValue\": <0-100>, \"explanation\": \"<one sentence>\"}.\n\n{%mail_html_body%}"
}Neither of the other mechanisms can do this. These texts are stored with the feature's prompt, not in
a preference, and _org_prompts only adds new prompts.
Prompt ids
| Key | Text |
|---|---|
prompt_add_tags | Add Tags |
prompt_spamfilter | Spam Filter |
prompt_summarize | Summarize, the instruction |
prompt_summarize_email_template | Summarize, how each message is presented when several are summarized together |
prompt_summarize_email_separator | Summarize, the separator between those messages. Whitespace only is allowed here. |
prompt_translate_this | Translate |
prompt_get_calendar_event | Calendar Event |
prompt_get_calendar_event_from_clipboard | Calendar Event from the clipboard |
prompt_get_task | Task |
The Calendar Event settings page edits both calendar texts in one field. So a text you set for
prompt_get_calendar_event applies to the clipboard variant too, unless the policy also
names prompt_get_calendar_event_from_clipboard.
Behaviour
- Always enforced. There is no initial-value form:
"_special_prompts_text:locked"is ignored with a warning. - Only the text. Everything else about the feature (its icon, its menu visibility,
its connection) stays the user's. To set the connection, use
_special_prompts_connection. - Read-only on the settings page. The text is shown, marked as managed, and can be selected and copied, but not edited. Save and Reset are disabled.
- Never written to the user's profile. Removing an entry, or the whole key, restores each user's own text exactly as they had it, or the ThunderAI default.
The response format
Most of these features read the AI's answer as JSON, and the text of the prompt is what asks for that format. A text without it would break the feature for everyone. So a text is rejected if it never names the response fields its feature reads:
| Key | Must name | Must contain |
|---|---|---|
prompt_spamfilter | spamValue, explanation | a body placeholder, or no placeholder at all |
prompt_add_tags | tags | |
prompt_get_calendar_event, …_from_clipboard | startDate, endDate, summary | {%selected_text%}, {%selected_html%}, a body placeholder,
or no placeholder at all |
prompt_get_task | summary | |
prompt_translate_this | subject, body, status | {%mail_html_body%} or {%mail_text_body%}, and
{%thunderai_translate_lang%} |
prompt_summarize_email_template | — | a body placeholder, or no placeholder at all |
prompt_summarize, prompt_summarize_email_separator | — | — |
Body placeholders: {%mail_text_body%}, {%mail_html_body%},
{%mail_text_body_or_selected%}, {%mail_html_body_or_selected%}. A text with no
placeholder at all gets the message appended automatically.
A field name is matched as a whole word, case-sensitive, anywhere in the text. This catches a text rewritten without its output format; it does not check that the format itself is well formed. Start from the ThunderAI default text, shown on each feature's settings page, and keep its format part.
If the value of _special_prompts_text is not an object, the whole key is ignored.
Otherwise each entry is checked on its own. An entry is skipped, with a warning, if its key is not
one of the ids above, if its value is not a non-empty string, or if it misses a response field: the
user's own text then stays in effect. A missing placeholder (the third column) is only a warning,
logged at every startup: the text is still enforced, but the message may not reach the AI.
Per-feature connections
Some features can use a connection of their own instead of the main one. In each feature's
settings page, users switch on Use a specific integration and pick a provider.
_special_prompts_connection lets you set that connection from the policy, feature by
feature. For example, you can send spam filtering to a small, cheap model and leave everything else
on the main connection.
This connection is stored with the feature's prompt, not in a preference. That is why the ordinary keys cannot set it and this separate key exists.
"_special_prompts_connection": {
"spamfilter": {
"api_type": "openai_comp_api",
"openai_comp_host": "https://ai-gateway.acme.example/v1",
"openai_comp_api_key": "acme-gateway-token-REPLACE-ME",
"openai_comp_model": "gpt-4o-mini",
"openai_comp_model:locked": false
}
}Shape
The value is an object. Each key names a feature, and each value is that feature's connection:
| Key | Feature |
|---|---|
add_tags | Add Tags |
spamfilter | Spam Filter |
summarize | Summarize |
translate | Translate |
get_calendar_event | Calendar Event, including the "from clipboard" variant |
get_task | Task |
Inside each entry, api_type selects the provider. The other field names are
exactly the connection property names ThunderAI stores on the feature's prompt for that
api_type. There is no renaming, and they match the provider's global key
names:
api_type | Fields |
|---|---|
openai_comp_api | openai_comp_host, openai_comp_model, openai_comp_api_key,
openai_comp_use_v1 (boolean),
openai_comp_chat_name, openai_comp_temperature,
openai_comp_extra_body |
chatgpt_api | chatgpt_api_key, chatgpt_model, chatgpt_developer_messages,
chatgpt_temperature, chatgpt_store (boolean),
chatgpt_reasoning_summary, chatgpt_reasoning_effort,
chatgpt_extra_body,
chatgpt_max_output_tokens (number),
chatgpt_verbosity, chatgpt_text_format,
chatgpt_text_format_schema_name, chatgpt_text_format_schema,
chatgpt_top_p, chatgpt_truncation, chatgpt_prompt_cache_key,
chatgpt_service_tier, chatgpt_safety_identifier,
chatgpt_include_encrypted_reasoning (boolean) |
anthropic_api | anthropic_api_key, anthropic_model, anthropic_version,
anthropic_max_tokens (number),
anthropic_system_prompt, anthropic_temperature,
anthropic_top_p, anthropic_top_k, anthropic_stop_sequences,
anthropic_extended_thinking_budget (number),
anthropic_effort |
google_gemini_api | google_gemini_api_key, google_gemini_model,
google_gemini_system_instruction, google_gemini_thinking_budget,
google_gemini_temperature,
google_gemini_max_output_tokens (number),
google_gemini_top_p, google_gemini_top_k,
google_gemini_extra_body |
ollama_api | ollama_host, ollama_api_key, ollama_model,
ollama_num_ctx (number),
ollama_temperature, ollama_think,
ollama_format_json (boolean),
ollama_keep_alive, ollama_system_prompt,
ollama_extra_options |
Fields not marked otherwise are strings. That includes the temperatures,
*_top_p, *_top_k, google_gemini_thinking_budget and
ollama_think: write "0.7", not 0.7.
You can leave out any field except api_type. For a field the policy does not set, the
feature uses the user's own value if they have one, and otherwise the main connection's value, just
as it does without a policy.
Enforced and initial fields
api_typeis always enforced."api_type:locked": falseis ignored with a warning. Leaving the provider open to change would make no sense while its provider-specific fields are enforced.- Every other field is enforced unless you add a sibling
"<field>:locked": false, as everywhere else in the policy. An unlocked field works like other initial values, with one difference: it applies only while the user has no value of their own for that field. It is never written to their profile. If the user sets a value of their own, it is kept, and it stays after the policy is removed. - A top-level
"_special_prompts_connection:locked"is ignored with a warning.
Implied switch and connection type
Each accepted entry also switches the feature to its own connection and sets the connection type, both locked. It is as if the policy also contained:
"spamfilter_use_specific_integration": true,
"spamfilter_connection_type": "openai_comp_api"On the feature's settings page, the Use a specific integration switch is on and locked, and
the provider select shows your api_type, locked. Enforced fields are locked with the
padlock. Unlocked fields stay editable.
If the same policy also sets those two keys explicitly:
| Explicit key in the same policy | Result |
|---|---|
<feature>_use_specific_integration: false, locked or not | The connection entry is skipped, with a warning. The explicit switch wins, and the feature uses the main connection. |
<feature>_use_specific_integration: true with :locked: false | Enforced anyway, with a warning. |
<feature>_connection_type that differs from api_type, or is
unlocked | Replaced by api_type and locked, with a warning. |
Allowed types and validation
Every feature accepts the same five api_type values: chatgpt_api,
google_gemini_api, anthropic_api, ollama_api and
openai_comp_api. chatgpt_web is rejected for every
feature. The features cannot run on ChatGPT Web, and their settings pages don't offer it.
Each entry is checked on its own, and one bad entry never stops the rest of the policy:
- If the value of
_special_prompts_connectionis not an object, the whole key is ignored. - An entry is skipped as a whole if its key is not one of the six features, if it
is not an object, or if
api_typeis missing or not one of the five types above. - Every other problem skips only that field, and the rest of the entry
applies:
- a field that belongs to a different provider, for example
ollama_hostunder"api_type": "openai_comp_api"; - a value of the wrong type. It is not converted:
"4096"is not4096; - a value that fails its content rule:
- a field that belongs to a different provider, for example
| Field | Must be |
|---|---|
*_host | an http:// or https:// URL |
*_model | not empty |
ollama_num_ctx, anthropic_extended_thinking_budget | an integer, 0 or more |
anthropic_max_tokens | an integer, 1 or more |
google_gemini_max_output_tokens | an integer, 0 or more (0 means not set) |
chatgpt_max_output_tokens | 0 (not set), or an integer of 16 or more |
*_temperature | "", or a string holding a number of 0 or more |
*_top_p | "", or a string holding a number from 0 to 1 |
*_top_k | "", or a string holding an integer, 0 or more |
google_gemini_thinking_budget | "", or a string holding an integer ("-1" is Gemini's "dynamic") |
ollama_think | "", "false", "true", or a level in lowercase letters such as
"low", "medium", "high", "max". The boolean
true/false used before 5.1.0 is still read, with a warning. |
ollama_keep_alive | "", a whole number of seconds, or a duration such as "5m" or
"1h30m" |
*_extra_body, ollama_extra_options,
chatgpt_text_format_schema | "", or a string holding a JSON object |
chatgpt_verbosity | "", "low", "medium" or "high" |
chatgpt_text_format | "", "json_object" or "json_schema" |
chatgpt_truncation | "", "auto" or "disabled" |
chatgpt_service_tier | "", "auto", "default", "flex" or
"priority" |
"<field>:locked" must be a boolean. Any other value is ignored with a warning,
and the field stays enforced. A ":locked" whose field is absent or was rejected is also
reported.
The settings pages don't apply these rules to what users type. The policy does, because a typo there breaks the feature for everyone, and users cannot fix it themselves. Every skip is reported in the Error Console and names the feature and the field (see Troubleshooting).
The organization's key
An API key field works like any API key set by policy. The feature uses it
normally, but the settings page shows only a masked placeholder. The user cannot reveal the
key, fetch the model list with it, or test the connection with it. So set the model
in the entry too, for example openai_comp_model. The key is never saved in the user's
profile and never included in an export.
Changing and removing it
A change takes effect at the next Thunderbird restart, like every other policy change (see Before you start).
Nothing the policy supplies is written into the user's profile. Removing an entry, or the whole key, restores each user's own per-feature connection exactly as they had it, field by field, including their switch and connection type. A user who had none goes back to the main connection. When the policy replaces a value a user had stored, a warning is logged at startup. It names the feature and the field, never the value.
Limiting features to some accounts
The automatic spam filter and automatic Add Tags can be limited to some accounts. Users choose them
with checkboxes on the feature's settings page, stored in spamfilter_enabled_accounts and
add_tags_enabled_accounts. Those two keys cannot be set by policy: they hold account
identifiers that Thunderbird assigns separately in each profile, so no single value would be right on
every machine.
Use spamfilter_enabled_accounts_match and add_tags_enabled_accounts_match
instead. They name accounts by what is the same everywhere, and ThunderAI finds the matching accounts
in each profile:
"spamfilter_enabled_accounts_match": [
"support@acme.example",
"@sales.acme.example",
"local"
],
"add_tags_enabled_accounts_match": ["@acme.example"]| Entry | Matches |
|---|---|
user@acme.example | an account with an identity of that address |
@acme.example or *@acme.example | an account with an identity in that domain |
local | Local Folders |
- Entries are case-insensitive. An account matches when any of its identities does.
- RSS feed accounts are never matched.
- An address with a
+tag (user+tag@acme.example) is not accepted. Use the domain form to cover it. - Only the automatic runs are limited. Running the feature from the menu on a message works as without a policy.
- Always enforced.
":locked": falseis ignored with a warning: there is no control the user could change the value with. - A list that matches nothing means no account, not every account. A warning is
logged when it happens. An empty array (
[]) in the policy is different: it is the same as leaving the key out. - Accounts are matched again each time new mail is processed, so an account added after startup is picked up without a restart.
On the feature's settings page the account checkboxes show the matched accounts, disabled and marked as managed, with a note explaining it. The user's own selection is never changed: removing the key restores it.
Each entry is checked on its own: an entry that is not one of the forms above (including one that is not a string) is skipped with a warning naming its position, and the others still apply. Unlike the other list keys, one bad entry does not discard the whole list. That would fall back to the user's selection, perhaps every account, for a key whose job is to limit where mail is sent. For the same reason, a list whose entries are all invalid is kept as an empty list: no account.
Restrictions
Restrictions are policy-only switches that take something away instead of setting a value.
They have no counterpart in the settings, so the :locked convention does not apply to them.
"_disable_prompt_management": true,
"_disable_default_prompts": true,
"_disable_setup_wizard": trueA restriction is either on (true) or off (false, the same as leaving it
out). Any other value — including the string "true" — is ignored with a warning and the
restriction stays off. A policy that sets only restrictions still counts as active:
users see the managed banner and an explanation next to every disabled control.
| Key | What the user loses | What keeps working |
|---|---|---|
_disable_prompt_management | Creating, duplicating, importing and exporting prompts. Their own existing prompts become read-only and disappear from the menus. | Built-in and organization prompts, fully usable. The user's prompts are never deleted: they stay listed (read-only, with an explanation) and come back exactly as they were when the restriction is lifted. |
_disable_default_prompts | The ThunderAI built-in prompts, in every menu and in the popup. They stay listed, dimmed and explained, on the prompt pages. | The special features — Add Tags, Summarize, Translate, Spam Filter, Calendar Event, Task — plus the user's own prompts and the organization's. Menu positions and custom icons of the built-ins are kept. |
_disable_setup_wizard | The setup wizard: its buttons in the options page are disabled, the welcome-page banner is hidden, and opening the wizard by its URL shows Setup wizard unavailable. | Everything else. In the popup, the "run the setup wizard" link is replaced by a note that the connection is configured centrally. |
The first two are independent and can be combined: they cover different prompts (the user's own and the built-in ones), and each shows its own explanation.
Export is disabled on purpose under _disable_prompt_management: an
exported file carries the prompt texts and, with include API settings, can carry provider
credentials too.
_disable_setup_wizard only makes sense together with an enforced
connection (connection_type and its provider settings). On its own it just
leaves users with no guided way to configure ThunderAI. Custom data placeholders are not covered by
any restriction.
Settings that require a browser permission
⚠️ A policy can switch these features on, but cannot grant their permission
Two ThunderAI features need an optional permission that the add-on can only request
interactively, when the user turns the feature on in its settings page. A policy
value does not go through that path: setting the key to true switches the feature on
but does not grant the permission. The feature then appears enabled and does
nothing until the permission has been granted.
| Key | Permission | Settings page | Without the permission |
|---|---|---|---|
get_calendar_event_from_clipboard | clipboardRead | Calendar Event | The "calendar event from clipboard" menu item is shown, but cannot read the clipboard: using it only shows the error Could not read clipboard. Please check permissions. |
spamfilter_skip_addressbook | addressBooks | Spam Filter | The spam filter cannot check whether a sender is in the user's address books, so those
senders are not skipped and are analyzed like any other. Nothing is reported
to the user. Note that this key defaults to true, so this applies even if you
leave it out of the policy. |
What you can do
- Grant the permission in advance, if your deployment method allows it. Thunderbird's
ExtensionSettingspolicy, as documented in the Thunderbird policy templates, controls how an add-on is installed and updated, but lists no setting to pre-grant an add-on's optional permissions. This has not been verified for every Thunderbird version: check the templates for the version you deploy. If your tooling can grant the permission some other way, the feature works as soon as it is granted. - Otherwise, ask users to grant it once. Each user opens the ThunderAI settings page
listed above and turns the option on, then accepts the permission prompt. The prompt appears only
when the option is switched on: if it already shows as on, the user switches it off and on
again. This needs a control the user can change. If the key is locked by the policy, the
control is disabled and the prompt cannot be triggered from there. In that case either set
the key with
"<key>:locked": false, so the user can grant the permission, or leave the key out of the policy. - Check the result. Keys that ThunderAI rejects are reported in the Thunderbird
Error Console (Tools → Developer Tools → Error Console, filter on
ThunderAI), even with debug logging off — look there first. A missing permission, though, is not reported: the key is valid, it is the permission that is absent. Confirm it on a test machine using the symptom in the table above.
For example, both features on, as initial values the user can change to grant the permission:
"get_calendar_event_from_clipboard": true,
"get_calendar_event_from_clipboard:locked": false,
"spamfilter_skip_addressbook": true,
"spamfilter_skip_addressbook:locked": falseKeep in mind that an unlocked value is only a starting point: a user who already set the option keeps their own value, and can turn the feature off again (see Enforced vs. initial values).
As with every policy change, this takes effect only at the next Thunderbird restart — see Before you start.
Key reference
Every key below is a ThunderAI setting name, used verbatim as the policy key. Values must match the
type shown: a wrong type is ignored with a warning and is not coerced —
"true" is not true, and "70" is not 70.
Array keys (add_tags_exclusions, spamfilter_skip_addresses,
spamfilter_block_addresses, summarize_auto_senders_list) must contain only strings. A single element
of another type makes ThunderAI ignore the whole value, with a warning — it is not filtered out. The
account lists (*_enabled_accounts_match) are the exception: they are checked entry by entry,
see Limiting features to some accounts.
Values the add-on cannot use
A value of the right type is also checked against what the setting accepts, and rejected the same way — ignored with a warning naming the key, never adjusted:
| Keys | Must be |
|---|---|
connection_type | one of the connection types listed in the key reference. Not "", which means
"not configured". |
<feature>_connection_type | one of the five API types. Not chatgpt_web: the features cannot run on it. |
reply_type, diff_granularity, summarize_display_mode,
summarize_auto, translate_auto | one of the values listed for the key: the choices its settings page offers |
| every number | an integer, 0 or more. spamfilter_threshold: 0 to 100.
add_tags_maxnum, summarize_max_messages,
max_prompt_length, batch_max_concurrency: 1 or more. |
calendar_timezone | "", or a time zone name as the Calendar Event settings list it, such as
"Europe/Rome" (case-sensitive) |
the provider keys (openai_comp_host, ollama_num_ctx,
chatgpt_temperature…) | the same rules as the fields of a per-feature connection: hosts are
http(s):// URLs, models are not empty, temperatures are strings holding a number…
See the rules table. |
Free-text keys (languages, signature name, system prompts…) accept any string. Provider-specific
choices such as chatgpt_reasoning_effort or anthropic_effort are not checked:
the values each provider accepts change with its models. The exceptions are the ChatGPT choices the
settings page offers as a fixed list (chatgpt_verbosity, chatgpt_text_format,
chatgpt_truncation, chatgpt_service_tier): those must be one of its values, see
the rules table.
One rule spans two keys. An automatic summary is always shown inline. So when the
policy sets summarize_auto to 2 or 3 and also sets
summarize_display_mode, the display mode becomes "inline", with a warning, and
takes the lock of summarize_auto: enforced if that is enforced, an initial value if it has
":locked": false.
All keys
137 of ThunderAI's 146 settings can be set by policy; the other nine are listed at the end. Two settable keys need a permission the policy cannot grant — see Settings that require a browser permission.
In strict mode, every key not set by the policy is locked at the default
shown in this reference, except the per-machine keys, which the reference marks as not settable by
policy at the end of the list, and the two *_enabled_accounts_match keys.
Group by Section to browse by area, or A–Z when you already have a key name and need its type. The search box matches key names and descriptions; click a key to copy it.
Structure and organization 7
- Structure and organization
Values: true
trueturns strict mode on: every setting the policy does not set is locked at its default. See Strict mode.TypebooleanDefaultfalse - Structure and organization
since v5.1.0
Required for _org_prompts. Lowercase letters, digits and hyphens only, no underscore. Choose it once and never change it.
TypestringDefault"" - Structure and organization
since v5.1.0
Organization name shown beside the managed settings.
TypestringDefault"" - Structure and organization
since v5.1.0
The shared, read-only prompt set. See the Organization prompts section.
TypearrayDefault[] - Structure and organization
since v5.1.0
Policy schema version of this block.
TypenumberDefault1 - Structure and organization
since v5.1.0
Values: prompt_add_tags | prompt_spamfilter | prompt_summarize | prompt_summarize_email_template | prompt_summarize_email_separator | prompt_translate_this | prompt_get_calendar_event | prompt_get_calendar_event_from_clipboard | prompt_get_task
The enforced text of the special prompts. Always enforced. See Special prompt texts.
TypeobjectDefault{} - Structure and organization
Setting names that stay user-editable in strict mode. Only used when
_lock_unlistedistrue. See Strict mode.TypearrayDefault[]
Restrictions 3
- Restrictions
since v5.1.0
Values: true
Removes the ThunderAI built-in prompts from the menus, typically leaving only the organization ones. Special features (tags, summarize, translate, spam filter, calendar, task) are not affected. See Restrictions.
TypebooleanDefaultfalse - Restrictions
since v5.1.0
Values: true
No prompt can be created, duplicated, imported or exported. The user's own prompts become read-only and are removed from the menus, but are never deleted. Built-in and organization prompts keep working. See Restrictions.
TypebooleanDefaultfalse - Restrictions
since v5.1.0
Values: true
The setup wizard cannot be opened from anywhere, not even by its direct URL. Use it together with a locked connection. See Restrictions.
TypebooleanDefaultfalse
Connection 2
- Connection
since v5.1.0
Values: chatgpt_web | chatgpt_api | google_gemini_api | anthropic_api | ollama_api | openai_comp_api
Empty means "not configured" and triggers the setup wizard - set it to skip onboarding entirely.
TypestringDefault"" - Connection
since v5.1.0
TypebooleanDefaultfalse
OpenAI-compatible 7
- OpenAI-compatible
since v5.1.0
TypestringDefault"" - OpenAI-compatible
since v5.1.0
TypestringDefault"OpenAI Comp" - OpenAI-compatible
since v5.1.0
TypestringDefault"" - OpenAI-compatible
since v5.1.0
Point this at your internal gateway.
TypestringDefault"" - OpenAI-compatible
since v5.1.0
TypestringDefault"" - OpenAI-compatible
since v5.1.0
TypestringDefault"" - OpenAI-compatible
since v5.1.0
TypebooleanDefaulttrue
OpenAI / ChatGPT API 19
- OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypebooleanDefaultfalse - OpenAI / ChatGPT API
since v5.1.0
0means not set. Otherwise at least16.TypenumberDefault0 - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
Values: "" | auto | default | flex | priority
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
TypebooleanDefaultfalse - OpenAI / ChatGPT API
since v5.1.0
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
Values: "" | json_object | json_schema
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
Empty, or a string holding a JSON object.
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
Name of the schema used with
json_schema.TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
Empty, or a string holding a number from 0 to 1.
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
Values: "" | auto | disabled
TypestringDefault"" - OpenAI / ChatGPT API
since v5.1.0
Values: "" | low | medium | high
TypestringDefault""
Anthropic / Claude 11
- Anthropic / Claude
since v5.1.0
TypestringDefault"" - Anthropic / Claude
since v5.1.0
TypestringDefault"" - Anthropic / Claude
since v5.1.0
TypenumberDefault0 - Anthropic / Claude
since v5.1.0
TypenumberDefault4096 - Anthropic / Claude
since v5.1.0
TypestringDefault"" - Anthropic / Claude
since v5.1.0
One stop sequence per line.
TypestringDefault"" - Anthropic / Claude
since v5.1.0
TypestringDefault"" - Anthropic / Claude
since v5.1.0
TypestringDefault"" - Anthropic / Claude
since v5.1.0
Empty, or a string holding an integer, 0 or more.
TypestringDefault"" - Anthropic / Claude
since v5.1.0
Empty, or a string holding a number from 0 to 1.
TypestringDefault"" - Anthropic / Claude
since v5.1.0
TypestringDefault"2023-06-01"
Google Gemini 9
- Google Gemini
since v5.1.0
TypestringDefault"" - Google Gemini
since v5.1.0
Empty, or a string holding a JSON object.
TypestringDefault"" - Google Gemini
since v5.1.0
0means not set.TypenumberDefault0 - Google Gemini
since v5.1.0
TypestringDefault"" - Google Gemini
since v5.1.0
TypestringDefault"" - Google Gemini
since v5.1.0
TypestringDefault"" - Google Gemini
since v5.1.0
TypestringDefault"" - Google Gemini
since v5.1.0
Empty, or a string holding an integer, 0 or more.
TypestringDefault"" - Google Gemini
since v5.1.0
Empty, or a string holding a number from 0 to 1.
TypestringDefault""
Ollama 10
- Ollama
since v5.1.0
For an Ollama server behind authentication.
TypestringDefault"" - Ollama
since v5.1.0
Empty, or a string holding a JSON object.
TypestringDefault"" - Ollama
since v5.1.0
TypebooleanDefaultfalse - Ollama
since v5.1.0
TypestringDefault"" - Ollama
since v5.1.0
Empty, a whole number of seconds (
-1keeps the model loaded) or a duration such as"5m"or"1h30m".TypestringDefault"" - Ollama
since v5.1.0
TypestringDefault"" - Ollama
since v5.1.0
TypenumberDefault0 - Ollama
since v5.1.0
TypestringDefault"" - Ollama
since v5.1.0
TypestringDefault"" - Ollama
since v5.1.0
Values: "" | false | true | low | medium | high | max
A level, not a flag:
""(server default),"false","true", or a level such as"low","medium","high","max". Any lowercase word is accepted, because a model may report its own levels. The booleantrue/falseof policies written before 5.1.0 is still read, as"true"/"false", with a warning.TypestringDefault""
ChatGPT Web 5
- ChatGPT Web
since v5.1.0
TypestringDefault"" - ChatGPT Web
since v5.1.0
TypenumberDefault1000 - ChatGPT Web
since v5.1.0
TypestringDefault"" - ChatGPT Web
since v5.1.0
TypestringDefault"" - ChatGPT Web
since v5.1.0
TypebooleanDefaultfalse
Features 6
- Features
since v5.1.0
TypebooleanDefaultfalse - Features
since v5.1.0
TypebooleanDefaulttrue - Features
since v5.1.0
TypebooleanDefaulttrue - Features
since v5.1.0
TypebooleanDefaultfalse - Features
since v5.1.0
TypebooleanDefaultfalse - Features
since v5.1.0
TypebooleanDefaulttrue
Tagging 14
- Tagging
since v5.1.0
TypebooleanDefaultfalse - Tagging
since v5.1.0
TypebooleanDefaultfalse - Tagging
since v5.1.0
Auto tagging normally skips the sent folder; this re-enables it.
TypebooleanDefaultfalse - Tagging
since v5.1.0
TypebooleanDefaulttrue - Tagging
since v5.1.0
TypebooleanDefaultfalse - Tagging
since v5.1.0
TypestringDefault"" - Tagging
since v5.1.0
Accounts automatic Add Tags runs on, as an array of addresses, domains (
@acme.example) orlocal. Always enforced. See Limiting features to some accounts.TypearrayDefault[] - Tagging
since v5.1.0
Tags that are never assigned, as an array of strings. Matched as a substring, case-insensitive, unless add_tags_exclusions_exact_match is on. When locked, the tag dialog no longer offers to exclude a tag.
TypearrayDefault[] - Tagging
since v5.1.0
TypebooleanDefaultfalse - Tagging
since v5.1.0
TypebooleanDefaulttrue - Tagging
since v5.1.0
TypebooleanDefaulttrue - Tagging
since v5.1.0
TypebooleanDefaultfalse - Tagging
since v5.1.0
Max messages tagged at once from the context menu (
0= no limit). Above it the user is warned and the operation is blocked. Automatic tagging is not capped.TypenumberDefault0 - Tagging
since v5.1.0
At least 1.
TypenumberDefault3
Spam filter 7
- Spam filter
since v5.1.0
Senders always reported as spam (score 100), without asking the AI.
TypearrayDefault[] - Spam filter
since v5.1.0
Accounts the automatic spam filter runs on, as an array of addresses, domains (
@acme.example) orlocal. Always enforced. See Limiting features to some accounts.TypearrayDefault[] - Spam filter
since v5.1.0
If true, the automatic spam filter runs only on inbox folders.
TypebooleanDefaultfalse - Spam filter
since v5.1.0
TypebooleanDefaulttrue - Spam filter
since v5.1.0
Needs the
addressBookspermission, which a policy cannot grant. See Settings that require a browser permission.TypebooleanDefaulttrue - Spam filter
since v5.1.0
TypearrayDefault[] - Spam filter
since v5.1.0
From 0 to 100.
TypenumberDefault70
Summarize 9
- Summarize
since v5.1.0
0 disabled, 1 manual button, 2 on message open, 3 on receive. With 2 or 3 a
summarize_display_modeset in the same policy becomes"inline"and takes the lock of this key.TypenumberDefault1 - Summarize
since v5.1.0
TypebooleanDefaultfalse - Summarize
since v5.1.0
Sender addresses or domain patterns ("@domain.com", "*@domain.com").
TypearrayDefault[] - Summarize
since v5.1.0
Values: inline | webchat
inline or webchat. Forced to
"inline"when the policy setssummarize_autoto 2 or 3.TypestringDefault"inline" - Summarize
since v5.1.0
Always write the summary in
summarize_lang(ordefault_chatgpt_lang).TypebooleanDefaultfalse - Summarize
since v5.1.0
Summary language when
summarize_force_langis on; falls back ondefault_chatgpt_lang.TypestringDefault"" - Summarize
since v5.1.0
0 = no limit, otherwise max characters shown inline.
TypenumberDefault0 - Summarize
since v5.1.0
Max messages summarized at once in webchat mode. At least 1.
TypenumberDefault20 - Summarize
since v5.1.0
TypebooleanDefaultfalse
Translate 4
- Translate
since v5.1.0
0 disabled, 1 manual button, 2 on message open, 3 on receive.
TypenumberDefault0 - Translate
since v5.1.0
Languages not to translate.
TypestringDefault"" - Translate
since v5.1.0
Target language; falls back on default_chatgpt_lang.
TypestringDefault"" - Translate
since v5.1.0
0 = no limit, otherwise max characters shown inline.
TypenumberDefault0
Calendar and tasks 10
- Calendar and tasks
since v5.1.0
Append a link to the source email to the event description. Added by ThunderAI, never sent to the AI.
TypebooleanDefaultfalse - Calendar and tasks
since v5.1.0
TypebooleanDefaultfalse - Calendar and tasks
since v5.1.0
If true, do not ask for a selection but use the full prompt. The Calendar Event prompt must then contain
{%mail_text_body_or_selected%}or{%mail_html_body_or_selected%}, otherwise the message body is not sent to the AI: ThunderAI warns in the Error Console at startup and, when the key is locked, shows a note on the Calendar Event settings page.TypebooleanDefaultfalse - Calendar and tasks
since v5.1.0
Ask the AI for a reminder. If off, Thunderbird's default reminder is used.
TypebooleanDefaultfalse - Calendar and tasks
since v5.1.0
Natural-language reminder rules added to the prompt when
calendar_reminder_enabledis on.TypestringDefault"" - Calendar and tasks
since v5.1.0
Empty, or a time zone name such as
"Europe/Rome", written exactly as the time zone list in the Calendar Event settings shows it.TypestringDefault"" - Calendar and tasks
since v5.1.0
Needs the
clipboardReadpermission, which a policy cannot grant. See Settings that require a browser permission.TypebooleanDefaultfalse - Calendar and tasks
since v5.1.0
Same as
calendar_append_email_link, for the task description.TypebooleanDefaultfalse - Calendar and tasks
since v5.1.0
Same as
calendar_reminder_enabled, for tasks.TypebooleanDefaultfalse - Calendar and tasks
since v5.1.0
Same as
calendar_reminder_rules, for tasks.TypestringDefault""
Per-feature connection overrides 13
- Per-feature connection overrides
since v5.1.0
Values: add_tags | spamfilter | summarize | translate | get_calendar_event | get_task
The connection of each feature, enforced by the policy. See Per-feature connections.
TypeobjectDefault{} - Per-feature connection overrides
since v5.1.0
Values: chatgpt_api | google_gemini_api | anthropic_api | ollama_api | openai_comp_api
chatgpt_web is not accepted: the features cannot run on ChatGPT Web.
TypestringDefault"chatgpt_api" - Per-feature connection overrides
since v5.1.0
TypebooleanDefaultfalse - Per-feature connection overrides
since v5.1.0
Values: chatgpt_api | google_gemini_api | anthropic_api | ollama_api | openai_comp_api
chatgpt_web is not accepted: the features cannot run on ChatGPT Web.
TypestringDefault"chatgpt_api" - Per-feature connection overrides
since v5.1.0
TypebooleanDefaultfalse - Per-feature connection overrides
since v5.1.0
Values: chatgpt_api | google_gemini_api | anthropic_api | ollama_api | openai_comp_api
chatgpt_web is not accepted: the features cannot run on ChatGPT Web.
TypestringDefault"chatgpt_api" - Per-feature connection overrides
since v5.1.0
TypebooleanDefaultfalse - Per-feature connection overrides
since v5.1.0
Values: chatgpt_api | google_gemini_api | anthropic_api | ollama_api | openai_comp_api
chatgpt_web is not accepted: the features cannot run on ChatGPT Web.
TypestringDefault"chatgpt_api" - Per-feature connection overrides
since v5.1.0
TypebooleanDefaultfalse - Per-feature connection overrides
since v5.1.0
Values: chatgpt_api | google_gemini_api | anthropic_api | ollama_api | openai_comp_api
chatgpt_web is not accepted: the features cannot run on ChatGPT Web.
TypestringDefault"chatgpt_api" - Per-feature connection overrides
since v5.1.0
TypebooleanDefaultfalse - Per-feature connection overrides
since v5.1.0
Values: chatgpt_api | google_gemini_api | anthropic_api | ollama_api | openai_comp_api
chatgpt_web is not accepted: the features cannot run on ChatGPT Web.
TypestringDefault"chatgpt_api" - Per-feature connection overrides
since v5.1.0
TypebooleanDefaultfalse
General 12
- General
since v5.1.0
Max messages processed at once by a batch (automatic Add Tags, Spam Filter, Summarize and Translate, on receive or from the context menu). At least
1.TypenumberDefault1 - General
since v5.1.0
Show the token counts reported by the API under each answer in the chat, and a session total.
TypebooleanDefaulttrue - General
since v5.1.0
TypestringDefault"" - General
since v5.1.0
TypestringDefault"" - General
since v5.1.0
Values: words | sentences
words or sentences.
TypestringDefault"words" - General
since v5.1.0
TypebooleanDefaultfalse - General
since v5.1.0
TypebooleanDefaulttrue - General
since v5.1.0
In characters, at least 1.
TypenumberDefault30000 - General
since v5.1.0
TypebooleanDefaultfalse - General
since v5.1.0
Values: reply_all | reply_sender
reply_all or reply_sender.
TypestringDefault"reply_all" - General
since v5.1.0
In milliseconds, before a hung special command is aborted.
TypenumberDefault120000
No keys match your search.
Keys that cannot be set by policy
These are per-machine or per-profile state, not configuration. Setting them is ignored with a warning.
| Key | Why |
|---|---|
add_tags_enabled_accounts | Account identifiers, specific to a profile. Use add_tags_enabled_accounts_match instead. |
api_webchat_font_scale | Local UI zoom. |
chatgpt_win_height | Window geometry and position, specific to a machine display. |
chatgpt_win_left | Window geometry and position, specific to a machine display. |
chatgpt_win_save_position | Window geometry and position, specific to a machine display. |
chatgpt_win_top | Window geometry and position, specific to a machine display. |
chatgpt_win_width | Window geometry and position, specific to a machine display. |
custom_prompts_view | Layout of the Manage Prompts page. |
spamfilter_enabled_accounts | Account identifiers, specific to a profile. Use spamfilter_enabled_accounts_match instead. |
Troubleshooting
Open about:policies to confirm Thunderbird loaded the file at all. If
ThunderAI's settings are not listed there, the problem is the file's location or its JSON syntax, not
ThunderAI.
Open the Error Console (Tools → Developer Tools → Error Console) and filter on
ThunderAI. Rejected keys and prompts are reported there, each naming what was wrong.
These warnings are not gated on debug logging.
| Symptom | Likely cause |
|---|---|
| Nothing applied at all | File in the wrong place, or invalid JSON. Check about:policies. |
| One key ignored, warning about its type | Value type does not match — "true" is not true,
"70" is not 70. |
| One key ignored, warning that it "must be" something | The type is right but the value is not usable: not one of the listed values, out of range, a host that is not a URL, an unknown time zone. See Values the add-on cannot use. |
| A list key ignored as a whole | One of its elements is not a string. Array keys must contain only strings. |
| One key ignored, "cannot be set by policy" | It is in the excluded list above. |
| Settings still editable | The key has "<key>:locked": false, or you are looking at a key you did
not set. |
| A change did nothing | Thunderbird was not restarted. Policies are read only at startup. |
| Organization prompts missing | _org_id missing or contains an underscore/uppercase — the whole set is then
skipped. |
| One prompt missing | Missing id, name or text, a duplicate
id, or an invalid api_type. |
| A special prompt text not applied | Unknown prompt id, an empty text, or a text that does not name the response fields its feature
reads (for example spamValue). The user's own text stays in effect. See
Special prompt texts. |
| A special prompt text applied, but the feature ignores the message | The text has placeholders but none that brings in the message (or, for Translate, no
{%thunderai_translate_lang%}). A warning is logged at every startup. |
| Automatic spam filter or Add Tags runs on no account | *_enabled_accounts_match matches no account in that profile, or all its entries
were rejected. See Limiting features to some accounts. |
Summaries shown inline although the policy sets webchat | Expected: summarize_auto is 2 or 3, and automatic
summaries are always inline. |
| A per-feature connection ignored as a whole | Unknown feature key, a missing api_type, chatgpt_web, or the same
policy sets <feature>_use_specific_integration to false. See
Per-feature connections. |
| One field of a per-feature connection ignored | It belongs to a different provider, has the wrong type (temperatures are strings), or fails
its content rule, such as a host that is not an http(s):// URL. |
| A restriction has no effect | The value is not the boolean true — "true" or 1 is
ignored and the restriction stays off. |
| Users' own prompts vanished from the menus | _disable_prompt_management is on. The prompts are not deleted: they are still
listed read-only and return when the restriction is removed. |
| Built-in prompts vanished from the menus | _disable_default_prompts is on. Special features such as Summarize or Add
Tags are not affected. |
| An unlocked value did not apply | The user had already set that preference; their value wins. Enforce it instead. |
| Calendar from clipboard or address book skipping enabled, but not working | The feature's permission was never granted. See Settings that require a browser permission. |
Calendar Event ignores the message body, warning about calendar_no_selection | The option is on but the Calendar Event prompt has neither
{%mail_text_body_or_selected%} nor {%mail_html_body_or_selected%}. |
| Users cannot test the connection or fetch models | Expected when the API key comes from the policy. Set the model in the policy. See API keys set by policy. |
Notes
- ThunderAI requests no additional permissions for any of this. Two features have a permission of their own that a policy cannot grant — see Settings that require a browser permission.
- With no policy installed, ThunderAI behaves exactly as it does for any individual user.