System: FreeScout helpdesk — operations mailbox
Owner: operator
Purpose: One-time provisioning of FREESCOUT_OPERATIONS_MAILBOX_ID for the auto-ticketing pipeline
Introduced: 2026-05-05 (PRs #1155, #1156)
Last reviewed: 2026-05-05
Related runbooks:
- Pipeline overview: docs/ops/runbooks/auto-ticketing-pipeline-overview.md
- Rollout sequence: docs/ops/runbooks/auto-ticketing-rollout.md
- Incident response: docs/ops/runbooks/auto-ticketing-runbook.md
- FreeScout system: docs/ops/runbooks/freescout.md
The auto-ticketing pipeline (PRs #1155 and #1156) routes all auto-created tickets to a dedicated operations mailbox in FreeScout. This mailbox is separate from the customer-facing support mailbox. The mailbox must exist in FreeScout before the flags are flipped on.
This SOP covers: 1. Creating the operations mailbox in FreeScout (operator-only, UI action) 2. Retrieving the mailbox ID via the FreeScout API 3. Storing the mailbox ID in Infisical 4. Setting the env var on both Heroku apps 5. Restarting dynos to pick up the new env var 6. Verifying the Investigate button works end-to-end
Important: Vault folder /MooseQuest/freescout/ must exist in Infisical before writing any secret to that path. Writing to a non-existent path returns HTTP 404. If the folder does not exist, create it via the Infisical API before the secret write step (step 3 below covers this).
https://tickets.raxx.app/ returns 200). If not, resolve the FreeScout incident first — see docs/ops/runbooks/freescout.md.FREESCOUT_API_KEY is already set on both Heroku apps. Verify:heroku config:get FREESCOUT_API_KEY --app raxx-console-staging
heroku config:get FREESCOUT_API_KEY --app raxx-console-prod
If either is missing, retrieve from Infisical and set it first:
FS_KEY=$(infisical secrets get FREESCOUT_API_KEY --path /MooseQuest/freescout --env prod --plain)
heroku config:set FREESCOUT_API_KEY="$FS_KEY" --app raxx-console-staging >/dev/null 2>&1
heroku config:set FREESCOUT_API_KEY="$FS_KEY" --app raxx-console-prod >/dev/null 2>&1
This step is operator-only. The FreeScout API does not support mailbox creation — it must be done through the admin UI.
https://tickets.raxx.app (passes through Cloudflare Access first — use your kris@moosequest.net credentials).Operations (this is the internal label; customers never see it)
- Email: ops@raxx.app (this is the from-address for any operator replies; it does not need to be a receiving address since auto-ticketing only creates tickets, it does not receive inbound email via this mailbox)
- Reply-to name: Raxx OpsNote on email delivery: The operations mailbox is for auto-filed internal tickets. If you configure it to receive inbound email, set up a Postmark relay as described in docs/ops/runbooks/freescout-postmark-relay.md. For the auto-ticketing pipeline, inbound email is not required — tickets are created via the API only.
FS_KEY=$(infisical secrets get FREESCOUT_API_KEY --path /MooseQuest/freescout --env prod --plain)
curl -s \
-H "Authorization: Bearer $FS_KEY" \
https://tickets.raxx.app/api/mailboxes \
| python3 -m json.tool
The response is a JSON array of mailbox objects. Each object has an id (integer) and a name field. Locate the entry with "name": "Operations" and note its id.
Example response (IDs are illustrative — your instance will differ):
{
"_embedded": {
"mailboxes": [
{"id": 1, "name": "Support"},
{"id": 3, "name": "Operations"}
]
}
}
In this example, the operations mailbox ID is 3. Record this value.
Check that the vault folder exists before writing. Writing to a non-existent path returns HTTP 404 with no helpful error message.
# Check if the folder exists (a successful list means it does)
infisical secrets --path /MooseQuest/freescout --env prod list 2>/dev/null | head -5
If the command returns secrets, the folder exists. If it errors with a 404 or returns nothing, create the folder first via the Infisical API:
# Create the folder (only needed if it does not already exist)
INFISICAL_TOKEN=$(infisical service-token get --plain)
curl -s -X POST \
-H "Authorization: Bearer $INFISICAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"workspaceId": "<your-project-id>", "environment": "prod", "name": "freescout", "path": "/MooseQuest"}' \
"https://vault.raxx.app/api/v1/folders" \
| python3 -m json.tool
Once the folder exists, write the secret. Replace <MAILBOX_ID> with the numeric ID from Step 2:
infisical secrets set FREESCOUT_OPERATIONS_MAILBOX_ID=<MAILBOX_ID> \
--path /MooseQuest/freescout \
--env prod
Verify the write succeeded:
infisical secrets get FREESCOUT_OPERATIONS_MAILBOX_ID \
--path /MooseQuest/freescout \
--env prod \
--plain
Expected output: the numeric mailbox ID (e.g. 3). No other text.
Retrieve the mailbox ID from Infisical and set it on both apps. The value is not sensitive (it is a public numeric ID visible in any API response), but silencing the output is standard practice to prevent adjacent secrets from appearing in shell history:
MBID=$(infisical secrets get FREESCOUT_OPERATIONS_MAILBOX_ID \
--path /MooseQuest/freescout \
--env prod \
--plain)
heroku config:set FREESCOUT_OPERATIONS_MAILBOX_ID="$MBID" --app raxx-console-staging >/dev/null 2>&1
heroku config:set FREESCOUT_OPERATIONS_MAILBOX_ID="$MBID" --app raxx-console-prod >/dev/null 2>&1
Heroku applies the new env var on the next dyno restart. Trigger restarts immediately to avoid a silent window where the env var is set in config but not yet available to the running process:
heroku restart --app raxx-console-staging
heroku restart --app raxx-console-prod
Wait approximately 30 seconds for the dynos to cycle, then confirm the env var is visible to the running process:
heroku config:get FREESCOUT_OPERATIONS_MAILBOX_ID --app raxx-console-staging
heroku config:get FREESCOUT_OPERATIONS_MAILBOX_ID --app raxx-console-prod
Both must return the numeric mailbox ID.
This step requires FLAG_CONSOLE_INVESTIGATE_FROM_STATUS to be on. If it is not yet on, complete this verification after the flag is flipped per docs/ops/runbooks/auto-ticketing-rollout.md.
/console/status on the staging console (purple banner).https://tickets.raxx.app/conversation/<id>).auto:status.If step 5 shows an error instead of a URL:
- Check Heroku logs for the FreeScout API response: heroku logs --app raxx-console-staging | grep -iE "freescout|investigate"
- Re-run the diagnostic chain in docs/ops/runbooks/auto-ticketing-runbook.md (Failure mode 2: Tickets not creating).
| Secret | Infisical path | Description |
|---|---|---|
FREESCOUT_API_KEY |
/MooseQuest/freescout/FREESCOUT_API_KEY |
FreeScout API key for all console → FreeScout API calls |
FREESCOUT_OPERATIONS_MAILBOX_ID |
/MooseQuest/freescout/FREESCOUT_OPERATIONS_MAILBOX_ID |
Numeric ID of the Operations mailbox; set once at provisioning |
GET /api/mailboxesFREESCOUT_OPERATIONS_MAILBOX_ID written to Infisical at /MooseQuest/freescout/raxx-console-staging (silenced)raxx-console-prod (silenced)heroku config:get FREESCOUT_OPERATIONS_MAILBOX_ID returns non-empty on both appsdocs/ops/runbooks/auto-ticketing-rollout.md