Skip to content

Environment Variables ​

API Configuration ​

These variables are set in api/local.settings.json for local development or in Azure Function App → Configuration for production.

VariableRequiredDescriptionDefault
COSMOS_ENDPOINT✅Cosmos DB endpoint URLhttps://localhost:8081 (emulator)
COSMOS_KEY✅Cosmos DB primary keyEmulator key
COSMOS_DATABASE✅Database namechoirtickets
JWT_SECRET✅Secret for signing session tokens—
FRONTEND_URL✅Frontend URL for magic link callbackshttp://localhost:5173
RESEND_API_KEY❌Resend API key for sending emails—
FROM_EMAIL❌Sender email addressonboarding@resend.dev
E2E_TEST_MODE❌Enable test-mode endpoints (dangerous)false
NODE_ENV❌Environment: development or productiondevelopment
REFRESH_TOKEN_TTL_DAYS❌Positive integer lifetime for refresh tokens, in days90
COSMOS_REQUEST_TIMEOUT_MS❌Numeric Cosmos request timeout in milliseconds for key-based clientsCosmos SDK default
SHARED_EVENTS_ENABLED❌Exact true or false switch for Shared Event creation and mutationfalse
SHARED_EVENTS_ORGANIZATION_ALLOWLIST❌Comma-separated organization IDs allowed to mutate Shared EventsEmpty, no organization restriction
PRE_HANDOUT_SALES_ENABLED❌Exact true enables new pre-handout sales and fulfillment adoption within the configured modefalse
PRE_HANDOUT_SALES_MODE❌Exact allowlist or all-organizations. Global acceptance requires explicit all-organizations and the enabled switch. Invalid values deny acceptance.allowlist
PRE_HANDOUT_SALES_ORGANIZATION_ALLOWLIST❌Comma-separated canonical organization IDs. In allowlist mode both owner and seller must be listed; missing or empty denies acceptance. Malformed or duplicate nonempty entries deny acceptance in either mode.Empty, no organizations allowed in allowlist mode
GITHUB_FEEDBACK_TOKEN❌Token with issue-write access for optional GitHub issue creation from beta feedbackNone
DELEGATED_MEMBER_CATALOG_ENABLED❌Rollout switch that shows a partner organization's delegated concerts in that partner's own member catalog (Home, Bestill billetter)false
DELEGATED_MEMBER_ORDERING_ENABLED❌Rollout switch that allows a partner organization's members to create new self-service orders for a delegated concertfalse
SALES_CORRECTIONS_EXTERNAL_WRITE❌Enables unified external/Ticketmaster correction writes for organization admins in an explicitly configured environmentfalse
PLATFORM_SUPER_ADMIN_CORRECTION_IMPERSONATION❌Exact literal true permits the existing audited organization-admin session flow for corrections. Never permits direct Super Admin writes. Requires separate operator approval after verified UI deployment.false
PLATFORM_SUPER_ADMIN_EXTERNAL_CORRECTIONS_WRITE❌Exact literal true, together with SALES_CORRECTIONS_EXTERNAL_WRITE=true, enables direct Ticketmaster corrections by the authenticated platform Super Admin. No administrator selection or impersonation session is required.false

Organization-scoped pre-handout preview ​

For the restricted preview, configure only the numbered pull request API environment, not the production default. The approved manual preview organization is Nordlys Koret, docs-nordlys-koret:

text
PRE_HANDOUT_SALES_ENABLED=true
PRE_HANDOUT_SALES_MODE=allowlist
PRE_HANDOUT_SALES_ORGANIZATION_ALLOWLIST=docs-nordlys-koret

The server reads these settings for each decision. IDs follow the existing organization normalization rules: trim whitespace, use lowercase, and reject malformed or duplicate entries. An empty allowlist never enables all organizations in allowlist mode. Server-loaded order ownership and seller attribution determine eligibility, not request-body organization claims or the current UI selection. With this single entry, cross-organization delegated adoption is not enabled. Existing authorization and Shared Event capacity gates still apply.

Build the preview frontend with VITE_MEMBER_SALES_REPORTING_ENABLED=true. This build setting cannot bypass server checks. Removing an organization from the allowlist or disabling acceptance preserves adopted order visibility, existing sold-ticket handout, and settlement/conservation guards. Ordinary legacy delivery keeps its existing route; it does not adopt the new schema.

Shared database compatibility: this preview uses the existing shared database by explicit user choice, not a separate database. Only the user performs manual mutations in docs-nordlys-koret; agents do not modify those records. Organization gating limits new-schema acceptance, but does not make older production code understand new test-organization records. Do not use older production mutation paths on adopted preview orders. Disabling the preview does not remove or downgrade persisted records.

