Back to HomeDialogflow

Dialogflow Fulfillment and API Integration Complete Tutorial

16 min min read
#Dialogflow#Fulfillment#Webhook#API#Cloud Functions

Dialogflow Fulfillment and API Integration Complete Tutorial

Dialogflow Fulfillment and API Integration Complete Tutorial

Static responses can only make simple FAQ bots. To make a bot truly useful, you must connect to backend systems: check orders, check inventory, handle reservations.

Dialogflow uses Fulfillment (Webhook) to let your bot call external APIs for dynamic responses. This article teaches you from scratch how to develop Webhooks, deploy to Cloud Functions, and integrate third-party APIs.

If you're not yet familiar with Dialogflow basics, we recommend first reading the Dialogflow Complete Guide and Intent and Context Tutorial.


Fulfillment Basic Concepts

When Do You Need Fulfillment

Scenarios that don't need Fulfillment:

  • Answering fixed questions (business hours, address)
  • Pure chitchat conversations
  • Providing static information

Scenarios that need Fulfillment:

  • Querying real-time information (order status, inventory count)
  • Executing actions (creating orders, reservations)
  • Dynamic responses based on conditions
  • Connecting to external systems (CRM, ERP)

How Webhooks Work

User → Dialogflow → Webhook → Your Backend → Webhook → Dialogflow → User
  │          │          │          │          │          │          │
"Check order" Intent   Call API  Query DB   Return    Compose    "Your order..."
            matching                        result    response

Flow explanation:

  1. Dialogflow detects an Intent that needs Fulfillment
  2. Sends HTTP POST request to your configured Webhook URL
  3. Your Webhook processes the request, calls database or API
  4. Returns structured JSON to Dialogflow
  5. Dialogflow sends content to user

ES vs CX Fulfillment Differences

DifferenceESCX
Request formatv2 API formatv3 API format
Response formatfulfillmentText / messagesfulfillmentResponse
Context handlingoutputContextssessionInfo.parameters
Trigger methodIntent-level enablePage/Route-level enable
Official SDKdialogflow-fulfillmentNo official SDK

This article primarily uses ES as examples. For CX Webhook development, refer to Dialogflow CX Tutorial.


Creating Webhook Service

Using Cloud Functions (Recommended)

Google Cloud Functions is the simplest Webhook deployment method—serverless, auto-scaling.

Step 1: Create Cloud Function

  1. Go to Google Cloud Console
  2. Select your project (same as Dialogflow Agent)
  3. Search for "Cloud Functions" and enter
  4. Click "Create Function"

Basic settings:

  • Function name: dialogflow-webhook
  • Region: asia-east1 (Taiwan)
  • Trigger type: HTTPS
  • Authentication: Allow unauthenticated invocations

Step 2: Write Code

Select Runtime: Node.js 20

package.json:

{
  "name": "dialogflow-webhook",
  "version": "1.0.0",
  "dependencies": {
    "dialogflow-fulfillment": "^0.6.1"
  }
}

index.js:

const { WebhookClient } = require('dialogflow-fulfillment');

exports.dialogflowWebhook = (request, response) => {
  const agent = new WebhookClient({ request, response });

  console.log('Intent: ' + agent.intent);
  console.log('Parameters: ' + JSON.stringify(agent.parameters));

  function welcome(agent) {
    agent.add('Welcome to our service! How can I help you?');
  }

  function fallback(agent) {
    agent.add('Sorry, I don\'t quite understand what you mean.');
  }

  function checkOrder(agent) {
    const orderId = agent.parameters.order_id;
    // Call your order system here
    agent.add(`Order ${orderId} status is: Processing, expected to ship tomorrow.`);
  }

  const intentMap = new Map();
  intentMap.set('Default Welcome Intent', welcome);
  intentMap.set('Default Fallback Intent', fallback);
  intentMap.set('check_order', checkOrder);

  agent.handleRequest(intentMap);
};

Step 3: Deploy

  1. Click "Deploy"
  2. Wait for deployment to complete (about 1-2 minutes)
  3. Copy the trigger URL

Step 4: Configure in Dialogflow

  1. Enter Dialogflow Console
  2. Click "Fulfillment" on the left
  3. Enable "Webhook"
  4. Paste the Cloud Functions URL
  5. Click "Save"

