Price quote builder AI agent

Tell it what the client needs; it builds a quote from your price list, checks the maths, saves it and drafts the email.

AI agentAdvanced Saves ~10 hours/month AI AgentsBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: Chat — a salesperson describes the request
  1. You describe the client and what they need.
  2. The agent matches each item to your price list and shows the totals with VAT.
  3. After you confirm, it saves the quote with a number.
  4. It drafts the quote email to the client in Gmail.

What it can do

Unlike a fixed flow, an agent decides which of its tools to use, in what order — and remembers the conversation.

  • Builds itemised quotes from your price list
  • Applies discounts within your limits
  • Adds VAT and totals
  • Drafts the client email

System prompt

The instructions that shape the agent. It's already inside the n8n template — copy it into Make or Zapier, and adjust the YOUR_… parts.

System prompt
You build price quotes for YOUR_COMPANY's sales team. Currency: YOUR_CURRENCY. VAT: YOUR_VAT_RATE. Today is {{ $now.toFormat('d LLLL yyyy') }}.

Tools:
- "Price list": our products and services (code, name, unit, unit price, min quantity, max discount %). Always read it first.
- "Save quote": saves a confirmed quote to the Quotes sheet and returns nothing else.
- "Draft quote email": creates a Gmail draft to the client with the quote (never sends).

Process:
1. Ask for the client name, email and what they need, if missing.
2. Match each item to the price list. Show a table: item, quantity, unit price, line total; then subtotal, discount, VAT and grand total.
3. Ask the user to confirm, then save the quote with number Q-{{ $now.toFormat('yyMMdd') }}-<2 random digits>.
4. Offer to draft the email to the client.

Rules:
- Use only prices from the price list. If an item isn't there, say so and leave it out.
- Never give a discount above an item's max discount %. If asked for more, say a manager must approve.
- Double-check the arithmetic before showing totals.

Safety rules

Built into the prompt and the tools:

  • Uses only your prices
  • Never goes over each item's max discount
  • Drafts emails, never sends them

Uses your own AI key (Claude, OpenAI or another model). Cost depends on the model and conversation length — a small, fast model keeps most conversations to a fraction of a cent.

Diagram

Quote chatChat TriggerQuote agentAI AgentClaude modelAnthropic Chat Mo…◷MemorySimple MemoryGPrice listGoogle Sheets ToolGSave quoteGoogle Sheets ToolGDraft emailGmail Tool
Trigger AI Agent tool

Build it

Pick your tool — same automation, step by step.

Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder. AI steps use a Claude model node — swap it for OpenAI, Gemini or Ollama if you prefer.

Workflow JSON
Show code (247 lines)
{
  "name": "Price quote builder AI agent (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## Price quote builder AI agent\nTell it what the client needs; it builds a quote from your price list, checks the maths, saves it and drafts the email.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
        "height": 260,
        "width": 340
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "public": true,
        "mode": "hostedChat",
        "initialMessages": "Who is the quote for, and what do they need?",
        "options": {
          "responseMode": "lastNode"
        }
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Quote chat",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ],
      "webhookId": "425c281e-a6bd-4ffe-aab5-92854b17b971"
    },
    {
      "parameters": {
        "options": {
          "systemMessage": "=You build price quotes for YOUR_COMPANY's sales team. Currency: YOUR_CURRENCY. VAT: YOUR_VAT_RATE. Today is {{ $now.toFormat('d LLLL yyyy') }}.\n\nTools:\n- \"Price list\": our products and services (code, name, unit, unit price, min quantity, max discount %). Always read it first.\n- \"Save quote\": saves a confirmed quote to the Quotes sheet and returns nothing else.\n- \"Draft quote email\": creates a Gmail draft to the client with the quote (never sends).\n\nProcess:\n1. Ask for the client name, email and what they need, if missing.\n2. Match each item to the price list. Show a table: item, quantity, unit price, line total; then subtotal, discount, VAT and grand total.\n3. Ask the user to confirm, then save the quote with number Q-{{ $now.toFormat('yyMMdd') }}-<2 random digits>.\n4. Offer to draft the email to the client.\n\nRules:\n- Use only prices from the price list. If an item isn't there, say so and leave it out.\n- Never give a discount above an item's max discount %. If asked for more, say a manager must approve.\n- Double-check the arithmetic before showing totals."
        }
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Quote agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        300,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5",
          "mode": "list"
        },
        "options": {}
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Claude model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        -40,
        220
      ]
    },
    {
      "parameters": {
        "contextWindowLength": 10
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        130,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Returns our price list: code, name, unit, unit price, min quantity, max discount %.",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Price list",
          "mode": "name"
        },
        "options": {}
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Price list",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        300,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Saves a confirmed quote.",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Quotes",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "date": "={{ $now.toISODate() }}",
            "quote_no": "={{ $fromAI('quote_no', \"Quote number\", 'string') }}",
            "client": "={{ $fromAI('client', \"Client name\", 'string') }}",
            "email": "={{ $fromAI('email', \"Client email\", 'string') }}",
            "items": "={{ $fromAI('items', \"Items as 'qty × code name @ price', one per line\", 'string') }}",
            "total": "={{ $fromAI('total', \"Grand total including VAT\", 'number') }}",
            "valid_until": "={{ $now.plus({ days: 14 }).toISODate() }}",
            "status": "sent"
          }
        },
        "options": {}
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Save quote",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        470,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Creates a Gmail draft to the client with the quote (never sends).",
        "resource": "draft",
        "operation": "create",
        "subject": "={{ $fromAI('subject', \"Email subject\", 'string') }}",
        "message": "={{ $fromAI('body', \"Email body with the quote table, total, validity (14 days) and next steps\", 'string') }}",
        "options": {
          "sendTo": "={{ $fromAI('to', \"The recipient's email address\", 'string') }}"
        }
      },
      "id": "425c281e-a6bd-4ffe-aab5-92854b17b971",
      "name": "Draft email",
      "type": "n8n-nodes-base.gmailTool",
      "typeVersion": 2.1,
      "position": [
        640,
        220
      ]
    }
  ],
  "connections": {
    "Quote chat": {
      "main": [
        [
          {
            "node": "Quote agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude model": {
      "ai_languageModel": [
        [
          {
            "node": "Quote agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Memory": {
      "ai_memory": [
        [
          {
            "node": "Quote agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Price list": {
      "ai_tool": [
        [
          {
            "node": "Quote agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Save quote": {
      "ai_tool": [
        [
          {
            "node": "Quote agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Draft email": {
      "ai_tool": [
        [
          {
            "node": "Quote agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • Add a max_discount column to the price list, so the agent knows your limits.

Related flows