All-organization pre-handout activation ​

This mode requires deployment of the API that supports PRE_HANDOUT_SALES_MODE. The v1.12.0 API supports only allowlisted acceptance. Do not set a wildcard, enumerate all organizations, or configure the new mode before deploying support. After review, release, and verified production deployment, enable only the production default API environment with:

text
PRE_HANDOUT_SALES_ENABLED=true
PRE_HANDOUT_SALES_MODE=all-organizations

Use an append/merge settings update to preserve every unrelated setting. Do not change a preview environment's allowlist mode or its Nordlys allowlist. In global mode the allowlist is optional and does not restrict membership, but any nonempty list must still be valid and contain no normalized duplicates. Both server-resolved owner and seller IDs must be valid. Invalid modes, wildcard lists, and malformed settings fail closed for new acceptance.

This is a rollout gate, not authorization. Membership, role, order ownership, live delegation permissions, capacity, and settlement checks remain unchanged. Newly created organizations are eligible without another settings update. Verify only these rollout settings with a filtered read; do not create real customer sales to test activation.

Set PRE_HANDOUT_SALES_ENABLED=false to stop new adoption and pre-handout sales. Already adopted orders remain visible and serviceable for sold-ticket handout and settlement. Returning to allowlist mode restores owner-and-seller restriction and requires a valid nonempty allowlist. Neither rollback removes persisted fulfillment data.

Frontend Configuration ​

These variables control the frontend application behavior. Set them in a .env file in the frontend/ directory:

VariableRequiredDescriptionDefault
VITE_API_URL❌Base URL for API calls/api (proxy)
VITE_BETA_FEEDBACK❌Set to 'true' to enable the beta feedback UI (floating button + menu item). Disabled if not set.false
VITE_DELEGATED_MEMBER_CATALOG_ENABLED❌Set to the literal string 'true' to show delegated concerts in a partner organization's own Home and Bestill billetter screens. Any other value, including unset, keeps them hidden.false
VITE_SALES_CORRECTIONS_EXTERNAL_WRITE_ENABLED❌Set to the literal string 'true' to expose the unified external/Ticketmaster correction workflow. API authorization and the server runtime switch still apply.false

Test mode and development ​

E2E_TEST_MODE enables dangerous test-only endpoints (like /api/management/cleanup) for E2E testing. It requires three conditions to be active:

  1. E2E_TEST_MODE=true
  2. NODE_ENV is NOT production
  3. FRONTEND_URL contains localhost

DANGER

Do NOT enable this in production. Test mode endpoints can delete all data.

NODE_ENV controls the build and runtime environment:

  • development — enables debug logging and verbose errors
  • production — optimized build, hides debug info

In production, this must be set to production to prevent test endpoints from running, regardless of E2E_TEST_MODE.

Shared Event configuration ​

SHARED_EVENTS_ENABLED accepts only the lowercase strings true and false. If it is unset, Shared Event creation and mutation are disabled. Invalid values are rejected as configuration errors. Read and maintenance paths use their own safe behavior so existing records and recovery work are not deleted when mutation is disabled.

SHARED_EVENTS_ORGANIZATION_ALLOWLIST is optional. Separate organization IDs with commas. The API trims and lowercases each ID, accepts up to 128 characters, and allows lowercase letters, digits, ., _, :, and -. Empty means no additional organization restriction. Malformed or duplicate normalized entries are configuration errors. Super Admin mutation paths can bypass the allowlist where the handler explicitly permits it, but not the global disabled switch.

Delegated member ordering configuration ​

These two switches control the rollout of delegated normal-event sales for partner organization members (v1.2.0). Both are exposure/availability controls, never authorization: every live delegation, membership, and role check runs exactly as before regardless of these settings. Both default to off, and both are read fresh on every call, so a rollback takes effect without a restart.

  • DELEGATED_MEMBER_CATALOG_ENABLED (server) and VITE_DELEGATED_MEMBER_CATALOG_ENABLED (frontend build-time) decide whether a delegated concert is shown at all in a partner organization's own member catalog. Accepted truthy values on the server are 1, true, yes, on (case-insensitive); the frontend flag accepts only the exact string 'true'. This is exposure only — it does not close the ordering endpoint.
  • DELEGATED_MEMBER_ORDERING_ENABLED (server) is the real availability switch. It gates only the creation of new delegated member self-orders. While it is off, the API returns a stable 503 with error code DELEGATED_MEMBER_ORDERING_UNAVAILABLE for a new order attempt, and the member's ticket selection stays in the cart so they can try again later. Orders already in the system are unaffected: cancel, approve, deliver, report sold, and report returned all keep working, so an emergency stop never strands tickets mid-lifecycle.

