Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DevMauricio03/n8n-whatsapp-ai-agent/llms.txt

Use this file to discover all available pages before exploring further.

A single PostgreSQL instance hosts three logically separate databases, each owned by a distinct layer of the platform. This isolation strategy ensures that n8n’s internal state, Chatwoot’s conversation data, and the business-specific agent data never share tables — a misconfigured query in one layer cannot accidentally read or corrupt data belonging to another. The n8n and chatwoot databases are managed exclusively by their respective services through their own migration mechanisms. The agent database is the only one that operators interact with directly: it holds the bd_clientes client table that the AI queries during conversations, and the conversational memory table that n8n’s Postgres Chat Memory node creates and manages.

Database structure

DatabasePurpose
n8nn8n internal configuration, encrypted credentials, workflow definitions, and execution history
chatwootChatwoot conversations, contacts, agent accounts, inbox configuration, and Sidekiq job state
agentBusiness client data (bd_clientes) and AI conversational memory

bd_clientes table

The bd_clientes table is the primary data source for AI-assisted customer lookups. When a customer provides a service folio during a conversation, the AI agent queries this table to retrieve their associated record and respond with accurate, database-backed information rather than fabricated data.

Column reference

folio
text
required
Primary key. The unique service identifier for a client record. Used as the upsert conflict target in the Google Drive sync process. Typically a human-readable code such as DEMO-2026-0001.
razon_social
text
The client’s registered business or personal name. Optional — may be null if the record was created without this field populated.
telefono
text
The phone number associated with the client record. Optional, but indexed for efficient lookup when matching inbound messages to client accounts. Stored as text to preserve formatting and country-code prefixes.
correo
text
The client’s registered email address. Optional.
materia_prima
text
The sample, material, or product associated with the service. Optional — the content and vocabulary depend on the specific business domain.
estatus_pago
text
The current payment status for the service. Optional. Common values are business-defined (e.g., PENDIENTE, PAGADO) and should be consistent with what the AI is instructed to report.
aplica_convenio
text
Indicates whether a commercial agreement or special pricing applies to this client. Optional. Typical values are SI or NO.
updated_at
timestamptz
Timestamp of the last time the record was created or updated. Defaults to now() at insert time and is set to now() on every upsert via the Google Drive sync. Useful for auditing the freshness of client data.

SQL schema

This is the exact schema created by docker/initdb/02-agent-schema.sql when the PostgreSQL volume is first provisioned:
\connect agent

CREATE TABLE IF NOT EXISTS bd_clientes (
    folio text PRIMARY KEY,
    razon_social text,
    telefono text,
    correo text,
    materia_prima text,
    estatus_pago text,
    aplica_convenio text,
    updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_bd_clientes_telefono
    ON bd_clientes (telefono);

-- n8n crea la tabla de memoria al configurar Postgres Chat Memory.
-- Se documenta su estructura esperada, pero no se crea aquí para evitar
-- incompatibilidades entre versiones del nodo.

Upsert example

The Google Drive sync branch uses an ON CONFLICT (folio) DO UPDATE pattern so that re-running the sync is always safe — existing records are updated in place and new folios are inserted without duplication. The following fictional record illustrates the pattern:
INSERT INTO bd_clientes (
  folio, razon_social, telefono, correo, materia_prima,
  estatus_pago, aplica_convenio
) VALUES (
  'DEMO-2026-0001', 'Cliente de prueba', '520000000000',
  'demo@example.com', 'Muestra de prueba', 'PENDIENTE', 'NO'
)
ON CONFLICT (folio) DO UPDATE SET
  estatus_pago = EXCLUDED.estatus_pago,
  updated_at = now();
Note that updated_at is explicitly set to now() in the DO UPDATE clause. The DEFAULT now() applies only at initial insert — it does not update automatically on subsequent writes without an explicit assignment.

Conversational memory

The n8n Postgres Chat Memory node creates and manages its own table in the agent database. This table stores the rolling message history that the AI agent uses as its context window — currently configured to retain the last 10 messages per session. Session identification. In the current workflow, the session is identified by the customer’s phone number. This means that all conversations from the same phone number share a single memory context, regardless of how much time has passed between them or how many different topics were discussed. Recommendations for memory management:
  • Where possible, use a non-reusable identifier — such as the Chatwoot conversation ID — rather than the phone number. Conversation IDs are unique per interaction, which prevents historical context from a previous conversation from influencing a new one.
  • Define and enforce a retention policy that specifies how many days of conversational memory are kept. Unbounded growth increases storage costs and may retain sensitive customer information longer than necessary or legally permitted.
  • Avoid storing information in the memory table that is not necessary for the AI to answer follow-up questions within the same conversation.
  • Verify that the AI agent cannot retrieve or reference memory rows belonging to a different customer’s session. The session key is the primary guard against cross-conversation leakage.
Do not manually create the Postgres Chat Memory table in the agent database without first checking the exact schema expected by the version of the n8n Postgres Chat Memory node you have installed. The expected table structure varies between n8n versions, and a manually created table with an incorrect schema will cause the memory node to fail or behave unexpectedly. Let n8n create the table automatically on first use.

Database access

To open a psql shell connected to the agent database from within the running container:
docker compose --env-file .env -f docker/docker-compose.yml \
  exec postgres sh -lc 'psql -U "$POSTGRES_USER" -d agent'

Verification queries

Once inside the psql shell, use these commands to confirm the schema and data are in the expected state:
\dt
SELECT count(*) FROM bd_clientes;
SELECT folio, estatus_pago, updated_at
FROM bd_clientes
ORDER BY updated_at DESC
LIMIT 10;

Migrations

The SQL scripts in docker/initdb/ are executed by PostgreSQL only when the data volume is created for the first time. If the volume already exists from a previous deployment, these scripts will not run again — even if you add new scripts or modify existing ones. This behavior is by design in the official PostgreSQL Docker image. For environments where the volume already exists, apply schema changes through the following manual process:
1

Create a backup

Before making any schema change, take a full dump of the agent database. Store the backup in a location outside the Docker volume.
2

Apply the change manually

Connect to the agent database using the psql shell and execute the DDL statement (e.g., ALTER TABLE, CREATE INDEX, ADD COLUMN) directly.
3

Record the migration

Log the change with its date, the n8n and stack version at the time, and the full SQL applied. A plain text file in the repository’s docker/initdb/ directory with a sequential number prefix is a simple but effective record.
4

Test read and write operations

After applying the migration, execute the verification queries above and run a manual n8n workflow execution that reads from and writes to the affected table to confirm correct behavior.
5

Maintain a rollback procedure

Prepare the inverse DDL statement (e.g., DROP COLUMN, DROP INDEX) and keep it ready. If the migration causes unexpected issues, the rollback procedure should be executable without the backup — the backup is a last resort, not the primary recovery path.

Data retention

Your organization’s legal and contractual obligations govern how long each category of data must be retained. The table below provides starting-point guidance; validate each period against applicable regulations before adopting it.
Data categorySuggested retention
n8n successful executionsShort — keep only what is needed for recent operational review (e.g., 7–14 days)
n8n error executionsLong enough for diagnosis and root-cause analysis (e.g., 30–90 days)
Conversational memoryOnly the period necessary for active customer support continuity
Database backupsEncrypted, with a defined expiry; rotate according to your backup policy
Client data (bd_clientes)As required by contract and applicable data protection regulation
Retention periods for customer conversation data and client records may be subject to regional data protection laws such as Mexico’s LFPDPPP or the EU’s GDPR if your business serves customers in those jurisdictions. Consult your legal team before setting final retention values.

Build docs developers (and LLMs) love