# Optional settings

> Every variable that turns on an extra Crewtavo feature, such as texting, maps, accounting, AI, Google Drive import or error tracking, and where to add it on Railway or with Docker.

Everything a Crewtavo needs to run is generated when it's set up. These variables turn on extra features.

**Where to add them:** on Railway, open the **crewtavo** service, then **Variables**, and deploy the change when Railway asks. With Docker, add them to `.env.instance` and run `npm run instance` again. (`.env.example` in the source code lists most of them.)

## Features

| Variable | Turns on |
| --- | --- |
| `APP_URL` | Your own domain. **Settings → Web address** sets it for you on Railway. See [Use your own domain](https://crewtavo.com/docs/hosting/own-domain/). |
| `RESEND_API_KEY`, `EMAIL_FROM` | Optional default sender through Resend, used until a company connects its own email. Most businesses connect Resend under **Settings → Email** instead. See [Send email from Crewtavo](https://crewtavo.com/docs/connections/email/). |
| `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_FROM_NUMBER` | Text messages through Twilio, from one number for every business on the server. All three are needed. See [Text clients with Twilio](https://crewtavo.com/docs/connections/texting-with-twilio/), and for two-way texting, [Texting replies](#texting-replies). |
| `GOOGLE_MAPS_BROWSER_KEY` | Google satellite as a map choice on **Measure** for every business on the server. Measure works without it on free imagery, and each business can add its own key in **Settings → Maps**. Turn on the Map Tiles API and restrict the key to it and to your domain. See [Map imagery for measuring](https://crewtavo.com/docs/connections/map-imagery/). |
| `GOOGLE_MAPS_SERVER_KEY` | Roof pitch suggestions (Google's Solar API). Restrict the key to the Solar API. |
| `ANTHROPIC_API_KEY` (and optionally `AI_MODEL`) | AI writing help and **Ask** in the knowledge base, for every business on the server. Not needed: each business can add its own key under **Apps → Claude** instead, which wins when both are there. See [Ask a question](https://crewtavo.com/docs/team/knowledge-base/#ask-a-question). |
| `QUICKBOOKS_CLIENT_ID`, `QUICKBOOKS_CLIENT_SECRET`, `QUICKBOOKS_ENVIRONMENT` | An Intuit app for QuickBooks Online, set up on the server. Not needed: each business pastes its own app's keys under **Apps → QuickBooks Online** instead, which wins when both are there. See [Sync with QuickBooks Online](https://crewtavo.com/docs/connections/quickbooks/). |
| `ZOHO_CLIENT_ID`, `ZOHO_CLIENT_SECRET`, `ZOHO_ACCOUNTS_URL` | A Zoho app for Zoho Books, set up on the server. Not needed: each business pastes its own app's keys under **Apps → Zoho Books** instead, which wins when both are there. See [Sync with Zoho Books](https://crewtavo.com/docs/connections/zoho-books/). |
| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | Google sign-in for Google Calendar: pick calendars from a Google account, and put visits on a calendar Crewtavo makes within minutes. See [Google Calendar](#google-calendar). Not needed for most businesses: without these, Google Calendar works by secret address both ways, with nothing to set up. See [Google Calendar and other calendars](https://crewtavo.com/docs/connections/google-calendar/). |
| `GOOGLE_PICKER_API_KEY` | **Import from Google Drive** in Drive, together with `GOOGLE_CLIENT_ID` (the secret isn't needed for it). A browser key restricted to the Google Picker API, your domain and `https://docs.google.com/*`. See [Import from Google Drive](https://crewtavo.com/docs/connections/drive/#set-it-up). |
| `TURNSTILE_SITE_KEY`, `TURNSTILE_SECRET_KEY` | Bot protection on public booking and referral forms (Cloudflare Turnstile). |
| `CREWTAVO_SENTRY_DSN` | Error tracking. See [Error tracking](#error-tracking). |

## Advanced

| Variable | What it's for |
| --- | --- |
| `ENCRYPTION_KEY` | A separate key for encrypting app tokens and signing client links: 32 or more random bytes in base64, or any 32 or more characters. Without it, one is derived from the instance's secret. **Keep it the same once set**: changing it disconnects every app and breaks links already sent to clients. |
| `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` | Not needed for most businesses: [connect Stripe on the Apps page](https://crewtavo.com/docs/connections/stripe/) instead. These share one Stripe account with every business on the server that hasn't connected its own, and need a Stripe webhook pointed at `https://your-domain/api/webhooks/stripe`; the [deployment guide](https://github.com/jviars/crewtavo/blob/main/docs/deploy.md#optional-settings) lists its API version and events. |
| `CREWTAVO_SCHEDULER` | Set to `off` on extra copies of the `crewtavo` service, so background tasks run in one place. |
| `CREWTAVO_VISITOR_IP` | Where visitors' addresses come from, when something other than Railway or one web server is in front. See [Behind other proxies](#behind-other-proxies). |
| `CREWTAVO_BIND` | Docker only: the address port 8080 answers on. Defaults to `127.0.0.1`, the server itself. |
| `CREWTAVO_SENTRY_WEB_DSN` | Send the web app's errors to a separate Sentry project. Defaults to `CREWTAVO_SENTRY_DSN`. |
| `CREWTAVO_SENTRY_ENVIRONMENT` | The environment shown on each error report, such as `staging`. Defaults to `production`. |

## Texting replies

For two-way texting, set your Twilio number's **Messaging Configuration → A message comes in** webhook to `https://your-domain/api/webhooks/twilio` (HTTP POST). `APP_URL` must match that address exactly, because Twilio signs the full address. After a switch to your own domain, the old address keeps working too.

A reply then appears in **Messages**, in the conversation of the client whose **Phone** matches the number it came from, and owners, admins and office staff get a notification. A text from a number that isn't on any client is dropped. The full setup, with A2P 10DLC registration and troubleshooting, is in [Text clients with Twilio](https://crewtavo.com/docs/connections/texting-with-twilio/).

## Google Calendar

Optional: Google Calendar already works without this, by secret address (see [Google Calendar and other calendars](https://crewtavo.com/docs/connections/google-calendar/)). Google sign-in adds picking calendars from the account and visits that reach Google within minutes.

1. In Google Cloud, create a project and turn on the **Google Calendar API**.
2. In **Google Auth Platform**, fill in **Branding**. Under **Data access**, add the scopes `calendar.calendarlist.readonly`, `calendar.events.readonly` and `calendar.app.created`.
3. Under **Clients**, create a **Web application** client whose redirect address is the one shown on the Google Calendar page under **Apps**: `https://your-domain/api/apps/google_calendar/callback`. Set its ID and secret as `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`.
4. Under **Audience**, choose **Publish app**. While the app is in **Testing**, Google ends every sign-in after 7 days and Google Calendar disconnects a week after it's connected. On Google Workspace you can choose **Internal** instead, for people in your organization only.

Until Google verifies the app, people see a "Google hasn't verified this app" screen when they connect. They choose **Advanced**, then **Go to** your app, to continue. Up to 100 people can connect to an unverified app.

## Error tracking

Set `CREWTAVO_SENTRY_DSN` to a project's DSN from [Sentry](https://sentry.io) (**Project Settings → Client Keys**). A self-hosted [GlitchTip](https://glitchtip.com), or any other Sentry-compatible service, works the same way.

Reports are errors only: no performance tracing, session tracking or replays. Personal data is removed before anything leaves the server or the browser:

- request bodies, cookies, query strings and headers (except the browser's User-Agent) are never sent;
- no user is attached: no email, IP address or account ID;
- email addresses, phone numbers, sign-in tokens and client links are masked.

Nothing is collected until the variable is set. You can also turn on **Prevent Storing of IP Addresses** in the Sentry project's security settings.

## Behind other proxies

Crewtavo limits how often each visitor can use public pages, by the visitor's address, so it only trusts the address its own proxy sets. On Railway that's automatic, and with Docker behind one web server see [Visitors' addresses](https://crewtavo.com/docs/hosting/docker/#visitors-addresses). With something else in front, set `CREWTAVO_VISITOR_IP`:

| Value | When |
| --- | --- |
| `x-forwarded-for:2` | Two proxies add to `X-Forwarded-For`, such as Cloudflare's proxy in front of Caddy. List Cloudflare's addresses in Caddy's `trusted_proxies`. The number counts the proxies. |
| `cf-connecting-ip`, `x-real-ip` (or another header) | The proxy in front sets this header to the visitor's address, replacing any the visitor sent. |
| `connection` | Nothing is in front: use the address each connection comes from. |

Once set, it's believed on every connection, so nothing may reach Crewtavo without going through those proxies. Keep port 8080 on the server, and let the web server take connections only from the proxy in front of it. For Cloudflare, allow only [Cloudflare's addresses](https://www.cloudflare.com/ips/) in the server's firewall. Caddy with `x-forwarded-for:2` doesn't need the firewall rule, since it replaces the `X-Forwarded-For` of anyone outside `trusted_proxies`.

Source: https://crewtavo.com/docs/hosting/settings/ (last updated 2026-11-02)