Rollout order matters. Enable DELEGATED_MEMBER_ORDERING_ENABLED before DELEGATED_MEMBER_CATALOG_ENABLED / VITE_DELEGATED_MEMBER_CATALOG_ENABLED. Turning the catalog on first would show members a delegated concert whose orders are refused. Enabling the catalog switch does not by itself grant any new capability; it only makes an already-authorized concert visible.

Enabling the switches in an Azure Static Web Apps pull request preview ​

Azure Static Web Apps application settings are scoped per environment, so a fresh preview environment can have these switches configured independently of any other environment, following the same ordering-first-then-catalog sequence documented above. az staticwebapp appsettings set targets one environment with --environment-name, and it merges into that environment's existing settings instead of replacing them, so inherited database settings stay intact. The preview environment has to exist first, so run the commands after the pull request deployment has finished.

bash
# 1. availability first
az staticwebapp appsettings set -n <SWA_NAME> -g <RESOURCE_GROUP> \
  --environment-name <PR_NUMBER> \
  --setting-names DELEGATED_MEMBER_ORDERING_ENABLED=true

# 2. exposure second
az staticwebapp appsettings set -n <SWA_NAME> -g <RESOURCE_GROUP> \
  --environment-name <PR_NUMBER> \
  --setting-names DELEGATED_MEMBER_CATALOG_ENABLED=true

# verify, without printing any other setting value
az staticwebapp appsettings list -n <SWA_NAME> -g <RESOURCE_GROUP> \
  --environment-name <PR_NUMBER> \
  --query "properties.{ordering:DELEGATED_MEMBER_ORDERING_ENABLED,catalog:DELEGATED_MEMBER_CATALOG_ENABLED}"

Never run az staticwebapp appsettings list without the --query filter in a shared terminal or a CI log: it prints every setting value, including database keys. set redacts its own output.

A settings change restarts the environment's managed functions. Allow up to about two minutes and retry the verification query before concluding that something failed. No redeployment is required, because both switches are read from process.env on every call.

VITE_DELEGATED_MEMBER_CATALOG_ENABLED needs no manual step for a pull request preview. As part of the v1.2.0 production release gate, the deployment workflow now builds this flag as true for both pull request builds and pushes to main. A fresh preview or other non-production environment still shows nothing until DELEGATED_MEMBER_CATALOG_ENABLED is also set on that same environment, so the ordering-first-then-catalog sequence documented above still applies to any environment where these switches default to off. Closing the pull request deletes the preview environment and its settings with it.

Existing data compatibility. No migration is required to enable these switches. Delegations created before this rollout do not store the newer approval permission explicitly; an active delegation is treated as carrying it, so existing partner delegations work immediately once the switches are turned on.

External sales correction rollout ​

Production frontend exposure for #504 was approved on 2026-09-17. The deployment workflow builds VITE_SALES_CORRECTIONS_EXTERNAL_WRITE_ENABLED as true for both pull request previews and pushes to main. Local or custom builds still default off unless the value is the literal string true. To roll back exposure, set the frontend build flag to false, then rebuild and redeploy. This does not disable the API or change its runtime switch.

The SWA deployment action cannot persist a build-step variable as a managed API application setting. SALES_CORRECTIONS_EXTERNAL_WRITE remains a separate, per-environment API switch; when it is off, unified correction writes remain unavailable regardless of frontend exposure. This frontend release gate does not change production API runtime settings. API authorization and organization isolation still apply. For a separately authorized numbered preview, an authenticated Azure operator can configure and verify only that preview's API switch:

bash
az staticwebapp appsettings set -n <SWA_NAME> -g <RESOURCE_GROUP> \
  --environment-name <PR_NUMBER> \
  --setting-names SALES_CORRECTIONS_EXTERNAL_WRITE=true

# verify only this non-secret switch; never print the complete settings object
az staticwebapp appsettings list -n <SWA_NAME> -g <RESOURCE_GROUP> \
  --environment-name <PR_NUMBER> \
  --query "properties.{externalWrite:SALES_CORRECTIONS_EXTERNAL_WRITE}"

