Welcome to the Jose Madrid Salsa developer docs — explore features, APIs, and deployment guides.
Jose Madrid SalsaJMS Docs

Environment Variables

Complete reference of all environment variables used by Jose Madrid Salsa

Environment Variables

Jose Madrid Salsa uses environment variables for all secrets, API keys, and runtime configuration. Variables prefixed with NEXT_PUBLIC_ are exposed to the browser; all others are server-only.

Never commit .env files to version control. Use .env.example as a reference template and configure real values in Vercel or your local .env.local file.

Database

VariableRequiredDescription
DATABASE_URLYesPrimary PostgreSQL connection string (pooled). Falls back to PRISMA_DATABASE_URL then POSTGRES_URL if unset.
DATABASE_URL_UNPOOLEDRecommendedDirect (non-pooled) connection for migrations. Used by Prisma directUrl.
PRISMA_DATABASE_URLNoAlias fallback for DATABASE_URL.
POSTGRES_URLNoSecond fallback for DATABASE_URL. Vercel Postgres integration sets this automatically.
.env.local
DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=..."
DATABASE_URL_UNPOOLED="postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/josemadridsalsa?sslmode=require"

Authentication

VariableRequiredDescription
NEXTAUTH_URLProductionCanonical URL of the site. Auto-detected on Vercel in most cases.
NEXTAUTH_SECRETYesSecret used to sign JWT tokens. Generate with openssl rand -base64 32.

OAuth Providers

VariableRequiredDescription
GOOGLE_CLIENT_IDFor Google loginGoogle OAuth 2.0 client ID
GOOGLE_CLIENT_SECRETFor Google loginGoogle OAuth 2.0 client secret
GITHUB_CLIENT_IDFor GitHub loginGitHub OAuth App client ID
GITHUB_CLIENT_SECRETFor GitHub loginGitHub OAuth App client secret
FACEBOOK_CLIENT_IDFor Facebook loginFacebook App ID
FACEBOOK_CLIENT_SECRETFor Facebook loginFacebook App secret
APPLE_CLIENT_IDFor Apple loginApple Services ID
APPLE_CLIENT_SECRETFor Apple loginApple client secret (JWT)

Encryption

VariableRequiredDescription
MASTER_KEYYes64-character hex string (32 bytes) for AES-256-GCM encryption of service keys. Generate with node -e "console.log(require('crypto').randomBytes(32).toString('hex'))".
ENCRYPTION_KEYYesBase64-encoded key for SMTP password encryption. Generate with node -e "console.log(require('crypto').randomBytes(64).toString('base64'))".

Stripe Payments

VariableRequiredDescription
STRIPE_SECRET_KEYYesStripe secret key (sk_test_... or sk_live_...)
STRIPE_PUBLISHABLE_KEYYesStripe publishable key for client-side (pk_test_...)
STRIPE_WEBHOOK_SECRETYesWebhook signing secret (whsec_...)

PayPal

VariableRequiredDescription
PAYPAL_CLIENT_IDFor PayPalPayPal REST app client id
PAYPAL_CLIENT_SECRETFor PayPalPayPal REST app secret
PAYPAL_SANDBOXNotrue uses the sandbox API host
PAYPAL_WEBHOOK_IDFor PayPalWebhook id used to verify inbound events. Missing means every webhook is rejected — the route logs CRITICAL rather than trusting the payload.
PAYPAL_RETURN_URLNoWhere PayPal returns the buyer after approval
PAYPAL_CANCEL_URLNoWhere PayPal returns the buyer on cancel

Square

VariableRequiredDescription
SQUARE_ACCESS_TOKENFor SquareSquare access token
SQUARE_LOCATION_IDFor SquareSquare location the charge belongs to
SQUARE_SANDBOXNotrue uses the Square sandbox
SQUARE_WEBHOOK_SIGNATURE_KEYFor SquareVerifies inbound webhooks
SQUARE_WEBHOOK_URLFor SquareThe notification URL the signature is computed against
SQUARE_TERMINAL_DEVICE_IDFor POSThe Square Terminal to send an in-person charge to

