Raxx · internal docs

internal · gated ↑ index

FreeScout Operations Mailbox — First-Deploy Provisioning SOP

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


Overview

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).


Prerequisites

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

Step 1 — Create the operations mailbox in FreeScout

This step is operator-only. The FreeScout API does not support mailbox creation — it must be done through the admin UI.

  1. Open https://tickets.raxx.app (passes through Cloudflare Access first — use your kris@moosequest.net credentials).
  2. In the top navigation bar, click the gear icon (Settings).
  3. In the left sidebar, click Mailboxes.
  4. Click New Mailbox (or Create Mailbox depending on your FreeScout version).
  5. Fill in the mailbox fields: - Name: 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 Ops
  6. Click Save.
  7. The new mailbox appears in the mailbox list. Note the mailbox name — you will retrieve its numeric ID in the next step.

Note 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.


Step 2 — Retrieve the mailbox ID via the FreeScout API

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.


Step 3 — Store the mailbox ID in Infisical

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.


Step 4 — Set the env var on both Heroku apps

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

Step 5 — Restart console dynos

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.


Step 6 — Verify end-to-end (Investigate button)

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.

  1. Navigate to /console/status on the staging console (purple banner).
  2. Find a surface showing DEGRADED (or wait for one; alternatively, test on a surface known to be intermittently degraded).
  3. Hover the degraded tile. Confirm the Investigate button appears.
  4. Click Investigate.
  5. Confirm: the console displays a FreeScout ticket URL (format: https://tickets.raxx.app/conversation/<id>).
  6. Open the URL. Confirm: the ticket exists in the FreeScout Operations mailbox and carries the tag auto:status.
  7. Click Investigate again within 60 minutes. Confirm: the same ticket URL is returned — no duplicate is created.

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).


Vault reference

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

Post-provisioning checklist