Duration 2 hours
Day 3 of 5

Learning Objectives

By the end of this module, students will be able to:

  • Create DataMap functions for REST API integration
  • Use variable substitution in requests
  • Process API responses for voice output
  • Handle arrays with foreach expressions

Topics

1. What is DataMap? (20 min)

The Problem

Traditional function flow:

AI → Your Webhook → External API → Your Webhook → AI
         ↑                              ↑
    You write code              You write code

The DataMap Solution

AI → DataMap (No Code) → External API → DataMap → AI

DataMap handles API calls without custom webhook handlers!

When to Use DataMap

Use DataMap Use Custom Functions
Simple REST calls Complex business logic
Direct API mapping Multiple API calls
No transformation needed Data transformation
Quick integration Database operations

2. DataMap Structure (30 min)

Basic DataMap Definition

from signalwire_agents import AgentBase, DataMap, SwaigFunctionResult

agent = AgentBase(name="weather-agent")

# Create DataMap using builder pattern
weather_map = (
    DataMap("get_weather")
    .description("Get current weather for a city")
    .parameter("city", "string", "City name", required=True)
    .webhook("GET", "https://api.weather.com/current?q=${enc:args.city}&appid=API_KEY")
    .output(SwaigFunctionResult(
        "The weather in ${args.city} is ${response.main.temp} degrees "
        "with ${response.weather[0].description}."
    ))
)

# Register with agent
agent.register_swaig_function(weather_map.to_swaig_function())

DataMap Components

Method Purpose
DataMap("name") Function name AI will call
.description() When AI should use it
.parameter() Add input parameter
.webhook() API call configuration
.output() Response mapping with SwaigFunctionResult
.fallback_output() Response when API fails
.to_swaig_function() Convert to registerable function

3. Variable Substitution (25 min)

Available Variables

Pattern Source
${args.name} Function parameters
${env.VAR} Environment variables
${response.field} API response data
${meta.call_id} Call metadata

Parameter Substitution

from signalwire_agents import DataMap, SwaigFunctionResult

# Use ${enc:args.param} for URL-encoded values in URLs
data_map = (
    DataMap("search")
    .description("Search for data")
    .parameter("id", "string", "Record ID", required=True)
    .parameter("search_term", "string", "Search query", required=True)
    .webhook(
        "GET",
        "https://api.example.com/data/${enc:args.id}?query=${enc:args.search_term}",
        headers={"Authorization": "Bearer ${global_data.api_token}"}
    )
    .output(SwaigFunctionResult("Found: ${response.title}"))
)

Response Substitution

order_map = (
    DataMap("get_order")
    .description("Get order status")
    .parameter("order_id", "string", "Order ID", required=True)
    .webhook("GET", "https://api.example.com/orders/${enc:args.order_id}")
    .output(SwaigFunctionResult(
        "Order ${response.order_id} is ${response.status}. "
        "Shipped via ${response.carrier}."
    ))
)

Nested Response Access

# API returns: {"data": {"user": {"name": "John"}}}
user_map = (
    DataMap("get_user")
    .description("Get user info")
    .parameter("user_id", "string", "User ID", required=True)
    .webhook("GET", "https://api.example.com/users/${enc:args.user_id}")
    .output(SwaigFunctionResult("Hello, ${response.data.user.name}!"))
)

4. Array Processing (25 min)

The foreach Expression

Process arrays in responses using the .foreach() method:

from signalwire_agents import DataMap, SwaigFunctionResult

# API returns: {"orders": [{"id": "1", "status": "shipped"}, {"id": "2", "status": "pending"}]}
order_map = (
    DataMap("get_orders")
    .description("Get customer orders")
    .parameter("customer_id", "string", "Customer ID", required=True)
    .webhook("GET", "https://api.example.com/orders?customer_id=${enc:args.customer_id}")
    .foreach({
        "input_key": "orders",
        "output_key": "order_list",
        "max": 5,
        "append": "Order ${this.id} is ${this.status}\n"
    })
    .output(SwaigFunctionResult("Your orders:\n${order_list}"))
)
# Output: "Your orders:\nOrder 1 is shipped\nOrder 2 is pending\n"

foreach Configuration

Key Description
input_key Path to array in response
output_key Variable name for built string
max Maximum items to process (optional)
append Template for each item (use ${this.field})

5. Error Handling (20 min)

Fallback Responses

Use .fallback_output() to handle API failures gracefully:

weather_map = (
    DataMap("get_weather")
    .description("Get current weather")
    .parameter("city", "string", "City name", required=True)
    .webhook("GET", "https://api.weather.com/current?q=${enc:args.city}")
    .output(SwaigFunctionResult("The weather is ${response.temp} degrees."))
    .fallback_output(SwaigFunctionResult(
        "I'm having trouble getting the weather right now. Please try again."
    ))
)

Multiple Webhooks with Fallback

Chain multiple webhooks - if the first fails, the second is tried:

search_map = (
    DataMap("search")
    .description("Search multiple sources")
    .parameter("query", "string", "Search query", required=True)
    # Primary API
    .webhook("GET", "https://api.primary.com/search?q=${enc:args.query}")
    .output(SwaigFunctionResult("Primary result: ${response.title}"))
    # Backup API (tried if primary fails)
    .webhook("GET", "https://api.backup.com/search?q=${enc:args.query}")
    .output(SwaigFunctionResult("Backup result: ${response.title}"))
    # Final fallback if all webhooks fail
    .fallback_output(SwaigFunctionResult("Sorry, search is currently unavailable."))
)

Complete Example

Click to reveal complete solution

Weather Lookup Agent

#!/usr/bin/env python3
"""Weather agent using DataMap."""

from signalwire_agents import AgentBase, DataMap, SwaigFunctionResult

agent = AgentBase(name="weather-agent")

agent.prompt_add_section(
    "Role",
    "You help users check the weather. Ask what city they want weather for."
)

agent.add_language("English", "en-US", "rime.spore")

# Weather DataMap using builder pattern
weather = (
    DataMap("get_weather")
    .description("Get current weather conditions for a city")
    .parameter("city", "string", "City name", required=True)
    .webhook(
        "GET",
        "https://api.openweathermap.org/data/2.5/weather"
        "?q=${enc:args.city}&appid=${global_data.api_key}&units=imperial"
    )
    .output(SwaigFunctionResult(
        "In ${response.name}, it's currently ${response.main.temp} degrees "
        "with ${response.weather[0].description}. "
        "Humidity is ${response.main.humidity} percent."
    ))
    .fallback_output(SwaigFunctionResult(
        "I couldn't get weather data for that city. Please check the city name."
    ))
)

# Register the DataMap as a SWAIG function
agent.register_swaig_function(weather.to_swaig_function())

if __name__ == "__main__":
    agent.run()

Key Takeaways

  1. DataMap = No-code API integration - Direct REST calls
  2. Variable substitution is powerful - Args, env, response
  3. foreach handles arrays - Process lists in responses
  4. Always set fallbacks - Handle API failures gracefully
  5. Keep it simple - Complex logic needs custom functions

Preparation for Lab 2.2

  • Get an API key (OpenWeather, or any REST API)
  • Review the API documentation
  • Identify response fields you want to use

Lab Preview

In Lab 2.2, you will:

  1. Create a weather lookup DataMap
  2. Add order status DataMap
  3. Handle array responses
  4. Implement error fallbacks

Back to top

SignalWire AI Agents Certification Program