Skip to content

Install ​

Running the whole stack on a laptop. If you only want to call the API of an existing deployment, you do not need any of this — go to Quickstart.

Requirements ​

VersionWhy
PHP8.3 or newer; 8.4 is what it is developed againstThe control plane is Laravel 13.
Node22 or newer; 24 is what it is developed againstThe gateway, SDK and CLI are ESM-only.
pnpm11Workspace package manager.
Dockerany recentPostgres 17 + pgvector, Redis, NATS JetStream, MinIO, FreeSWITCH, and the observability stack.

CallAgent is developed against Laravel Herd for PHP and Docker for the backing services.

The stack ​

bash
git clone <repo> callagent
cd callagent

pnpm install
pnpm infra:up                 # docker compose up -d

cp apps/control-plane/.env.example apps/control-plane/.env
php apps/control-plane/artisan key:generate
php apps/control-plane/artisan callagent:generate-credential-key
php apps/control-plane/artisan migrate --seed

pnpm infra:up starts Postgres, Redis, NATS, MinIO, FreeSWITCH and the Prometheus / Loki / Tempo / Grafana stack. The SIP test peer and SIPp are behind a sip-lab compose profile and are not started by default:

bash
docker compose --profile sip-lab up -d

Ports are shifted on purpose

Postgres is on 5433, Redis on 6380, MinIO on 9002/9003, FreeSWITCH's ESL on 8022. They are moved off the defaults so a local Postgres or Redis you already run is not shadowed. .env.example already points at the shifted ports; if you change one in docker-compose.yml, change it in .env too.

callagent:generate-credential-key is not optional. Tenant provider credentials are encrypted with a key deliberately separate from APP_KEY, so that rotating the application key does not mean re-encrypting every tenant's secrets. Without it, storing a credential fails.

Check it before you go further ​

bash
php apps/control-plane/artisan callagent:check
+-----------------------+--------+----------------------------------+
| Component             | Status | Detail                           |
+-----------------------+--------+----------------------------------+
| PostgreSQL            | OK     | v17.11                           |
| pgvector              | OK     | v0.8.6                           |
| Redis                 | OK     | reachable                        |
| Object storage        | OK     | read/write ok                    |
| Credential encryption | OK     | key valid, round-trip ok         |
| Voice Gateway         | OK     | healthy                          |
| NATS                  | OK     | healthy                          |
| FreeSWITCH ESL        | OK     | port open                        |
+-----------------------+--------+----------------------------------+

This is the fastest way to find a wrong port or a missing key. Fix every row before following the quickstart.

php artisan callagent:doctor is the next one to reach for: it diagnoses class-load faults that stop the application booting at all.

doctor can be killed by the fault it is meant to report

It autoloads the application's classes to find a broken one, so a fatal class-load error takes doctor down with it and you get the raw PHP error instead of a report. That is still the answer — read the fatal, it names the file and the line. callagent:check does not autoload the dashboard and keeps working when doctor does not.

Run it ​

Three processes, three terminals:

bash
pnpm cp:serve                                  # control plane on :8000
pnpm --filter @callagent/voice-gateway dev     # voice gateway on :3010
php apps/control-plane/artisan horizon         # jobs: webhooks, artifacts, retention

The queue worker matters more than it looks. Webhook delivery, artifact relocation, retention and post-call transcription are all queued on Redis. Without a worker they never happen, and nothing in the API says so. php artisan queue:work does the same job if you would rather not run Horizon.

Health:

bash
curl http://localhost:8000/api/health
curl http://localhost:3010/health     # also lists every provider adapter registered

The gateway's health response is the authoritative answer to "which providers does this build know how to speak to".

migrate:fresh is not safe in a shared checkout

migrate:fresh, migrate:rollback and db:wipe drop data other work in flight may be using. Plain php artisan migrate is idempotent.

Scheduled work ​

Several things only happen on a schedule. In development, run the scheduler:

bash
php apps/control-plane/artisan schedule:work

It drives retention (daily at 03:15), analytics rollups, webhook retry re-queueing, the contact-centre queue tick, monitor evaluation, SIP OPTIONS probing and campaign dispatch. A queue that never offers a call to an operator is usually this.

Packages ​

bash
pnpm build                                  # everything
pnpm --filter @callagent/web-sdk build      # browser SDK + widget bundles
pnpm --filter @callagent/cli build          # the `callagent` binary
pnpm --filter @callagent/docs-site build    # this site

The CLI ​

Not published to npm

@callagent/cli and @callagent/web-sdk are private: true workspace packages. There is no npm i -g @callagent/cli and no public CDN bundle. Build from the workspace:

bash
pnpm --filter @callagent/cli build
node apps/cli/dist/index.js --version

Link it if you want the bare callagent command:

bash
cd apps/cli && npm link

Then:

bash
callagent login            # prompts, echo suppressed
callagent whoami

See the CLI reference for every command and flag.

The browser SDK ​

Same caveat — build it from the workspace and serve the bundle yourself:

bash
pnpm --filter @callagent/web-sdk build
# dist/index.js and dist/widget.js
html
<script src="/your-own-path/widget.js"
        data-public-key="ca_pk_…"
        data-assistant-id="01JQ…"></script>

The public key prefix is ca_pk_. See the widget reference.

Tests ​

bash
pnpm test                                   # every workspace package
pnpm cp:test                                # the control plane's Pest suite
pnpm --filter @callagent/voice-gateway test

Next ​

Documented against the code. Anything not yet implemented is labelled, never described as working.