← Back to projects
Project 1/20/2026

Gold Transaction Engine in n8n

Designing a state-aware, multi-role escrow bot using n8n, PostgreSQL, and Chatwoot to secure high-value gold trades.

This article details the technical architecture and logic behind the Dapuremas Goldbot, a system built to automate escrow services for peer-to-peer gold trading.

The Challenge: Chaos in the Gold Market

At Dapuremas, one of our biggest operational bottlenecks was managing high-value peer-to-peer gold transactions. Buyers and Sellers needed a neutral ground—an escrow service—but manual coordination via WhatsApp groups was a nightmare. Agents were spending hours manually connecting parties, verifying identities, and overseeing chats.

The Core Problems:

  • Identity Fraud: In open WhatsApp groups, it was hard to verify who the real "Seller" was. Impersonators could easily slip in.
  • Unmonitored Negotiations: Deals happened in private chats, leaving no audit trail for dispute resolution.
  • Manual Overhead: Every transaction required an agent to manually create a group, add participants, and monitor the chat 24/7.

My task was to build an automated "Escrow Bot" that could spin up a secure, private chat room for every deal, assign specific roles (Buyer, Seller, Admin, Courier), and log everything for compliance—all without forcing users to install a new mobile app.

The Architecture: A State-Driven Machine

I chose a stack that prioritized auditability and flexibility. I didn't just write a script; I built a visual engine using low-code tools backed by a robust database.

How Data Flows Through the System

Step Component Action
1 WhatsApp User sends a message ("Hi", "/create").
2 Chatwoot Receives message via API & forwarding webhook.
3 n8n Process Logic: Checks User State & Routing Rules.
4 PostgreSQL Stores/Retrieves Session State (e.g., awaiting_role).
5 Chatwoot Sends automated reply back to User.

The Brain: n8n

I utilized n8n for its visual workflow capabilities. Instead of a monolithic Node.js script, I broke the system down into modular components. This allowed me to visualize the logic flow and debug specific parts (like "Registration" or "Message Relaying") independently. The visual nature also meant that non-developer stakeholders could understand the process flows.

The Interface: Chatwoot

For the user interface, we used Chatwoot as a "headless" agent. It acts as the gateway to WhatsApp and Telegram. The bot "listens" to every incoming message via a webhook, processes it, and then sends a response back through the Chatwoot API. This allows for an omnichannel experience where the backend logic is agnostic of the messaging platform.

The Memory: PostgreSQL & State Management

The critical piece of this architecture is PostgreSQL. Stateless bots are forgetful; they don't know if a user is just saying "Hi" or confirming a million-rupiah purchase.

I implemented a table called session_state to track exactly where a user is in a conversation flow. For example, if a user triggers the "Create Transaction" command, their state changes to awaiting_party_count. The next message they send is treated as a number (the count), not a generic chat message. This state-machine approach allows for complex, multi-turn conversations.

Inside the Logic: Managing the Transaction Lifecycle

1. Onboarding & Identification (The Router)

The system starts with a Main Router. When a message comes in, it first checks: "Does this user exist in our database?"

  • If No: The user is routed to the Registration Module. We silently create a User record linked to their ID. This gives every subsequent message a persistent identity.
  • If Yes: We check their session_state. Are they in the middle of a command? If so, route them to the specific handler. If not, check if they are part of an active transaction.

2. Secure Room Creation & Role Assignment

To start a deal, a user sends the /buat (create) command. The bot generates a unique, random 6-character code (e.g., TXN-ABC123) and creates a record in the transactions table.

The Logic: I decided to separate "Creation" from "Joining". A user creates a room for N participants, but they aren't automatically the "Seller". They must explicitly choose their role (Buyer, Seller, Admin, etc.) in the next step. This flexibility allows an Agent to set up a room for two other people without being forced to be a participant themselves.

3. The "Man-in-the-Middle" Relay Engine

The core innovation is the Relay Engine. When a transaction is active, the bot acts as a proxy or a "Man-in-the-Middle".

Sequence: How a Message is Relayed

This sequence ensures anonymity and clear ownership of every message sent.

Actor Type Message Content
Seller Sends "I have shipped the item."
Bot Intercepts (Database lookup: Who else is in this deal?)
Bot Forwards To Buyer: 📨 **SELLER:** I have shipped the item.
Buyer Receives They see the message is from "SELLER", not a phone number.

Why this matters:

  • Anonymity: Users never see each other's phone numbers or direct contacts.
  • Role Clarity: You always know if you are talking to the "Admin" or the "Buyer".
  • Audit Trail: Every single relayed message is inserted into a messages table in Postgres. This creates an immutable audit trail that can be reviewed if a dispute arises.

Safety & Value-Add Features

The "Exit" Protocol

In high-stakes deals, accidental drop-offs can be panic-inducing. I built a specific Confirmation Protocol. If a user types /keluar, the system doesn't just kick them out, it changes their state to confirm_exit and asks, "Are you sure? (YES/NO)".

Upon confirmation, the system updates the database to mark them as inactive and broadcasts a system alert to all other participants: ⚠️ **BUYER** has left the transaction. This transparency ensures no one "ghosts" a deal without the room knowing.

Utility: Real-Time Price Scraper

To make the bot "sticky" and useful even outside of active deals, I added a Check Price feature. Since the official gold price API was prohibitively expensive or non-existent for our local market, I wrote a robust web scraper.

In the Pricing Module, I implemented a script that fetches the HTML from harga-emas.org. Because reliable scraping is hard, I used a "fallback strategy" with multiple Regular Expressions. It tries to match the price using three different HTML patterns. If the site layout changes slightly, the bot attempts the next pattern, ensuring high availability of price data.

Impact

This system transformed Dapuremas's operations. What used to be a chaos of unmonitored WhatsApp chats is now a structured, logged, and secure transaction environment. It proved that complex business logic—state management, role-based access control, and audit logging—can be effectively built using modern low-code tools like n8n combined with a solid database architecture.