IAMScouting docs

IAMScouting — environment setup

Reference for production env vars and what happens when each is missing or

mis-configured. Maintained alongside /lib/logger.ts so the fail-modes here

match what actually ends up in app_error_log / the fallback file.

Required env vars

The application boots without every var below, but routes that depend on a

missing var return a 503 service_unavailable and log to app_error_log

(falling back to /var/log/iams-audit-fail.log if the DB is unavailable).

| Variable | Used by | Failure mode |

| --- | --- | --- |

| DATABASE_URL / SUPABASE_* | All DB access | App refuses to boot |

| STRIPE_SECRET_KEY | /api/network/leads/[id]/boost, billing portal | 503 + server log |

| ANTHROPIC_API_KEY | /api/network/ai-assistant | 503 + server log |

| RESEND_API_KEY | Transactional email | Mails are dropped silently |

| ADMIN_ALLOWLIST | /api/admin/* + admin pages | Defaults to start@iamscouting.com |

stripe-keys

Lead Boost (the only paid one-off on /network) creates a Stripe Checkout

session in /app/api/network/leads/[id]/boost/route.ts. The route uses

requireStripe() from /lib/stripe.ts, which throws when

STRIPE_SECRET_KEY is blank.

Operational note (2026-05-21): the VPS env file was observed with a

blank STRIPE_SECRET_KEY=. The route now returns a 503 with a generic

"Boost service temporarily unavailable" instead of leaking the raw error.

To restore the feature:

  1. Pull the live secret from the Stripe dashboard (Test or Live keys —

matching whichever STRIPE_MODE is set on the box).

  1. Write it into /opt/iamscouting/.env as STRIPE_SECRET_KEY=sk_live_….
  2. Restart the container: docker compose up -d --force-recreate.
  3. Verify with `curl -sX POST .../api/network/leads/<id>/boost -d

'{"duration":"14d"}' — expect a checkout_url`, not a 503.

The STRIPE_WEBHOOK_SECRET is a separate env var required for the

/api/stripe/webhook endpoint; verify both are set together.

Annual Stripe price IDs (v0.8)

/api/network/subscribe resolves the Stripe price_id for each

(tier_code, interval) pair in this order:

  1. Row in stripe_prices with interval='year' and active=true
  2. annual_price_id column on the matching monthly row (added in

migration 0065_stripe_annual_coupons.sql)

  1. Env var STRIPE_PRICE_<TIER>_ANNUAL — one of:

- STRIPE_PRICE_STARTER_ANNUAL

- STRIPE_PRICE_PRO_ANNUAL

- STRIPE_PRICE_ENTERPRISE_ANNUAL

  1. Falls back to the monthly price so the upgrade flow never hard-fails

(UI shows annual price label but Stripe charges monthly until the

annual SKU is wired). Logged via `event_log.price_source =

'monthly_fallback'`.

Place real price_xxx IDs from the Stripe dashboard into the env file

on the VPS and restart the container. No code redeploy required.

Coupon codes (v0.8)

The coupon_codes table (migration 0065) maps a user-facing promo

string (e.g. LAUNCH50) to the corresponding Stripe promotion_code_id

(promo_xxx). The /account/club-tier page validates codes against

/api/coupon/validate before forwarding to /api/network/subscribe,

which attaches the promo to the Checkout session. The webhook

increments used_count on checkout.session.completed. To seed:

INSERT INTO coupon_codes (code, stripe_promotion_code_id, percent_off, max_uses, valid_until)
VALUES ('LAUNCH50', 'promo_xxx', 50, 100, NOW() + INTERVAL '30 days');

Codes without a stripe_promotion_code_id still validate (useful for

soft launch testing) but won't change the price at Checkout.

anthropic-key

/api/network/ai-assistant uses the Anthropic SDK with model

claude-opus-4-7 (or whatever ANTHROPIC_MODEL_RECOMMENDER overrides).

The route now wraps every upstream call: missing key → 503, SDK throw →

503, empty draft → 503. The real error message is in

app_error_log server-side.

Operational note (2026-05-21): the production key has a $0 credit

balance and is returning HTTP 402 from Anthropic. From the client's

perspective this surfaces as a 503 "AI service temporarily unavailable" —

the same shape as a missing-key error, by design (don't leak billing

state).

To restore:

  1. Top up the workspace at https://console.anthropic.com/settings/billing.
  2. Confirm a 200 response from `curl

https://api.anthropic.com/v1/messages -H "x-api-key: $KEY" -d

'{"model":"claude-opus-4-7","max_tokens":8,"messages":[{"role":"user","content":"ping"}]}'`.

Audit log fallback

When the Postgres insert into app_error_log or admin_audit_log fails,

the logger now writes a JSON line to the path in

IAMS_FALLBACK_LOG_PATH (defaults to /var/log/iams-audit-fail.log).

This file is included in the nightly backup tarball — see

scripts/backup-daily.sh.