Dialogflow Fulfillment and API Integration Complete Tutorial
- Fulfillment Basic Concepts
- When Do You Need Fulfillment
- How Webhooks Work
- ES vs CX Fulfillment Differences
- Creating Webhook Service
- Using Cloud Functions (Recommended)
- Using Node.js + Express
- Using Python Flask
- Deployment and HTTPS Setup
- Dialogflow API Calls
- DetectIntent API
- Service Account and Authentication Setup
- Example Code
- GitHub Example Projects
- Official Example Repository Introduction
- Community Recommended Projects
- Quick Deployment Template
- Advanced Integration
- Connecting to Database (Firestore / MySQL)
- Calling Third-Party APIs
- Async Processing and Long-Running Tasks
- Error Handling and Retry Mechanism
- Deployment and Operations
- Monitoring and Logging
- Performance Optimization
- Security Considerations
- Next Steps
- Need Help with Backend Integration?
- Need Professional Cloud Advice?
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:
- Dialogflow detects an Intent that needs Fulfillment
- Sends HTTP POST request to your configured Webhook URL
- Your Webhook processes the request, calls database or API
- Returns structured JSON to Dialogflow
- Dialogflow sends content to user
ES vs CX Fulfillment Differences
| Difference | ES | CX |
|---|---|---|
| Request format | v2 API format | v3 API format |
| Response format | fulfillmentText / messages | fulfillmentResponse |
| Context handling | outputContexts | sessionInfo.parameters |
| Trigger method | Intent-level enable | Page/Route-level enable |
| Official SDK | dialogflow-fulfillment | No 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
- Go to Google Cloud Console
- Select your project (same as Dialogflow Agent)
- Search for "Cloud Functions" and enter
- 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
- Click "Deploy"
- Wait for deployment to complete (about 1-2 minutes)
- Copy the trigger URL
Step 4: Configure in Dialogflow
- Enter Dialogflow Console
- Click "Fulfillment" on the left
- Enable "Webhook"
- Paste the Cloud Functions URL
- Click "Save"
Step 5: Enable Intent's Fulfillment
- Enter the Intent that needs Fulfillment
- Scroll to the bottom
- Expand "Fulfillment" section
- Check "Enable webhook call for this intent"
- 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
- Go to Google Cloud Console > IAM & Admin > Service Accounts
- Click "Create Service Account"
- Enter name (e.g., dialogflow-client)
- Grant role: "Dialogflow API Client"
- Create key (JSON format)
- 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
| Project | Language | Features |
|---|---|---|
| dialogflow-fulfillment-nodejs | Node.js | Official Fulfillment library |
| flask-dialogflow-webhook | Python | Flask Webhook template |
| dialogflow-angular | TypeScript | Angular 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:
- Go to Google Cloud Console > Cloud Functions
- Select your Function
- Click "Logs" tab
Setting Up Alerts:
- Go to Cloud Monitoring
- Create alert policy
- Set conditions: Error rate > 5%, latency > 2 seconds
- 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:
- Optimize Conversation Design: Dialogflow Intent and Context Complete Tutorial
- Learn CX Version: Dialogflow CX Tutorial: From Beginner to Advanced
- Integrate LINE Bot: Dialogflow LINE Bot Integration Tutorial
- 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
