Quickstart
Bring AVA up on your own AutoTask instance, from clone to the app running
locally with real AutoTask, Entra ID, and AI wiring. Every command is
copy-pasteable; every environment variable name is exact.
Work through requirements.md first — you'll need the
AutoTask API user, the Entra app registration + security group, reCAPTCHA v3
keys, and an SMTP2GO account in hand.
Placeholders like <your-host> are yours to fill in. Shell examples assume
Linux/macOS. When you're done here, continue to
production-deployment.md for the production
stack and first-login.md for engineer onboarding and the
end-to-end smoke test.
Step 1 — Clone and install
git clone <clone URL provided with your license> ava-chat
cd ava-chat
npm installStep 2 — Create your env file from the template
cp .env.example .env.localYou will fill this in across the next steps. .env.local is gitignored and is
never baked into the Docker image — keep it out of version control.
configuration.md documents every variable; this
quickstart sets the minimum to boot plus the AutoTask picklists.
Secret with a
$: the AutoTask secret often contains$. Wrap it in
double quotes in.env.local, e.g.AUTOTASK_API_SECRET="ab$cd$ef".
Step 3 — AutoTask API user + zone discovery
Set the four AutoTask credential variables in .env.local:
[email protected]
AUTOTASK_API_SECRET="your-api-secret"
AUTOTASK_API_INTEGRATION_CODE=your-integration-code
AUTOTASK_API_ZONE_URL= # discovered nextAutoTask assigns each account to a data-center "zone". Discover your REST base
URL by querying the (unauthenticated) zone-information endpoint with your API
user's email:
curl "https://webservices.autotask.net/atservicesrest/v1.0/[email protected]"The response's url field is your zone base (e.g.
https://webservices5.autotask.net/ATServicesRest/V1.0). Put it in
AUTOTASK_API_ZONE_URL. AVA normalizes this value — a trailing slash is fine,
and it appends /V1.0 if you omit it — so either of these works:
AUTOTASK_API_ZONE_URL=https://webservices5.autotask.net/ATServicesRest/V1.0
AUTOTASK_API_ZONE_URL=https://webservices5.autotask.net/ATServicesRestVerify connectivity (Test 1 checks the zone URL + credentials; the optional
domain/email arguments run company + contact lookups):
npx tsx scripts/test-autotask.ts
# or, exercising the domain→company and email→contact matchers:
npx tsx scripts/test-autotask.ts aclientdomain.com [email protected]A PASS on Test 1 confirms the credentials, integration code, and zone URL are
correct. A 401 means the credential/integration-code/user triple is wrong (or
the API user is locked); a 403 means the security level is missing an entity.
Step 4 — Discover your picklist IDs
AutoTask picklist IDs are instance-specific. List yours:
npx tsx scripts/discover-picklists.tsThis prints values for status, priority, queueID, source, ticketType,
ticketCategory, issueType, and subIssueType, marking each [DEFAULT] /
[INACTIVE] / [SYSTEM]. Map them into .env.local:
AUTOTASK_DEFAULT_TICKET_STATUS=1 # your "New" status id
AUTOTASK_DEFAULT_TICKET_PRIORITY=3 # your "Normal" priority id
AUTOTASK_DEFAULT_TICKET_QUEUE_ID= # your helpdesk queue id
AUTOTASK_DEFAULT_TICKET_SOURCE= # your "Chat" source id (create it in AT if needed)
AUTOTASK_DEFAULT_TICKET_TYPE= # ticket type for chat-originated tickets
# Triage priority (P1-P4) -> your AT priority picklist ids. MUST discover and
# set all four: if a Pn is left empty, it falls back to the single shared
# AUTOTASK_DEFAULT_TICKET_PRIORITY above, so ALL AI-triaged chats file at the
# same AT priority regardless of severity.
AUTOTASK_PRIORITY_P1=
AUTOTASK_PRIORITY_P2=
AUTOTASK_PRIORITY_P3=
AUTOTASK_PRIORITY_P4=
# Client-facing note publish value — per-instance picklist; 1 = "Internal Only"
# is universally valid. Only change if your instance defines an external value.
AUTOTASK_NOTE_PUBLISH_EXTERNAL=1
# Resource role id for engineer time entries on close — SET THIS for your
# instance (unset warns at runtime and bills against a fallback role id that
# will not match your instance).
AUTOTASK_DEFAULT_ROLE_ID=The note-publish and role-id values are the two easiest to get wrong.
AUTOTASK_NOTE_PUBLISH_EXTERNALmust be apublishvalue your instance
actually defines (a nonexistent value makes the close-note post fail with a
500).AUTOTASK_DEFAULT_ROLE_IDwarns-not-fails when unset, so a missing
value boots fine but bills against the wrong role — set it deliberately.
Step 5 — Catchall company
Pick (or create) an AutoTask company to receive tickets from unknown/personal
email domains, and set its numeric ID. To find a company's ID, use the
connectivity script's domain lookup:
npx tsx scripts/test-autotask.ts yourdomain.com
# prints: - <Company Name> (ID: 1234, web: ...)AUTOTASK_CATCHALL_COMPANY_ID=1234Leaving it unset lets unknown-domain chats start without a ticket (they
recover at close), so setting it is strongly recommended.
Step 6 — Microsoft Entra ID
From your app registration and security group (see
requirements.md §2), set:
AZURE_AD_CLIENT_ID=your-app-client-id
AZURE_AD_CLIENT_SECRET=your-app-client-secret
AZURE_AD_TENANT_ID=your-tenant-id
AZURE_AD_REDIRECT_URI=https://<your-host>/api/auth/callback
ENGINEERING_GROUP_ID=your-security-group-object-idConfirm two things in the Entra portal:
- The redirect URI on the app registration matches
AZURE_AD_REDIRECT_URI
exactly (scheme, host, path). - Token configuration → Add groups claim (Security groups) is enabled for
the ID token, so thegroupsclaim is emitted. Without it, every
sign-in is denied withaccess_denied(fail-closed by design).
Step 7 — reCAPTCHA, session secret, email, AI key
# reCAPTCHA v3
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=your-recaptcha-site-key
RECAPTCHA_SECRET_KEY=your-recaptcha-secret-key
# Session signing — generate a strong random string (>=32 chars is ENFORCED
# at container start; placeholder-looking values are rejected):
# openssl rand -base64 48
NEXTAUTH_SECRET=your-generated-random-string
# SMTP2GO
SMTP2GO_HOST=mail.smtp2go.com
SMTP2GO_PORT=2525
SMTP2GO_USER=your-smtp2go-user
SMTP2GO_PASSWORD=your-smtp2go-password
[email protected] # no default — unset means every send fails closed
# Where the after-hours email-a-ticket form delivers. REQUIRED — the
# container refuses to start without it (no vendor default).
[email protected]
# AI provider key — the defaults use Anthropic, so ANTHROPIC_API_KEY is
# required unless you change LLM_FAST / LLM_QUALITY to another provider
# (see configuration.md for the provider matrix).
ANTHROPIC_API_KEY=your-anthropic-api-key
# Public URLs (used by the app and, for the container build, baked into the
# client bundle at build time).
NEXT_PUBLIC_APP_URL=https://<your-host>
NEXT_PUBLIC_SOCKET_URL=wss://<your-host>Set your identity/branding and business hours now if you like — with nothing
set, AVA presents a neutral "Your Company" identity, Mon–Fri 6am–6pm Pacific
business hours, and no support phone or portal link. Overriding all of it is
covered in configuration.md and
white-label.md.
Step 8 — Initialize the database and run
Fastest path — local process (recommended for the first run)
The custom server loads .env.local directly, so no container is needed to
verify your AutoTask/Entra/AI wiring:
npx prisma generate
npx prisma db push # creates the SQLite schema
npm run dev # custom server + Socket.ioOpen http://localhost:3000. (For a local run, set NEXT_PUBLIC_APP_URL /
NEXT_PUBLIC_SOCKET_URL to http://localhost:3000 / ws://localhost:3000,
and temporarily set BUSINESS_HOURS_BYPASS=true if you're testing outside
your configured hours.)
Container path
The image never contains your secrets — .env.local is mounted at runtime as
/app/.env.local, and docker-entrypoint.sh validates the required variables
before the server starts. The NEXT_PUBLIC_* values are baked at build
time, so pass them as build args:
docker build \
--build-arg NEXT_PUBLIC_APP_URL=https://<your-host> \
--build-arg NEXT_PUBLIC_SOCKET_URL=wss://<your-host> \
--build-arg NEXT_PUBLIC_RECAPTCHA_SITE_KEY=your-recaptcha-site-key \
-t ava:latest .
# Run just the app container, mounting your env file and the data volumes:
docker run -d --name ava \
-p 3000:3000 \
-v ava-data:/app/data \
-v ava-uploads:/app/uploads \
-v "$(pwd)/.env.local:/app/.env.local:ro" \
ava:latest
docker logs -f ava # expect "Environment validation passed." then "Syncing database schema..."If a required variable is missing, the entrypoint prints
ERROR: Missing required environment variables in /app/.env.local: … and
exits — fix .env.local and re-run.
Your app is up locally. For the public production stack — nginx
terminating TLS with Let's Encrypt auto-renewal, the hardened container
settings, and the build-before-swap upgrade flow — continue to
production-deployment.md. Then walk
first-login.md to onboard your first engineer and run
the end-to-end smoke test that proves the AutoTask round-trip.
Boot-requirement checklist (what the container validates)
docker-entrypoint.sh refuses to start unless every one of these is present in
the mounted env file. The steps above set each — this is the cross-check:
| Required variable | Set in step | Purpose |
|---|---|---|
NEXTAUTH_SECRET | 7 | Session-token signing (also checked for ≥ 32 chars, non-placeholder) |
AUTOTASK_API_USER | 3 | AutoTask auth |
AUTOTASK_API_SECRET | 3 | AutoTask auth |
AUTOTASK_API_INTEGRATION_CODE | 3 | AutoTask auth |
AUTOTASK_API_ZONE_URL | 3 | AutoTask REST base URL |
AZURE_AD_CLIENT_ID | 6 | Entra app registration |
AZURE_AD_CLIENT_SECRET | 6 | Entra app registration |
AZURE_AD_TENANT_ID | 6 | Entra tenant |
ENGINEERING_GROUP_ID | 6 | Engineer authorization (groups claim) |
RECAPTCHA_SECRET_KEY | 7 | reCAPTCHA server verification |
SMTP2GO_USER | 7 | Outbound email |
SMTP2GO_PASSWORD | 7 | Outbound email |
SUPPORT_FALLBACK_EMAIL | 7 | After-hours email-form recipient (no vendor default) |
ANTHROPIC_API_KEY* | 7 | AI provider key for the configured provider |
* The entrypoint requires the key for whichever provider(s) LLM_FAST /
LLM_QUALITY reference. Both default to Anthropic, so ANTHROPIC_API_KEY is
required out of the box; if you point a tier at openai/*, google/*, or
openrouter/*, that provider's key becomes the required one instead (see
configuration.md).
Everything else has a safe default or is optional. The NEXT_PUBLIC_* values
are validated by being present at build time, not by the entrypoint.
Updated about 1 hour ago