Step 5: Enable Intent's Fulfillment

  1. Enter the Intent that needs Fulfillment
  2. Scroll to the bottom
  3. Expand "Fulfillment" section
  4. Check "Enable webhook call for this intent"
  5. Save

Using Node.js + Express

If you want to deploy on your own server:

const express = require('express');
const { WebhookClient } = require('dialogflow-fulfillment');

const app = express();
app.use(express.json());

app.post('/webhook', (req, res) => {
  const agent = new WebhookClient({ request: req, response: res });

  function handleIntent(agent) {
    agent.add('This is a response from custom server');
  }

  const intentMap = new Map();
  intentMap.set('my_intent', handleIntent);

  agent.handleRequest(intentMap);
});

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

Note: Self-hosted servers require:

  • HTTPS (can use ngrok for testing)
  • Publicly accessible URL
  • Handling scalability issues

Using Python Flask

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    req = request.get_json()

    intent_name = req['queryResult']['intent']['displayName']
    parameters = req['queryResult']['parameters']

    if intent_name == 'check_order':
        order_id = parameters.get('order_id')
        response_text = f'Order {order_id} status is: Processing'
    else:
        response_text = 'Message received'

    return jsonify({
        'fulfillmentText': response_text
    })

if __name__ == '__main__':
    app.run(port=5000)

Deployment and HTTPS Setup

Local testing with ngrok:

# Install ngrok
npm install -g ngrok

# Start local server
node server.js

# In another terminal
ngrok http 3000

# Copy the HTTPS URL from ngrok, paste into Dialogflow

Production deployment options:

  • Google Cloud Functions (Recommended)
  • Google Cloud Run
  • AWS Lambda
  • Heroku
  • Self-hosted server + SSL certificate

Dialogflow API Calls

Besides passively receiving Webhooks, you can also actively call Dialogflow API.

DetectIntent API

DetectIntent API lets you send messages to Dialogflow from your own application.

Use cases:

  • Custom chat interface
  • Integration into existing App
  • Batch testing conversations

Service Account and Authentication Setup

Step 1: Create Service Account

  1. Go to Google Cloud Console > IAM & Admin > Service Accounts
  2. Click "Create Service Account"
  3. Enter name (e.g., dialogflow-client)
  4. Grant role: "Dialogflow API Client"
  5. Create key (JSON format)
  6. Download and safely store the key file

Step 2: Set Environment Variable

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"

Example Code

Node.js:

const dialogflow = require('@google-cloud/dialogflow');
const uuid = require('uuid');

async function detectIntent(projectId, text) {
  const sessionId = uuid.v4();
  const sessionClient = new dialogflow.SessionsClient();
  const sessionPath = sessionClient.projectAgentSessionPath(projectId, sessionId);

  const request = {
    session: sessionPath,
    queryInput: {
      text: {
        text: text,
        languageCode: 'en-US',
      },
    },
  };

  const [response] = await sessionClient.detectIntent(request);
  const result = response.queryResult;

  console.log('Intent:', result.intent.displayName);
  console.log('Response:', result.fulfillmentText);

  return result;
}

// Usage example
detectIntent('your-project-id', 'I want to make a reservation');

Python:

from google.cloud import dialogflow_v2 as dialogflow
import uuid

def detect_intent(project_id, text):
    session_client = dialogflow.SessionsClient()
    session_id = str(uuid.uuid4())
    session = session_client.session_path(project_id, session_id)

    text_input = dialogflow.TextInput(text=text, language_code='en-US')
    query_input = dialogflow.QueryInput(text=text_input)

    response = session_client.detect_intent(
        request={'session': session, 'query_input': query_input}
    )

    result = response.query_result
    print(f'Intent: {result.intent.display_name}')
    print(f'Response: {result.fulfillment_text}')

    return result

# Usage example
detect_intent('your-project-id', 'I want to make a reservation')

GitHub Example Projects

Official Example Repository Introduction

Google provides official example code:

Dialogflow ES:

Dialogflow CX:

Community Recommended Projects

ProjectLanguageFeatures
dialogflow-fulfillment-nodejsNode.jsOfficial Fulfillment library
flask-dialogflow-webhookPythonFlask Webhook template
dialogflow-angularTypeScriptAngular integration example

Quick Deployment Template

One-click deploy to Cloud Functions:

# Clone example project
git clone https://github.com/your-repo/dialogflow-webhook-template.git
cd dialogflow-webhook-template

