Environment Variables
Owlat uses environment variables to configure payments, email sending, authentication, and feature flags. Backend variables are set via the Convex...
Owlat uses environment variables to configure payments, email sending, authentication, and feature flags. Backend variables are set via the Convex dashboard; frontend variables live in .env files.
Where to Set Variables
Convex backend (all process.env references in apps/api/):
npx convex env set VAR_NAME value
Or set them in the Convex dashboard under your deployment's Settings > Environment Variables.
Nuxt frontend (apps/web/.env):
NUXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
NUXT_PUBLIC_CONVEX_SITE_URL=https://your-deployment.convex.site
NUXT_PUBLIC_SITE_URL=http://localhost:3000
Payments (Stripe)
Prerequisites
- A Stripe account
- Four subscription products with monthly prices (one per plan tier)
- Optionally, a metered price and billing meter for per-email charges
Required Variables
Set these in the Convex dashboard:
| Variable | Description |
|---|---|
BILLING_ENABLED | Set to true or 1 to enable the billing system. Disabled by default. |
STRIPE_SECRET_KEY | Your Stripe secret API key (starts with sk_). Required when billing is enabled. |
STRIPE_WEBHOOK_SECRET | Stripe webhook signing secret (starts with whsec_). Required for processing Stripe webhook events. |
STRIPE_PRICE_CONTACT_TIERED | Stripe Price ID for the graduated tiered contact price |
SITE_URL | Public site URL used for Stripe checkout redirect URLs. Defaults to http://localhost:3000. |
Optional (Metered Email Billing)
| Variable | Description |
|---|---|
STRIPE_PRICE_EMAIL_METERED | Stripe Price ID for metered email usage (per-email charges) |
STRIPE_METER_ID | Stripe Billing Meter ID for reporting email usage events |
Setup Steps
- Create a Stripe account at stripe.com and note your secret key from the API Keys page.
- Create a single product in Stripe with graduated tiered pricing for contacts. Copy the Price ID (starts with
price_). - (Optional) Create a metered price for per-email billing:
- Create a product for email usage
- Add a metered price (e.g., €0.25 per 1,000 emails)
- Create a Billing Meter in Stripe and copy the Meter ID
- Set the environment variables in the Convex dashboard:
npx convex env set BILLING_ENABLED true npx convex env set STRIPE_SECRET_KEY sk_live_... npx convex env set STRIPE_WEBHOOK_SECRET whsec_... npx convex env set STRIPE_PRICE_CONTACT_TIERED price_...
Contact Tiers (Graduated Pricing)
| Contacts | Monthly Price |
|---|---|
| 0 – 10,000 | €8 |
| 10,001 – 40,000 | €20 |
| 40,001 – 500,000 | €100 |
| 500,001+ | €50 + €0.0001/contact |
Email sending is billed at €0.25 per 1,000 emails when metered billing is configured.
Email Sending
Provider Selection
Owlat supports two email providers: AWS SES (default) and Resend. Set the EMAIL_PROVIDER variable in the Convex dashboard:
npx convex env set EMAIL_PROVIDER ses # default
npx convex env set EMAIL_PROVIDER resend
AWS SES
| Variable | Description |
|---|---|
AWS_SES_REGION | AWS region for SES (e.g., eu-west-1, us-east-1). Required. |
AWS_SES_ACCESS_KEY_ID | IAM access key ID with SES permissions. Required. |
AWS_SES_SECRET_ACCESS_KEY | IAM secret access key. Required. |
Setup Steps
- Create an IAM user in AWS with programmatic access and the
AmazonSESFullAccesspolicy (or a scoped policy allowingses:SendEmailandses:SendRawEmail). - Verify your sending domain in the SES console. SES requires domain verification before you can send emails.
- Request production access if your SES account is in sandbox mode. Sandbox mode limits sending to verified addresses only.
- Set the environment variables:
npx convex env set EMAIL_PROVIDER ses npx convex env set AWS_SES_REGION eu-west-1 npx convex env set AWS_SES_ACCESS_KEY_ID AKIA... npx convex env set AWS_SES_SECRET_ACCESS_KEY ...
Resend
| Variable | Description |
|---|---|
RESEND_API_KEY | Resend API key. Required when using Resend. Also used for organization invitation emails. |
RESEND_WEBHOOK_SECRET | Webhook signing secret (format: whsec_<base64>). Required for delivery event tracking. |
Setup Steps
- Create a Resend account at resend.com and add your sending domain.
- Generate an API key from the Resend dashboard.
- (Optional) Set up webhooks for delivery tracking:
- In Resend, create a webhook pointing to your Convex site URL's webhook endpoint
- Copy the webhook signing secret
- Set the environment variables:
npx convex env set EMAIL_PROVIDER resend npx convex env set RESEND_API_KEY re_... npx convex env set RESEND_WEBHOOK_SECRET whsec_...
Common Email Configuration
These variables apply regardless of which provider you use:
| Variable | Description | Default |
|---|---|---|
DEFAULT_FROM_EMAIL | Default sender email address for transactional emails | noreply@example.com |
DEFAULT_FROM_NAME | Default sender display name | Owlat |
DEFAULT_FROM_DOMAIN | Domain used for system emails (e.g., invitation emails). Prepended with noreply@. | mail.owlat.app |
CONVEX_SITE_URL | Your Convex site URL. Used for tracking pixels and unsubscribe links in campaign emails. | — |
UNSUBSCRIBE_SECRET | Secret key for generating HMAC-signed unsubscribe tokens. Required. | — |
Waitlist
The waitlist gates new signups behind admin approval. When enabled, new users land on a waiting screen until an admin approves them.
Variables
Convex dashboard:
| Variable | Description |
|---|---|
WAITLIST_ENABLED | Set to true to enable the waitlist. Disabled by default. |
WAITLIST_AUTO_APPROVE_EMAILS | Comma-separated list of email addresses that skip the waitlist and are approved automatically. |
Nuxt frontend (apps/web/.env):
| Variable | Description |
|---|---|
NUXT_PUBLIC_WAITLIST_ENABLED | Set to true to show waitlist UI in the frontend. Must match the backend setting. |
Setup Steps
- Enable the waitlist on both sides:
# Convex npx convex env set WAITLIST_ENABLED true # apps/web/.env NUXT_PUBLIC_WAITLIST_ENABLED=true - Optionally, auto-approve specific emails (e.g., your team):
npx convex env set WAITLIST_AUTO_APPROVE_EMAILS "admin@example.com,dev@example.com"
Analytics (PostHog)
Owlat integrates with PostHog for product analytics and error tracking. The integration is optional — everything works without it.
Variables
Convex dashboard:
| Variable | Description |
|---|---|
POSTHOG_API_KEY | PostHog project API key (starts with phc_). Required to enable server-side tracking. |
POSTHOG_HOST | PostHog instance URL. Defaults to https://eu.i.posthog.com (EU cloud). |
Nuxt frontend (apps/web/.env):
| Variable | Description |
|---|---|
NUXT_PUBLIC_POSTHOG_API_KEY | PostHog project API key. Required to enable client-side tracking. |
NUXT_PUBLIC_POSTHOG_HOST | PostHog instance URL. Defaults to https://eu.i.posthog.com. |
Setup Steps
- Create a PostHog project at posthog.com and copy the project API key.
- Add the
organizationgroup type in PostHog: go to Settings → Groups → add a group type calledorganization. This enables organization-level analytics. - Set the environment variables:
# Convex (server-side events) npx convex env set POSTHOG_API_KEY phc_... npx convex env set POSTHOG_HOST https://eu.i.posthog.com # apps/web/.env (client-side events) NUXT_PUBLIC_POSTHOG_API_KEY=phc_... NUXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
Tracked Events
Client-side (automatic):
$pageview— SPA page navigations$pageleave— page leave events$exception— Vue errors and unhandled promise rejections- User identification and organization group association
Server-side (fire-and-forget from mutations):
campaign_created,campaign_sentcontact_createdautomation_created,automation_activated,automation_pausedmailing_list_created
All server events include organizationId as a property and PostHog group.
Translations (AI)
Owlat supports AI-powered email template translations. OpenRouter is the primary provider; OpenAI is used as a fallback if OpenRouter is not configured.
| Variable | Description |
|---|---|
OPENROUTER_API_KEY | OpenRouter API key for AI translations (primary). |
OPENAI_API_KEY | OpenAI API key for AI translations (fallback if OpenRouter is not set). |
Set in the Convex dashboard:
npx convex env set OPENROUTER_API_KEY sk-or-...
# or, as fallback:
npx convex env set OPENAI_API_KEY sk-...
Authentication
| Variable | Description | Default |
|---|---|---|
BETTER_AUTH_SECRET | Secret key for BetterAuth session signing. Required. | — |
SITE_URL | Public site URL for auth redirects and callbacks. | http://localhost:3000 |
Set BETTER_AUTH_SECRET in the Convex dashboard. Use a long, random string:
npx convex env set BETTER_AUTH_SECRET $(openssl rand -base64 32)
Nuxt Frontend
These go in apps/web/.env:
| Variable | Description |
|---|---|
NUXT_PUBLIC_CONVEX_URL | Your Convex deployment URL (e.g., https://your-deployment.convex.cloud) |
NUXT_PUBLIC_CONVEX_SITE_URL | Your Convex site URL (e.g., https://your-deployment.convex.site). Used for auth proxy. |
NUXT_PUBLIC_SITE_URL | Public site URL. Must match SITE_URL set in Convex. |
NUXT_PUBLIC_WAITLIST_ENABLED | Set to true to show waitlist UI (see Waitlist) |
Complete Reference
All environment variables in one table:
| Variable | Where | Required | Default | Purpose |
|---|---|---|---|---|
BILLING_ENABLED | Convex | No | Disabled | Enable the billing system |
STRIPE_SECRET_KEY | Convex | If billing | — | Stripe API secret key |
STRIPE_WEBHOOK_SECRET | Convex | If billing | — | Stripe webhook signing secret |
STRIPE_PRICE_CONTACT_TIERED | Convex | If billing | — | Price ID for graduated tiered contact pricing |
STRIPE_PRICE_EMAIL_METERED | Convex | No | — | Price ID for metered email usage |
STRIPE_METER_ID | Convex | No | — | Stripe Billing Meter ID |
SITE_URL | Convex | Yes | http://localhost:3000 | Public site URL for redirects |
EMAIL_PROVIDER | Convex | No | ses | Email provider (ses or resend) |
AWS_SES_REGION | Convex | If SES | — | AWS region for SES |
AWS_SES_ACCESS_KEY_ID | Convex | If SES | — | AWS IAM access key |
AWS_SES_SECRET_ACCESS_KEY | Convex | If SES | — | AWS IAM secret key |
RESEND_API_KEY | Convex | If Resend | — | Resend API key |
RESEND_WEBHOOK_SECRET | Convex | No | — | Resend webhook signing secret |
DEFAULT_FROM_EMAIL | Convex | No | noreply@example.com | Default sender email |
DEFAULT_FROM_NAME | Convex | No | Owlat | Default sender name |
DEFAULT_FROM_DOMAIN | Convex | No | mail.owlat.app | Domain for system emails |
CONVEX_SITE_URL | Convex | Yes | — | Convex site URL for tracking/unsubscribe |
UNSUBSCRIBE_SECRET | Convex | Yes | — | HMAC secret for unsubscribe tokens |
BETTER_AUTH_SECRET | Convex | Yes | — | Session signing secret |
WAITLIST_ENABLED | Convex | No | Disabled | Enable waitlist gating |
WAITLIST_AUTO_APPROVE_EMAILS | Convex | No | — | Emails to auto-approve |
NUXT_PUBLIC_CONVEX_URL | Nuxt .env | Yes | — | Convex deployment URL |
NUXT_PUBLIC_CONVEX_SITE_URL | Nuxt .env | Yes | — | Convex site URL for auth |
NUXT_PUBLIC_SITE_URL | Nuxt .env | Yes | — | Public site URL |
NUXT_PUBLIC_WAITLIST_ENABLED | Nuxt .env | No | Disabled | Show waitlist UI |
OPENROUTER_API_KEY | Convex | No | — | AI translations (primary provider) |
OPENAI_API_KEY | Convex | No | — | AI translations (fallback provider) |
POSTHOG_API_KEY | Convex | No | — | PostHog project API key (server-side) |
POSTHOG_HOST | Convex | No | https://eu.i.posthog.com | PostHog instance URL (server-side) |
NUXT_PUBLIC_POSTHOG_API_KEY | Nuxt .env | No | — | PostHog project API key (client-side) |
NUXT_PUBLIC_POSTHOG_HOST | Nuxt .env | No | https://eu.i.posthog.com | PostHog instance URL (client-side) |