The commands above are preview-only: do not omit --environment-name or use them to target production. Production API settings are managed separately. Direct platform Super Admin correction writes are disabled by default. The separate PLATFORM_SUPER_ADMIN_CORRECTION_IMPERSONATION setting must be the exact literal true to permit corrections through the existing audited organization-admin session flow. It does not grant direct Super Admin write access. Ticketmaster price and fee inputs remain immutable.

Direct Super Admin Ticketmaster corrections ​

The new PLATFORM_SUPER_ADMIN_EXTERNAL_CORRECTIONS_WRITE setting defaults to false. Only the exact string true enables it, and SALES_CORRECTIONS_EXTERNAL_WRITE must also be true. The old PLATFORM_SUPER_ADMIN_CORRECTION_IMPERSONATION setting is unchanged: it controls the existing session-based authorization flow and never enables direct access by itself.

After independent validation and verified UI deployment, an operator must explicitly approve enabling the new setting in the intended environment. This implementation does not change production settings. Leave allocation-repair and other ticket-write capabilities unchanged.

The authenticated Super Admin uses the same Correct sale dialog as an organization administrator. No administrator picker, borrowed identity, or session-creation call is needed. Audit records identify the actual authenticated Super Admin as the effective actor with an explicit platform role.

GET /api/management/sales/correction-capabilities?organizationId=... is read-only and returns the standard success wrapper with canCorrectExternalSales and reason (available, feature-disabled, or not-authorized). It performs no ticket, correction, or session writes. Invalid authentication, invalid scope, and dependency failures remain errors, not false permission results.

Current credentials, database roles, organization state, and both write gates are checked again for commit, replay, and recovery. Existing session proof, if supplied by a client, must be current and valid; it never selects a borrowed actor on the direct path. Legacy View as User remains read-only. Stale proof is rejected explicitly. Price and fee snapshots, partition isolation, ETags, and idempotent accounting remain enforced. Disabling writes does not remove authorized correction-history reads.

Token and database timeouts ​

REFRESH_TOKEN_TTL_DAYS accepts a positive integer number of days. Unset, non-integer, non-finite, and values below 1 use the 90-day default.

COSMOS_REQUEST_TIMEOUT_MS is converted to a number and passed to the Cosmos SDK only when COSMOS_KEY selects the key-based client, such as the local emulator. If it is unset or does not produce a usable nonzero number, the application leaves the SDK timeout unchanged. Managed Identity clients do not use this application setting.

Magic link emails are sent via Resend. The provider currently offers a free transactional-email allowance for low-volume use. Check Resend pricing for current monthly and daily limits before planning production volume.

VariableDescription
RESEND_API_KEYYour Resend API key
FROM_EMAILSender address, preferably a verified production sender. The code falls back to onboarding@resend.dev
FRONTEND_URLFrontend URL for magic link callback

Development without Resend ​

  • If RESEND_API_KEY is not set, email delivery is skipped. Development logs contain only a masked summary of the recipient, subject, and sender; they do not contain the login link, verification code, or email body.
  • The normal API response remains generic and does not expose a login link.
  • The API returns devLink and devCode only in guarded E2E test mode. This requires E2E_TEST_MODE=true, a localhost FRONTEND_URL, and a non-production NODE_ENV; the mode skips email delivery and must not be used as production behavior.

Switching email providers ​

Email logic is isolated in api/src/shared/email.ts. To switch providers, replace the Resend SDK with SendGrid, Azure Communication Services, etc.

Feedback configuration ​

  • Set VITE_BETA_FEEDBACK=true at frontend build time to show the feedback button and menu item. Any other value, including an unset value, leaves the UI disabled.
  • GITHUB_FEEDBACK_TOKEN is optional. When it is present, the API makes a best-effort attempt to create a GitHub issue after storing feedback in Cosmos DB. Feedback storage remains the primary operation if GitHub issue creation fails.

Database Setup ​

Run the setup script to create all containers with proper TTL settings:

bash
cd api
npx ts-node scripts/setup-database.ts

This creates:

  • rateLimits container with TTL enabled for auto-cleanup
  • authTokens container with TTL for expired token cleanup
  • All other application containers

Sample local.settings.json ​

json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "COSMOS_ENDPOINT": "https://localhost:8081",
    "COSMOS_KEY": "<your-emulator-key>",
    "COSMOS_DATABASE": "choirtickets",
    "JWT_SECRET": "dev-secret-change-in-production",
    "FRONTEND_URL": "http://localhost:5173"
  }
}

Production

In production, set JWT_SECRET to a strong random value and configure RESEND_API_KEY and FROM_EMAIL for email delivery.


Next: Azure Deployment · See also: Local Development

Built with VitePress