# Deploy
gcloud functions deploy dialogflow-webhook \
  --runtime nodejs20 \
  --trigger-http \
  --allow-unauthenticated \
  --region asia-east1

Advanced Integration

Connecting to Database (Firestore / MySQL)

Firestore Example:

const { Firestore } = require('@google-cloud/firestore');
const firestore = new Firestore();

async function checkOrder(agent) {
  const orderId = agent.parameters.order_id;

  const doc = await firestore.collection('orders').doc(orderId).get();

  if (!doc.exists) {
    agent.add('Cannot find this order, please confirm the order number is correct.');
    return;
  }

  const order = doc.data();
  agent.add(`Order ${orderId} status is: ${order.status}, expected delivery on ${order.delivery_date}.`);
}

MySQL Example:

const mysql = require('mysql2/promise');

async function checkInventory(agent) {
  const productName = agent.parameters.product;

  const connection = await mysql.createConnection({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
  });

  const [rows] = await connection.execute(
    'SELECT stock FROM products WHERE name = ?',
    [productName]
  );

  if (rows.length > 0) {
    agent.add(`${productName} current stock is ${rows[0].stock} units.`);
  } else {
    agent.add('Cannot find this product.');
  }

  await connection.end();
}

Calling Third-Party APIs

Weather API Example:

const axios = require('axios');

async function getWeather(agent) {
  const city = agent.parameters['geo-city'];

  try {
    const response = await axios.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}&units=metric`
    );

    const weather = response.data;
    agent.add(`Current weather in ${city}: ${weather.weather[0].description}, temperature ${weather.main.temp}°C.`);
  } catch (error) {
    agent.add('Sorry, unable to get weather information, please try again later.');
  }
}

Async Processing and Long-Running Tasks

Dialogflow Webhook has a 5-second timeout limit. If tasks take longer:

Method 1: Quick Response + Follow-up Notification

async function processLongTask(agent) {
  const taskId = generateTaskId();

  // Respond immediately
  agent.add(`Received your request, processing... We'll notify you when complete (Task ID: ${taskId})`);

  // Async processing (don't wait)
  processTaskInBackground(taskId);
}

async function processTaskInBackground(taskId) {
  // Execute long-running task
  const result = await someHeavyOperation();

  // Send notification after completion (via LINE Push, Email, etc.)
  await sendNotification(taskId, result);
}

Method 2: Using Cloud Tasks

Put tasks in a queue, processed by another service:

const { CloudTasksClient } = require('@google-cloud/tasks');

async function queueTask(agent) {
  const client = new CloudTasksClient();

  const task = {
    httpRequest: {
      httpMethod: 'POST',
      url: 'https://your-worker-service.com/process',
      body: Buffer.from(JSON.stringify({ data: agent.parameters })).toString('base64'),
    },
  };

  await client.createTask({ parent: queuePath, task });

  agent.add('Your request has been added to the processing queue, we\'ll notify you of results shortly.');
}

Error Handling and Retry Mechanism

async function robustApiCall(agent) {
  const maxRetries = 3;
  let lastError;

  for (let i = 0; i < maxRetries; i++) {
    try {
      const result = await callExternalApi();
      agent.add(`Query result: ${result}`);
      return;
    } catch (error) {
      lastError = error;
      console.error(`Attempt ${i + 1} failed:`, error.message);

      if (i < maxRetries - 1) {
        await sleep(1000 * (i + 1)); // Exponential backoff
      }
    }
  }

  // All retries failed
  console.error('All retries failed:', lastError);
  agent.add('System temporarily busy, please try again later. For urgent assistance, please call customer service.');
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Can't get integration working? Backend integration is the highest technical barrier, involving security, performance, and error handling. Book technical consultation to have experienced people help solve your integration challenges.


Deployment and Operations

Monitoring and Logging

Cloud Functions Logs:

  1. Go to Google Cloud Console > Cloud Functions
  2. Select your Function
  3. Click "Logs" tab

Setting Up Alerts:

  1. Go to Cloud Monitoring
  2. Create alert policy
  3. Set conditions: Error rate > 5%, latency > 2 seconds
  4. Set notification channels (Email, Slack)

Performance Optimization

1. Reduce Cold Start

// Initialize connections outside Function to avoid reconnecting every time
const firestore = new Firestore();

exports.webhook = async (req, res) => {
  // Use established connection
  const doc = await firestore.collection('orders').doc('123').get();
  // ...
};

2. Use Caching

const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300 }); // 5-minute cache

