Operate and recover the Chatwoot connector
Use this runbook to locate a Chatwoot connector failure before changing state. It starts with process, readiness, lease, storage, and provider evidence, then moves to Action, webhook, and central Event delivery.
When a provider mutation has an uncertain outcome, stop new related mutations. Read durable Action and provider state before any retry or replacement Action.
Signal order
Read signals in this order:
| Order | Surface | Primary question answered |
|---|---|---|
| 1 | GET /health |
Is the host HTTP process responding |
| 2 | GET /ready |
Can this replica accept routed Actions now |
| 3 | GET /metrics |
Is pressure, rejection, or lease recovery changing |
| 4 | Connector registry | Is the version, route, replica status, and lease authoritative |
| 5 | Durable Action state | Was work admitted, dispatched, completed, or left uncertain |
| 6 | Chatwoot provider reads | What state exists in the configured account |
| 7 | Webhook response and local Event state | Was inbound work suppressed, persisted, or queued |
| 8 | Central Event ingress | Was a locally durable Event acknowledged upstream |
Do not use a later signal to overwrite an earlier fact. A live process can be not ready, and a ready host cannot establish the outcome of an earlier provider mutation.
Liveness is process-only
GET /health always returns a static HTTP 200 body while the host router can
serve:
{
"status": "ok",
"protocol": "AIP",
"version": "1.0",
"service": "aip-connector-host"
}
This route does not probe PostgreSQL, Chatwoot, the lifecycle lease, gateway trust, admission, webhook replay state, or central Event delivery.
Use it for process and listener liveness only.
Readiness is a live composite probe
GET /ready performs a bounded durable-storage check and the connector health
probe. It also reads the local drain flag and current lease expiry.
HTTP 200 requires all four conditions:
- the replica is not draining;
- its lifecycle lease is still valid;
- durable runtime storage is readable and writable;
- authenticated Chatwoot account health is ready.
Any false condition returns HTTP 503. The body has this source-owned shape:
{
"status": "ready",
"lease_valid": true,
"draining": false,
"runtime": {
"durability": "durable_shared",
"ready": true,
"detail": "durable runtime storage is readable and writable",
"recovery": {
"recovered_at": "2026-01-01T00:00:00Z",
"pending_approvals": 0,
"recoverable_transactions": 0,
"queued_results": 0,
"delegation_results": 0,
"callback_deliveries": 0
}
},
"connector": {
"ready": true,
"detail": "Chatwoot account API is reachable"
}
}
status changes to not_ready on failure. Values shown above are an
illustrative healthy response; inspect the returned values rather than matching
the example timestamp or counts.
The runtime.recovery object is a bounded startup-recovery summary. It is not
a current queue, approval, transaction, callback, webhook, or outbox backlog.
Chatwoot product health
The connector health probe performs:
GET /api/v1/accounts/{configured_account_id}
api_access_token: <configured token>
The common host bounds this probe by the configured health timeout, which defaults to 2,000 ms.
A successful result establishes provider reachability and token acceptance for that account route at that moment. It does not exercise:
- all 153 capabilities;
- approval or idempotency;
- provider mutation payloads;
- webhook authentication;
- message delivery;
- central Event publication.
Host metrics
GET /metrics renders all eight Prometheus families on every scrape:
| Metric | Type | Operational meaning |
|---|---|---|
aip_connector_host_ready |
Gauge | Cached connector and storage flags combined with live lease and drain state |
aip_connector_host_storage_ready |
Gauge | Last stored durable-storage probe result |
aip_connector_host_in_flight |
Gauge | Actions currently executing in this process |
aip_connector_host_requests_total |
Counter | Native message requests received |
aip_connector_host_rejected_total |
Counter | Instrumented validation, trust, route, readiness, capacity, credential, approval, or gateway rejections |
aip_connector_host_heartbeat_failures_total |
Counter | Failed lease renewal or recovery attempts |
aip_connector_host_lease_recovery_attempts_total |
Counter | Re-registration attempts after lease expiry |
aip_connector_host_lease_recoveries_total |
Counter | Re-registrations that applied a new lease |
The metric families have no application labels and reset when the process restarts. Use bounded scrape-target metadata to identify the replica, version, zone, and connector type.
The ready gauge does not run a provider or storage probe. /ready and the
heartbeat loop update those cached flags. Read the readiness body when the
cause matters.
Do not calculate success as requests minus rejections. Response signing, process errors, durable Action state, and provider outcomes are not represented by that subtraction.
Source-visible observability gaps
The reviewed host exposes no public Chatwoot-specific metric for:
- provider request latency or status distribution;
- response-size rejection count;
- webhook authentication or loop-suppression count;
- replay-state entries or CAS contention;
- local Event append count;
- pending central Event outbox batches;
- provider business objects or delivery state.
ConnectorHostEventPublisher::pending_batches exists as an internal bounded
method, but the Chatwoot HTTP host does not publish it as a route or metric.
Use the webhook response’s central_delivery value as per-delivery evidence.
Use controlled internal diagnostics for current outbox state. Do not infer an
empty backlog from a healthy scrape.
Catalogue results can include a provider request ID from x-request-id or
x-runtime. Composite results do not expose a provider request ID. Preserve
the distinction during incident correlation.
Heartbeat and lease behavior
The heartbeat loop periodically:
- probes Chatwoot account health;
- probes durable storage;
- records the two readiness flags;
- reports in-flight Actions;
- renews the lease or recovers an expired lease.
An unhealthy heartbeat moves the registry replica offline. When storage and provider health recover, the loop can return it to ready under the current lease or re-register after expiry.
Lease recovery keeps one request ID and registration payload across ambiguous control-plane failures. This prevents a lost response from creating a second lifecycle mutation. Definitive admission or configuration rejection clears that pending request before a corrected attempt.
Increasing heartbeat failures with no recovery indicates a control-plane, identity, sequence, admission, or network boundary. It does not by itself prove a Chatwoot provider failure.
First response
1. Contain
Stop new related mutations when:
- provider outcome is uncertain;
- the wrong account or route may be active;
- storage consistency is unknown;
- credentials may be revoked or misbound;
- webhook or outbox state may be lost;
- a rollout changed version or manifest identity.
Drain the replica when it must stop receiving new Actions. Keep the process and state available long enough to inspect pinned work and complete bounded recovery.
2. Capture bounded evidence
Retain:
- host, version, instance, replica, tenant, and external-account identity;
/health,/ready, and/metricsobservations with timestamps;- registry route, replica status, lease sequence, and expiry;
- configuration and credential revisions without secret bytes;
- Action ID, capability, canonical input hash, approval, and key hash;
- provider status and request ID when exposed;
- webhook delivery ID and response fields when relevant;
- final provider reads used for reconciliation.
Do not retain tokens, signing seeds, webhook secrets, or unrestricted customer payloads in incident notes.
3. Classify the failing boundary
| Observation | Likely boundary | Next evidence |
|---|---|---|
/health unavailable |
Process, listener, edge, or network | Supervisor state and bounded startup error |
/health is 200 and /ready is 503 |
Drain, lease, storage, or provider | Read each readiness field |
runtime.ready is false |
Durable PostgreSQL or storage timeout | Database reachability, migration, and ownership |
connector.ready is false |
Provider origin, token, account, TLS, or provider availability | Bounded health detail and credential revision |
lease_valid is false |
Heartbeat, control plane, sequence, or admission | Registry replica and recovery counters |
draining is true |
Planned or interrupted shutdown | Lifecycle owner and in-flight count |
| In-flight equals configured capacity | Local saturation | Action duration, route pressure, and capacity rejection |
| Rejections increase | Input, trust, route, readiness, capacity, credential, approval, or gateway | Signed protocol error code |
Webhook returns 401 |
HMAC, clock, raw bytes, JSON, or connector mapping | Exact header identity and bounded host diagnostic |
Webhook returns 503 |
Replay state, Event Log, or durable outbox | Durable-state health and retained delivery ID |
Webhook returns durably_queued |
Central Event delivery pending | Fixed endpoint, signer, and internal outbox diagnostics |
| A mutation returns connector failure | Provider may already have applied it | Durable Action plus authoritative provider reads |
Recovery procedures
Storage not ready
- Keep the replica out of routing.
- Verify the configured database URL file and least-privilege role.
- Restore network, database, schema, and storage-health availability.
- Preserve Action, replay, Event, and outbox tables.
- Restart only when production Runtime Stores remain one coherent durable class.
- Inspect startup recovery and require
runtime.ready: true. - Reconcile every mutation whose dispatch boundary is unknown.
Do not edit leases, replay identities, idempotency records, or outbox entries manually to force readiness.
Provider health or authentication fails
- Confirm the fixed provider origin and configured account.
- Identify the credential revision without printing the token.
- Distinguish TLS, DNS, timeout,
401,403, rate limit, and server failure. - Rotate the owner-only token file through a replacement replica when needed.
- Re-run readiness and the safe
account.getcanary. - Reconcile prior mutations before enabling writes.
The token is read at startup. Replacing file bytes does not prove a running process adopted them.
Lease or control-plane recovery fails
- Compare local instance, replica, version, manifest, artifact, and sequence with the registry.
- Verify the fixed control endpoint, trusted DID, TLS, and lifecycle database.
- Preserve the host’s pending recovery request across ambiguous transport failure.
- Correct definitive admission or configuration rejection before another request.
- Require a valid ready lease and correct route before admitting traffic.
Do not create a replacement replica with the same replica ID while a live lease or pinned Action ownership remains unresolved.
Capacity is exhausted
connector_host.capacity is returned before provider dispatch and is marked
retryable by the host. Confirm durable Action state before repeating the same
correlated request.
Reduce upstream pressure or add an admitted replica before raising
AIP_CONNECTOR_HOST_MAX_IN_FLIGHT. A higher local limit does not increase
provider capacity or preserve latency.
Provider mutation outcome is uncertain
Treat provider-temporary, transport, cancellation, and oversized-response invocation failures as possible provider success.
- Keep the original Action ID, key, approval, and input.
- Read durable Action and provider state.
- Correlate provider request ID or AIP Action content attributes when present.
- Accept matching state, escalate ambiguity, or create a separately approved corrective intent.
- Never replace uncertainty with an uncorrelated retry.
All Chatwoot connector failures currently report retryable: false. Capability
contracts separately identify safe GET reads and the status composite.
Webhook authentication or replay state fails
Preserve the provider delivery ID, timestamp, signature identity, and protected raw-body evidence. Do not log the secret or full customer payload.
Correct clock, proxy byte preservation, signature format, secret revision, or durable replay state. Replay the same provider delivery only under the signed freshness and deterministic Event rules.
Central Event delivery is pending
A durably_queued webhook response means the local outbox owns delivery.
Repair the fixed central endpoint, TLS policy, host signer, admitted channel,
or central ingress without asking the provider to create a new delivery.
The background worker retries with bounded backoff. Preserve the outbox and Event Log through restart. Confirm central acknowledgement and Event identity before declaring the incident resolved.
Post-recovery verification
Require:
/healthis reachable;/readyreturns200with a valid lease, no drain, and both probes ready;- the registry exposes the intended version, replica, endpoint, and account;
- host-ready and storage-ready gauges agree with the latest readiness probe;
- recovery counters stop increasing unexpectedly;
- the safe account-read canary uses the intended route;
- every uncertain mutation has an explicit provider-state decision;
- webhook and central Event delivery are reconciled when enabled;
- no state, secret, or customer data was deleted to obtain recovery.
Monitor through at least one lease renewal and the deployment’s normal provider observation window. These checks establish operational recovery, not complete capability qualification.