Webhook Secrets

Every inbound webhook is verified. A missing secret is a refusal, not a bypass — see Webhooks.

VariableRequiredDescription
STRIPE_WEBHOOK_SECRETYesStripe signature secret (also listed above)
PAYPAL_WEBHOOK_IDFor PayPalVerified via PayPal's own API
SQUARE_WEBHOOK_SIGNATURE_KEYFor SquareHMAC signature key
EASYPOST_WEBHOOK_SECRETFor shippingVerifies EasyPost tracking events
RESEND_WEBHOOK_SECRETFor email eventsVerifies Resend delivery/bounce events (Svix headers)

Scheduled Jobs

VariableRequiredDescription
CRON_SECRETYes in productionBearer token Vercel Cron sends to /api/cron/*. Outside production a missing value is allowed; in production a missing value makes every cron route refuse, rather than leaving them world-callable. See Scheduled Jobs.
UNSUBSCRIBE_SECRETFor marketing emailSigns one-click unsubscribe links

Email (Resend)

VariableRequiredDescription
RESEND_API_KEYYesResend API key (re_...)
FROM_EMAILNoDefault sender address. Defaults to orders@josemadridsalsa.com.
BUSINESS_POSTAL_ADDRESSNoPhysical mailing address printed in marketing-email footers, as CAN-SPAM requires. Defaults to the Zanesville address.
FUNDRAISING_REPLY_TONoReply-to address on fundraiser re-signup invitations. Defaults to the owner's address.
EMAIL_IMAGE_BASE_URLNoAbsolute base URL for email header and footer images. Email clients cannot resolve relative paths, so every image needs a full URL. Defaults to https://www.josemadrid.net/email-templates; point it at the Vercel Blob store to serve the assets from there instead. No trailing slash.

Google Services

VariableRequiredDescription
NEXT_PUBLIC_GOOGLE_MAPS_API_KEYYesGoogle Maps JavaScript API key (client-side)
GOOGLE_PLACES_API_KEYFor reviewsGoogle Places API key (server-side)
NEXT_PUBLIC_GOOGLE_PLACE_IDNoGoogle Place ID for map accuracy
GOOGLE_PLACE_IDNoServer-side Google Place ID
GOOGLE_PLACE_NAMENoBusiness name fallback. Defaults to "Jose Madrid Salsa".
NEXT_PUBLIC_GOOGLE_STREETVIEW_PANONoSpecific Street View panorama ID
GOOGLE_SERVICE_ACCOUNT_EMAILFor calendarService account email
GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEYFor calendarService account private key (include \n for newlines)
GOOGLE_CALENDAR_IDFor scheduleCalendar ID for "Where is Jose"

File Uploads

VariableRequiredDescription
UPLOADTHING_SECRETFor uploadsUploadThing secret key
UPLOADTHING_APP_IDFor uploadsUploadThing application ID
BLOB_READ_WRITE_TOKENFor image hostingRead/write token for the josemadridsalsa-blob Vercel Blob store. Powers the Developer Console file explorer (/admin/developer/files), the product image uploader, and the images:sync / images:migrate scripts. Get it from Vercel → Storage → josemadridsalsa-blob. Without it, every blob upload path is disabled and returns 503.

QuickBooks Online

The Client ID and Secret are optional here — the preferred place to enter them is Admin → Settings → Integrations, which stores the secret encrypted in the database. These variables exist as a fallback and are read only when no admin-entered credentials are present.

QUICKBOOKS_ENVIRONMENT has no admin-panel equivalent and must be set to reach real company data: it defaults to sandbox, and the Connect button on the integrations page connects to whichever environment it names. Left unset in production, the flow completes successfully against an Intuit sandbox company and syncs nothing real.

VariableRequiredDescription
QUICKBOOKS_ENVIRONMENTTo reach live bookssandbox (default) or production. Selects both the Intuit API host and which stored credential set is used.
QUICKBOOKS_CLIENT_IDNoIntuit app Client ID. Fallback only; admin-entered credentials win.
QUICKBOOKS_CLIENT_SECRETNoIntuit app Client Secret. Fallback only; admin-entered credentials win.

The redirect URI is derived from NEXTAUTH_URL as {NEXTAUTH_URL}/api/integrations/quickbooks/callback and must be registered verbatim in the Intuit app. Sandbox and production keys are different pairs, and credentials are stored per environment — production keys saved while the app is in sandbox mode are filed under sandbox and will not work.

Analytics

VariableRequiredDescription
GOOGLE_ANALYTICS_IDNoGoogle Analytics measurement ID (G-XXXXXXXXXX)
NEXT_PUBLIC_AMPLITUDE_API_KEYNoAmplitude analytics API key

Shipping

VariableRequiredDescription
SHIPPING_PROVIDERNoShipping rate provider: easypost or shippo
SHIPPING_API_KEYFor ratesShipping provider API key
SHIPPING_TEST_MODENoSet to "false" for production
SHIPPING_ORIGIN_ADDRESSFor rates and labelsWarehouse street address
SHIPPING_ORIGIN_CITYFor rates and labelsWarehouse city
SHIPPING_ORIGIN_STATEFor rates and labelsWarehouse state (2-letter code)
SHIPPING_ORIGIN_ZIPFor rates and labelsWarehouse ZIP code
SHIPPING_ORIGIN_COUNTRYNoDefaults to US
SHIP_FROM_NAMENoSender name on labels. Defaults to Jose Madrid Salsa
SHIP_FROM_STREET, SHIP_FROM_CITY, SHIP_FROM_STATE, SHIP_FROM_ZIP, SHIP_FROM_COUNTRYNoLegacy aliases for the SHIPPING_ORIGIN_* variables above, still read as a fallback. Prefer SHIPPING_ORIGIN_*

Set the warehouse address

Rate quoting and label purchase both read the warehouse address through lib/shipping/origin.ts, which takes Settings → Shipping first and these variables second. Until one of the two is complete, no real carrier rate can be fetched — checkout falls back to flat-rate estimates and logs why.

They used to be configured separately: quoting read SHIPPING_ORIGIN_* and defaulted to a placeholder address in San Francisco when unset, while label purchase read SHIP_FROM_*. A half-configured deployment therefore quoted customers from one address and shipped from another, with nothing in the response to indicate it.

Monitoring

VariableRequiredDescription
SENTRY_ORGFor SentrySentry organization slug
SENTRY_PROJECTFor SentrySentry project slug
SENTRY_AUTH_TOKENFor SentrySentry auth token for source map uploads

AI

VariableRequiredDescription
ANTHROPIC_API_KEYFor AI chatAnthropic API key. Powers the Picante chat assistant and the form-capture extractor.
ANTHROPIC_MODELNoModel used by the Picante chat assistant. Defaults to claude-opus-5; claude-haiku-4-5 is the cheaper option.
AI_CHAT_PROVIDERNoBackend for /api/ai-chat: anthropic (default), openai, or smileyface.
NEXT_PUBLIC_AI_CHAT_PROVIDERNoBrowser-visible copy of the same choice, used only for the "Powered by" label in the chat widget.
OPENAI_API_KEYFor AI_CHAT_PROVIDER=openaiOpenAI API key. Only read when the provider is set to openai.
OPENAI_MODELNoModel used when the provider is openai. Defaults to gpt-4o-mini.

Internal

VariableRequiredDescription
SERVICE_API_KEYNoShared secret for internal service-to-service API calls
NEXT_PUBLIC_BASE_URLNoPublic-facing base URL. Defaults to https://josemadrid.net.

Platform Agent (apps/agent)

Set these in apps/agent/.env.local, not the storefront's.

VariableRequiredDescription
AI_GATEWAY_API_KEYOne of theseVercel AI Gateway key used to reach the agent's model
VERCEL_OIDC_TOKENOne of thesePulled automatically by eve link; the alternative to an AI Gateway key
STOREFRONT_API_URLNoBase URL the agent's read-only tools call. Defaults to https://www.josemadrid.net. Point it at http://localhost:3000 to run against a local storefront.

How is this guide?

Edit on GitHub

Last updated on

On this page