Environment Variables
API Configuration
These variables are set in api/local.settings.json for local development or in Azure Function App → Configuration for production.
| Variable | Required | Description | Default |
|---|---|---|---|
COSMOS_ENDPOINT | ✅ | Cosmos DB endpoint URL | https://localhost:8081 (emulator) |
COSMOS_KEY | ✅ | Cosmos DB primary key | Emulator key |
COSMOS_DATABASE | ✅ | Database name | choirtickets |
JWT_SECRET | ✅ | Secret for signing session tokens | — |
FRONTEND_URL | ✅ | Frontend URL for magic link callbacks | http://localhost:5173 |
RESEND_API_KEY | ❌ | Resend API key for sending emails | — |
FROM_EMAIL | ❌ | Sender email address | onboarding@resend.dev |
E2E_TEST_MODE | ❌ | Enable test-mode endpoints (dangerous) | false |
NODE_ENV | ❌ | Environment: development or production | development |
REFRESH_TOKEN_TTL_DAYS | ❌ | Positive integer lifetime for refresh tokens, in days | 90 |
COSMOS_REQUEST_TIMEOUT_MS | ❌ | Numeric Cosmos request timeout in milliseconds for key-based clients | Cosmos SDK default |
SHARED_EVENTS_ENABLED | ❌ | Exact true or false switch for Shared Event creation and mutation | false |
SHARED_EVENTS_ORGANIZATION_ALLOWLIST | ❌ | Comma-separated organization IDs allowed to mutate Shared Events | Empty, no organization restriction |
GITHUB_FEEDBACK_TOKEN | ❌ | Token with issue-write access for optional GitHub issue creation from beta feedback | None |
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 concert | false |
Frontend Configuration
These variables control the frontend application behavior. Set them in a .env file in the frontend/ directory:
| Variable | Required | Description | Default |
|---|---|---|---|
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 |
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:
E2E_TEST_MODE=trueNODE_ENVis NOTproductionFRONTEND_URLcontainslocalhost
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 errorsproduction— 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) andVITE_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 are1,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 stable503with error codeDELEGATED_MEMBER_ORDERING_UNAVAILABLEfor 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.
# 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.
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.
Email (magic links)
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.
| Variable | Description |
|---|---|
RESEND_API_KEY | Your Resend API key |
FROM_EMAIL | Sender address, preferably a verified production sender. The code falls back to onboarding@resend.dev |
FRONTEND_URL | Frontend URL for magic link callback |
Development without Resend
- If
RESEND_API_KEYis 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
devLinkanddevCodeonly in guarded E2E test mode. This requiresE2E_TEST_MODE=true, a localhostFRONTEND_URL, and a non-productionNODE_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=trueat frontend build time to show the feedback button and menu item. Any other value, including an unset value, leaves the UI disabled. GITHUB_FEEDBACK_TOKENis 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:
cd api
npx ts-node scripts/setup-database.tsThis creates:
rateLimitscontainer with TTL enabled for auto-cleanupauthTokenscontainer with TTL for expired token cleanup- All other application containers
Sample local.settings.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