async function getData(key) {
  let data = cache.get(key);

  if (!data) {
    data = await fetchFromDatabase(key);
    cache.set(key, data);
  }

  return data;
}

3. Set Minimum Instances

Avoid cold start latency:

gcloud functions deploy webhook \
  --min-instances 1 \
  --max-instances 10

Security Considerations

1. Verify Request Source

Confirm request actually comes from Dialogflow:

// Check User-Agent
if (!req.headers['user-agent'].includes('Google-Dialogflow')) {
  return res.status(403).send('Forbidden');
}

2. Environment Variable Management

Don't write sensitive information in code:

// ❌ Bad
const apiKey = 'sk-xxxxx';

// ✓ Good
const apiKey = process.env.API_KEY;

3. Limit Access Scope

Service accounts should only have needed permissions, don't use Owner permissions.

If you need to integrate into mobile apps, refer to Dialogflow Mobile Development Integration Tutorial. For LINE Bot integration, refer to Dialogflow LINE Bot Integration Tutorial.

FAQ

Q1: Dialogflow ES vs Dialogflow CX — which is better? Which for new projects?

New projects should pick Dialogflow CX. (1) Dialogflow ES (Essentials, legacy) — generous free tier, simple setup, Intent-based design fits simple Q&A. Limitations: (A) no state machine concept; complex flows require context hacks; (B) each agent limited to 20 contexts / 10,000 intents; (C) no flow-level grouping; (D) Google's investment has shifted; new features prioritize CX. (2) Dialogflow CX (formerly Conversational Agents) — paid (from $0.007 per session), visual flow builder, native state machine, Flow / Page / Route concepts clear; fits complex conversations (appointments, bookings, financial consulting). Limitations: (A) steeper learning curve; (B) higher pricing ($500–5,000+/month possible); (C) overkill for some simple use cases. Recommendations: (A) FAQ bot / simple customer service → Dialogflow ES (free tier sufficient); (B) Multi-step flows (registration, booking, purchasing) → Dialogflow CX; (C) Enterprise production → Dialogflow CX (free tier has long-term maintenance risks); (D) Existing ES wanting migration → can coexist, but refactoring flow logic takes 2–4 weeks. 2025 trend: Google launched Conversational Agents (integrating Gemini + CX + Dialogflow), which may become the unified future platform.

Q2: Does Fulfillment have to use Webhook? Is Inline Editor's Cloud Functions mode enough?

Depends on scale and needs. (1) Inline Editor (Cloud Functions) — Dialogflow's built-in Node.js function editor; pros: no self-deployment, free tier fits small scale, simple setup, fits prototypes. Limitations: (A) Node.js only (no Python); (B) limited package imports; (C) 500ms–2 second cold starts; (D) hard to integrate complex middleware; (E) difficult unit testing. (2) Webhook (self-hosted server / Cloud Run / Lambda) — pros: (A) any language (Python, Go, Java); (B) any library; (C) can pre-warm to eliminate cold starts; (D) complete CI/CD, monitoring, logs; (E) tight integration with existing systems. Cons: self-hosted operations, auth mechanism required. Selection principles: (A) Prototype / FAQ bot / small scale → Inline Editor; (B) Mid-to-large production, needs DB queries, third-party API integration → Webhook; (C) Finance / healthcare / sensitive data → must Webhook (data can't go to Google); (D) Python / Go development → Webhook. Practical: Cloud Run is the optimal Webhook choice — serverless, pay-per-use, simple IAM integration with Dialogflow in the same GCP project.

Q3: How to verify Webhook? How to confirm Dialogflow's request is genuine?

Three-layer verification. (1) JWT Token verification (officially recommended) — Dialogflow can be configured to include Bearer token in webhook calls, verified at webhook endpoint. (A) In Dialogflow Agent settings, configure webhook URL and auth header (like Authorization: Bearer YOUR_SECRET); (B) Webhook side check: if request.headers['Authorization'] != 'Bearer YOUR_SECRET': return 403; (C) Store token in Secret Manager, rotate every 90 days. (2) Request signature verification — Dialogflow CX supports webhook signatures; webhook verifies request hasn't been tampered using shared secret. (3) IP whitelisting (weaker but common) — Dialogflow requests come from specific Google IP ranges (check Google official docs); webhook accepts only these IPs. Common mistakes: (A) Not verifying webhook caller — anyone knowing your webhook URL can call it, risking abuse or leaks; (B) JWT secret hardcoded in code — must be in Secret Manager; (C) No payload schema validation — malicious payloads can crash webhook. Production checklist: (1) HTTPS only (no HTTP); (2) Authorization header verification; (3) Rate limiting (same session no more than 60 req/min); (4) Request/response logging (for debugging); (5) Cloud Armor / WAF in front to block DDoS.

