Most failures in FaPost are silent by nature. A provider that cannot reach you
does not complain; a queue that is not being worked simply grows. Knowing where
to look matters more than having alerts on everything.
Horizon
Queues are processed by Horizon, and its dashboard is the first place to look
when messages arrive but nothing happens.
Four supervisors run, grouped so that the latency-critical work is isolated:
inbound processing and conversation replies share dedicated processes, while
broadcasts, logging and background synchronisation are worked elsewhere. See
Queues.
In host tenancy mode the dashboard is served on the base domain and on declared
platform subdomains only, never on a tenant’s host, and it stays closed until a user
passes the viewHorizon gate; see
Several tenants.
What to watch:
- Wait time on
flow.execution — this is how long a contact waits before the
assistant even begins. It should be near zero.
- Failed jobs — a rising count on
flow.execution means inbound messages are
not being answered: replies are sent from inside that job.
- Throughput on
messaging.broadcast — expected to spike; it is the one
queue whose depth is not alarming.
An installation without Horizon running accepts messages and never answers
them. Everything looks healthy from the browser — the site serves, the panel
works — because nothing about the web tier depends on the workers. See
Services.
The scheduler
Four scheduled tasks matter, and all fail silently if the scheduler is not
running:
The monthly one is the trap: it fails at a month boundary, weeks after the
scheduler stopped, and presents as flow logging breaking for no apparent reason.
Conversation retention
The conversation transcript is kept forever by default. To cap it, set
CONVERSATION_RETENTION_DAYS in .env. The daily conversations:prune task then
drops, in every tenant schema, the monthly conversation_messages partitions that
lie wholly older than that many days. Whole months only, so a message lives between
CONVERSATION_RETENTION_DAYS and about 31 days longer.
Pruning is irreversible. A dropped partition is gone for good; only a database
backup brings it back. Preview first with
php artisan conversations:prune --dry-run.
Conversation threads (the inbox rows and their metadata) and downloaded media are
not touched. With the variable unset the task does nothing.
Webhook routing
Inbound routing resolves through Redis rather than the database, which makes the
registry a cache with a source of truth behind it — and caches drift.
Reports and optionally repairs inconsistency between Redis and the database.
Rebuilds the registry from the database. Run it after a Redis flush, a restore,
or a move to new Redis infrastructure — without it, channels exist and no message
resolves.
Checks the ingress end to end: which driver is active, whether Redis is
reachable, whether the key prefix matches, whether published specs agree with the
adapters, and how many channels still point at a previous address. Run it first
whenever webhooks are not arriving.
Per-assistant health
Each assistant console shows two counters that should
stay boring: live sessions and errors in the last 24 hours.
Live sessions climbing without traffic to explain it means conversations start
and do not finish — usually a flow waiting at an input nobody answers. Errors
above zero means at least one node failed; Flow logs says
which.
Application logs
Laravel’s log carries application errors. Telescope is available in development
and records every request with its contents — which is why it must be off in
production.
Backpressure
Provider rate limits are handled preventively rather than as a reaction to being
rejected. The relevant settings are per-tenant: message rate per chat, broadcast
chunk size, and how hard the platform pushes when a provider strains.
A provider complaining about rate is a signal to lower those, not to add
retries — a retry storm against a limit is the same request made worse.