D Diagent docs

Get started

Install Pitchbar (self-host)

This guide walks you from a fresh server to a running Pitchbar install with one admin account, one published agent, and the widget answering on a test page. Expect 20–40 minutes if your server is already provisioned with PHP, Node, a database, and Redis.

Two paths to Pitchbar
Hosted Pitchbar customers don't need this page β€” sign up at the marketing site and follow the Quickstart. This page is for buyers running Pitchbar on their own server (CodeCanyon Regular / Extended license) or self-host operators deploying from source.

1. Server requirements

ComponentMinimumNotes
PHP8.3+8.4 recommended. Extensions: bcmath, curl, fileinfo, gd, intl, mbstring, openssl, pdo_pgsql (or pdo_mysql), tokenizer, xml, zip.
Composer2.6+Used to install PHP dependencies.
Node.js20+Used to build the admin SPA and the visitor widget.
DatabasePostgreSQL 14+ or MySQL 8.0+Postgres is the primary target.
Redis7+Cache, sessions, queue, hot-path retrieval cache.
RAM / CPU2 vCPU / 2 GBOne app + one worker process. Scale up if you'll run them on the same box.
Disk10 GB+Application + log volume. Vector store sits in Cloudflare / Qdrant, not on disk.
TLSHTTPSThe widget requires an HTTPS origin to load on customer sites. Use Caddy / Nginx / Cloudflare in front of FrankenPHP.
SMTPany providerPostmark / Resend / SES / your own SMTP. Required for password reset, lead notifications, billing receipts.

External accounts you'll need

  • At least one LLM provider β€” Cloudflare Workers AI is the cheapest and what we recommend (chat + embeddings + vector DB + browser crawler all on one bill). OpenAI works as a drop-in. OpenRouter works too and exposes a free Llama 3.3 model.
  • A vector store β€” Cloudflare Vectorize (preferred, shares the Cloudflare account) or a self-hosted Qdrant instance.
  • Optional: Stripe for billing customers, and Sentry / Honeycomb for error / trace reporting.

2. Get the code onto the server

Upload the source bundle you downloaded (CodeCanyon zip), or clone your private repo, into the document root. Everything in this guide assumes you're inside the project directory.

cd /var/www/pitchbar          # or wherever you unpacked the zip
composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate

php artisan key:generate writes a fresh APP_KEY to .env. Back this value up the moment you generate it β€” every encrypted column in app_settings (Stripe / Cloudflare / OpenAI keys you'll paste in step 7) is sealed with this key. Losing it means losing those secrets.

3. Configure the database connection

Edit .env and fill the database block. The defaults point at a local Docker Postgres; swap to your actual host.

DB_CONNECTION=pgsql            # or mysql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=pitchbar
DB_USERNAME=pitchbar
DB_PASSWORD=…strong-password…

Create the database first if it doesn't exist:

createdb -U postgres pitchbar
# or, MySQL:
mysql -uroot -p -e "CREATE DATABASE pitchbar CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

4. Fill the rest of .env

Every key you can flip via the admin UI later β€” Stripe, PayPal, Razorpay, Cloudflare, OpenAI, OpenRouter, mail, branding β€” can be left blank in .env and pasted in the web admin instead. The keys below are the ones the app needs at boot, before you can open the admin.

VariableValue
APP_URLThe public HTTPS URL you'll serve the app from, e.g. https://app.example.com. Used to build widget snippets, OAuth callbacks, and signed URLs.
APP_NAMEDisplay name shown in the title bar and emails.
APP_ENVproduction.
APP_DEBUGfalse.
REDIS_HOST / REDIS_PORT / REDIS_PASSWORDRedis connection.
SESSION_DRIVER / CACHE_STORE / QUEUE_CONNECTIONAll redis in production.
WIDGET_JWT_SECRETThe HS256 signing secret for visitor session JWTs. Generate openssl rand -hex 32 and paste the result. Do not leave at the default.
BROADCAST_CONNECTIONreverb if you want the realtime inbox + live-chat handoff. Set to null to disable.
REVERB_APP_ID / REVERB_APP_KEY / REVERB_APP_SECRETRandom tokens identifying the Reverb app. Generate fresh strings.
REVERB_HOSTPublic hostname for the WebSocket process β€” same domain as APP_URL if you reverse-proxy WS on the same host.
REVERB_SCHEMEwss in production.
MAIL_FROM_ADDRESS / MAIL_FROM_NAMESender identity for outgoing email. Required.

Full reference for every variable lives at Environment variables.

LLM provider keys (you can also paste these in the admin later)

Set at least one of these so a freshly created agent can answer. The auto-binder picks Cloudflare β†’ OpenRouter β†’ OpenAI, in that order, based on which keys are present.

# Cloudflare Workers AI (preferred)
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_API_TOKEN=
CLOUDFLARE_VECTORIZE_INDEX=pitchbar-chunks

# or OpenAI
OPENAI_API_KEY=

# or OpenRouter (free Llama 3.3 model available)
OPENROUTER_API_KEY=
LLM_PROVIDER=openrouter        # required to opt into OpenRouter
Cloudflare Vectorize provisioning lag
Newly created Vectorize indexes take ~2 minutes before queries return results. Upserts succeed immediately; reads return 0 until the index is fully provisioned. VectorizeClient::ensureCollection is idempotent, so re-running install steps is safe.

5. Run the migrations and seed the plans

php artisan migrate --force
php artisan db:seed --class=PlanSeeder --force

PlanSeeder creates the Free / Pro plan rows the billing system reads. It is idempotent β€” re-running it won't duplicate plans.

Skip UserSeeder in production. It creates the demo accounts [email protected] / [email protected] with the public password password β€” fine for local dev, an open door on a public deployment.

6. Build the frontend bundles

npm ci
npm run build              # admin Inertia SPA β†’ public/build/
npm run build:widget       # visitor widget β†’ public/widget/widget.js
php artisan storage:link   # symlinks public/storage β†’ storage/app/public
php artisan optimize       # caches routes, config, views

Both build outputs are committed alongside source in our deploy artifact (the CodeCanyon zip includes them pre-built), but re-running on the server guarantees the bundle matches the PHP version of the code you uploaded.

7. Serve the app and run the workers

Pitchbar runs on Laravel Octane + FrankenPHP for the HTTP server, Horizon for the queue, and Reverb for the WebSocket realtime channel. All three need to be supervised processes; here's the minimum shape for a single-host install using systemd.

App server

# /etc/systemd/system/pitchbar-app.service
[Unit]
Description=Pitchbar Octane (FrankenPHP)
After=network.target redis.service postgresql.service

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/pitchbar
ExecStart=/usr/bin/php artisan octane:start --server=frankenphp --host=0.0.0.0 --port=8000 --workers=4
Restart=always

[Install]
WantedBy=multi-user.target

Put your TLS terminator (Caddy, Nginx, Cloudflare proxy) in front, pointed at 127.0.0.1:8000. The reverse proxy is what serves https://app.example.com to the public; the Octane process only binds to localhost.

Queue worker

# /etc/systemd/system/pitchbar-horizon.service
[Unit]
Description=Pitchbar Horizon queue worker
After=network.target redis.service

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/pitchbar
ExecStart=/usr/bin/php artisan horizon
Restart=always

[Install]
WantedBy=multi-user.target

Horizon supervises crawl / index / default queues by default. Check the queue health from the platform admin at /admin/queue-health.

WebSocket process

# /etc/systemd/system/pitchbar-reverb.service
[Unit]
Description=Pitchbar Reverb WebSocket server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/pitchbar
ExecStart=/usr/bin/php artisan reverb:start --host=0.0.0.0 --port=8080
Restart=always

[Install]
WantedBy=multi-user.target

Reverse-proxy wss://realtime.example.com (or the same domain on a different path) to 127.0.0.1:8080. Skip this process if you set BROADCAST_CONNECTION=null β€” you'll lose the live inbox and human takeover features.

Enable and start everything

sudo systemctl daemon-reload
sudo systemctl enable --now pitchbar-app pitchbar-horizon pitchbar-reverb

8. Wire up the cron scheduler

Several jobs run on a schedule β€” refresh stale crawls, sync OAuth sources, release stale "needs human" conversations, suggest curated answers from gaps. Pick one of the two options below.

Option A β€” host cron (simplest)

Add a single line to root's crontab (or the user that owns the project files):

* * * * * cd /var/www/pitchbar && php artisan schedule:run >> /dev/null 2>&1

Option B β€” Cloudflare Cron Worker

Pitchbar can deploy a Cloudflare Worker that hits your install's /_internal/queue/tick endpoint every minute, so you don't need a host cron at all. Useful for serverless deploys where no process can run periodically.

After you've pasted your Cloudflare account ID and API token into Settings β†’ System (next step), open Settings β†’ System β†’ Cron worker and click Deploy. Status is reported at /settings/system/cron-worker/status.

9. Create your first admin

Sign up the normal way at {APP_URL}/register. Pitchbar auto-creates the first workspace for you. Then promote your account to super_admin from the command line so you can reach the platform admin and paste system keys.

php artisan pitchbar:make-admin [email protected]

Log out and back in; /admin and Settings β†’ System are now visible in the sidebar.

10. Drop in system keys via the admin (recommended)

Open Settings β†’ System as the super_admin. Paste:

  • Cloudflare account ID + API token + Vectorize index name + AI Gateway URL (optional).
  • OpenAI key (optional fallback).
  • OpenRouter key (optional, free Llama 3.3 model).
  • Stripe publishable + secret + webhook signing secret (optional, for billing).
  • PayPal / Razorpay if you want extra payment gateways.
  • Mail SMTP / API credentials.
  • Branding β€” replace "Powered by Pitchbar" with your own label, footer logo, and link target.

Each section has a Test button that talks to the upstream API with the key you just pasted β€” Test mail sends a real email, Test LLM calls a small chat completion, Test Stripe hits the Stripe API root, etc. Use these before saving so you catch a wrong key immediately instead of at the first customer signup.

APP_KEY rotation requires a manual migration. The encrypted columns in app_settings are sealed with the value of APP_KEY at the time you pasted them; rotating without re-encrypting renders them unreadable and you'll have to paste every key again.

11. Smoke test the install

  1. Visit {APP_URL}/admin and confirm the platform dashboard renders without red banners.
  2. From Settings β†’ System, click each Test button (mail, LLM, Stripe). Each should report success.
  3. From your workspace, run the Quickstart: create an agent, add one knowledge source from your own site, watch it flip to indexed, publish the agent, paste the embed snippet on a test page.
  4. Open your test page in an incognito window. Ask the agent a question that should be answered from the page you indexed. You should see streaming tokens and a citation appear within ~1 second.
  5. Check /admin/queue-health β€” crawl + index queues should be draining, no failed jobs.

Troubleshooting

SymptomFix
Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest You skipped npm run build or pulled changes to resources/js/ without rebuilding. Run npm run build + npm run build:widget.
Agent answers "I don't have enough information" even with sources indexed Likely a confidence threshold mismatch. Cloudflare's bge-base-en-v1.5 peaks at 0.55–0.65, OpenAI peaks higher. Open the agent's Advanced tab and lower confidence_threshold to 0.5 for Cloudflare-backed installs. New agents get this default automatically.
Widget script loads but never opens on customer sites Check Agent β†’ Settings β†’ Allowed origins. Each entry is strict-matched against the page's Origin header; an empty list means deny-everywhere. See Allowed origins.
Queue not draining; /admin/queue-health shows growing depth Horizon process isn't running or isn't subscribed to the right connection. sudo systemctl status pitchbar-horizon β†’ check it's active (running); QUEUE_CONNECTION in .env should be redis.
Live inbox doesn't update in real time Reverb process isn't running or the WS reverse-proxy isn't wired. Check REVERB_HOST + REVERB_PORT match what your reverse proxy forwards; in the browser console, you should see a successful wss://… upgrade.
"Crawl failed: …" on every source Probably no LLM provider configured. Settings β†’ System β†’ Cloudflare + click Test LLM. The error column on the source is sanitized for customers; super_admins see the raw upstream message on the source detail page.
Failed to load PostCSS config during npm run build You probably ran npm install --production. The build needs the dev dependencies β€” re-run npm ci with the default flags.

What's next?