Q4: For chatbots needing conversation history and cross-session memory, how to design?

Dialogflow itself doesn't retain conversation history (privacy design); you have to build it. Architectural patterns: (1) Session state (short-term memory) — Dialogflow's Context / Session Parameters are sufficient; auto-maintained during sessions; (2) Conversation history (long-term storage) — in each webhook call: (a) store user input, bot response, timestamp, user_id, session_id to DB (Firestore, PostgreSQL, MongoDB all work); (b) query history by user_id; (3) User profile (cross-session) — separate table for user data (preferences, past purchases, contact info); retrieve by user_id in each webhook call and feed to logic. Implementation example (Python + Firestore): def save_conversation(user_id, session_id, user_input, bot_response): doc_ref = db.collection('conversations').document(); doc_ref.set({'user_id': user_id, 'session_id': session_id, 'user_input': user_input, 'bot_response': bot_response, 'timestamp': firestore.SERVER_TIMESTAMP}). Privacy and compliance: (1) Storing conversation history requires disclosure (privacy policy); (2) GDPR requires providing "view and delete" functions; (3) Sensitive data (credit cards, passwords) shouldn't be stored — do redaction; (4) Retention period — 6–12 months typically sufficient; delete beyond. Advanced: enhance AI responses with conversation history — put last 5–10 rounds into prompt fed to LLM (Gemini, Claude) for context-aware natural replies — common 2025 practice.

Q5: How much does Dialogflow cost? What's the monthly cost for a chatbot with 500 DAU?

Dialogflow ES is free for many scenarios; CX needs careful calculation. (1) Dialogflow ES pricing — (A) Text: $0.002 per request (first 15,000/month free); (B) Voice input: $0.065/minute; (C) Voice output: $0.0065/character. 500 DAU × 10 rounds/day = 5,000 requests/day × 30 days = 150,000/month → first 15,000 free + paid 135,000 × $0.002 = $270/month. (2) Dialogflow CX pricing — (A) Text: $0.007 per request (no free tier); (B) Voice input: $0.06/minute; (C) Voice output: same as ES. Same 500 DAU × 10 rounds/day → 150,000 requests × $0.007 = $1,050/month. (3) Additional costs — (A) Vertex AI fulfillment (if using generative features) — $0.001–0.005 per LLM call; (B) Webhook operation costs (Cloud Run) — possibly $20–200/month; (C) Data storage (Firestore) — $20–100/month; (D) Cloud Logging — $10–50/month. Total TCO: ES 500 DAU ~$300–400/month, CX 500 DAU ~$1,200–1,500/month. Cost-saving tips: (1) Categorize intents — don't waste free tier quotas on low-value chit-chat; (2) Use entities not regex — saves training time; (3) Add cache to fulfillment — cache common queries (like "business hours") to avoid DB hits; (4) Monitor abused intents — if bots are being toyed with, add limits.


Next Steps

After mastering Fulfillment development, you can:

  1. Optimize Conversation Design: Dialogflow Intent and Context Complete Tutorial
  2. Learn CX Version: Dialogflow CX Tutorial: From Beginner to Advanced
  3. Integrate LINE Bot: Dialogflow LINE Bot Integration Tutorial
  4. Mobile App Integration: Dialogflow Mobile Development Integration Tutorial

Need Help with Backend Integration?

Good backend architecture makes systems stable, secure, and easy to extend. Poor architecture causes problems when traffic increases or becomes security vulnerabilities.

If you need:

  • Design scalable Webhook architecture
  • Integrate complex enterprise systems
  • Handle high-traffic scenarios
  • Ensure security compliance

Book architecture consultation to have experienced engineers help design robust backend architecture.

Consultation is completely free, we'll respond within 24 hours.


Need Professional Cloud Advice?

Whether you're evaluating cloud platforms, optimizing existing architecture, or looking for cost-saving solutions, we can help

Book Free Consultation

Related Articles