Back to HomeDialogflow

Dialogflow Fulfillment and API Integration Complete Tutorial

12 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.

Illustration: Fulfillment Architecture Diagram

Scene Description: An architecture diagram showing Fulfillment data flow. Left side is user and Dialogflow, middle is Webhook service (Cloud Functions), right side is backend systems (database, third-party API). Arrows indicate Request and Response directions and content.

Visual Focus:

  • Main content clearly presented

Required Elements:

  • Based on key elements in description

Chinese Text to Display: None

Color Tone: Professional, clear

Elements to Avoid: Abstract graphics, gears, glowing effects

Slug: dialogflow-fulfillment-architecture


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')

Illustration: DetectIntent API Flow Diagram

Scene Description: A flowchart showing the DetectIntent API call process. From left to right: Your application → API Request (with projectId, sessionId, text) → Dialogflow → API Response (with intent, fulfillmentText) → Your application.

Visual Focus:

  • Main content clearly presented

Required Elements:

  • Based on key elements in description

Chinese Text to Display: None

Color Tone: Professional, clear

Elements to Avoid: Abstract graphics, gears, glowing effects

Slug: dialogflow-detect-intent-api-flow


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));
}

Illustration: Advanced Integration Architecture Diagram

Scene Description: A complex architecture diagram showing advanced integration. Center is Cloud Functions, connecting to: Dialogflow on the left, database (Firestore, MySQL) on upper right, third-party APIs (weather, payment) on middle right, messaging services (LINE Push, Email) on lower right. Each connection indicates data flow direction.

Visual Focus:

  • Main content clearly presented

Required Elements:

  • Based on key elements in description

Chinese Text to Display: None

Color Tone: Professional, clear

Elements to Avoid: Abstract graphics, gears, glowing effects

Slug: dialogflow-advanced-integration-architecture


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.


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