CSAT Survey
This example shows how to use Guava to call customers after a support interaction and collect a brief satisfaction survey — NPS score, resolution status, and open feedback. The results are written to your logs and posted back into Amazon Connect as a task linked to the original support contact, so the survey responses live alongside the conversation they're about.
The Guava agent (Jamie) can:
- Reach the right customer using
reach_person(handles voicemail and the gatekeeper problem) - Politely decline if the customer doesn't want to participate, without forcing them through the survey
- Collect an NPS score, resolution status, and open-ended feedback in a single conversational task
- Create an Amazon Connect task linked to the original
ContactIdviaRelatedContactId, so the survey is reportable in Connect's contact search and historical metrics
How It Works
Prerequisites
- Python 3.10 or later
- A Guava account with an API key and a phone number — sign up at app.goguava.ai
- An AWS account with an Amazon Connect instance
- The ContactId of the original support call you want to survey about (Amazon Connect surfaces this in the Contact Control Panel and contact search)
Step 1: Install Guava
Choose whichever package manager you prefer:
pip install guava-sdk # Install using pip
uv add guava-sdk # Install using uv
poetry add guava-sdk # Install using poetryYou also need boto3 for the Amazon Connect API:
pip install boto3Step 2: Set Up Amazon Connect
If you already have a Connect instance configured, skip to the parts you haven't done yet.
2a. Create or locate your Connect instance
- Open the Amazon Connect console.
- Create an instance if you don't have one, or select an existing one.
- Note the Instance ID — it's the UUID at the end of the instance ARN:
arn:aws:connect:us-east-1:123456789012:instance/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx └─────────────── Instance ID ──────┘
2b. Create a contact flow for survey tasks
Amazon Connect tasks need a dedicated contact flow — the default queue flow does not support tasks.
- In your Connect instance, go to Routing → Contact flows → Create contact flow.
- Name it something like
Guava CSAT Task Flow. - Add a Set working queue block and point it at the queue you'd like surveys to be reviewed in (a CX or QA queue is a natural fit).
- Add a Transfer to queue block, then a Disconnect block.
- Save and publish the flow.
- Open the flow, click Show additional flow information, and copy the Contact flow ID (the UUID at the end of the ARN).
Tip: If you only want surveys to land somewhere queryable rather than being worked by a live agent, point the flow at a low-priority "review" queue — the task will still appear in Connect's historical reports and contact search.
Step 3: Configure AWS Credentials
The example uses boto3, which reads credentials from the standard AWS credential chain. The easiest way for local development:
export AWS_ACCESS_KEY_ID="your-access-key-id"
export AWS_SECRET_ACCESS_KEY="your-secret-access-key"
export AWS_DEFAULT_REGION="us-east-1" # Match your Connect instance regionA minimal IAM policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["connect:StartTaskContact"],
"Resource": "arn:aws:connect:*:*:instance/YOUR_INSTANCE_ID/*"
}
]
}Step 4: Set Environment Variables
# Guava
export GUAVA_API_KEY="your-guava-api-key"
export GUAVA_AGENT_NUMBER="+15551000000" # Your Guava phone number (Jamie's number)
# Amazon Connect
export CONNECT_INSTANCE_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export CONNECT_CONTACT_FLOW_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Survey task flow from Step 2bStep 5: Run the Example
python -m examples.integrations.ccaas.amazon_connect.csat_survey \
+15559876543 \
--name "Jane Doe" \
--contact-id "11111111-2222-3333-4444-555555555555"Replace +15559876543 with the customer's phone number, set --name to their full name, and set --contact-id to the Amazon Connect ContactId of the original support call you're following up on.
What to expect:
- Guava calls the customer and verifies they're the right person
- Jamie asks if they have a minute for a quick survey
- If they decline, Jamie thanks them and ends the call gracefully — no pressure
- If they agree, Jamie collects an NPS score, asks whether the issue was resolved, and invites open-ended feedback
- The full survey result is printed as JSON to stdout and a Connect task is created linked to the original support contact via
RelatedContactId— so the response shows up in contact search and historical reports tied to the conversation it's about - If no one picks up, Guava ends the call politely
How the Code Works
Defining the agent and starting the call
agent = Agent(
name="Jamie",
organization="Pinnacle Gear Co.",
purpose=(
"to conduct a brief customer satisfaction survey about a recent "
"support experience"
),
)
agent.outbound_phone(
from_number=os.environ["GUAVA_AGENT_NUMBER"],
to_number=args.phone,
variables={
"customer_name": args.name,
"original_contact_id": args.contact_id,
},
).run()Agent is the top-level handle for the AI persona. outbound_phone places the call and binds the agent to that single conversation. The variables dict is passed into the call and made available to all handlers via call.get_variable(key) — here we pass the customer's name (so Jamie can address them) and the original Connect ContactId (so the resulting survey task can be linked back to the support call it's about).
Reaching the right person
@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.reach_person(contact_full_name=call.get_variable("customer_name"))
@agent.on_reach_person
def on_reach_person(call: guava.Call, outcome: str) -> None:
if outcome == "unavailable":
call.hangup("We were unable to reach the customer. End the call politely.")
return
# otherwise, set up the survey taskreach_person handles the gatekeeper problem — Jamie confirms she's speaking with the intended customer before starting the survey. If a family member or colleague answers, she politely asks for the right person. If the customer truly can't be reached, outcome == "unavailable" and we end the call without leaving a long voicemail (post-hoc surveys are easy to skip; we don't push them).
Asking permission, then collecting the survey
call.set_task(
"survey",
objective=(
"Conduct a short, friendly customer satisfaction survey about the customer's "
"recent support call with Pinnacle Gear. Keep it conversational and brief."
),
checklist=[
guava.Say(f"Hi {customer_name}, this is Jamie from Pinnacle Gear. ..."),
guava.Field(key="willing_to_participate", ..., required=True),
guava.Field(key="nps_score", ..., field_type="integer", required=False),
guava.Field(key="issue_resolved", ..., required=False),
guava.Field(key="feedback", ..., required=False),
],
)set_task gives Jamie a named objective and an ordered checklist. The first Field — willing_to_participate — is the gate. The remaining fields are marked required=False and the descriptions explicitly say "Only ask this if they agreed to participate" — so if the customer declines, Jamie doesn't push them through the rest of the survey. The agent reads the prompts as guidance and adapts its phrasing rather than reciting them verbatim, so the conversation stays natural even though the schema is structured.
Honoring the customer's choice
participation_recognizer = IntentRecognizer(["willing to participate", "not willing to participate"])
@agent.on_task_complete("survey")
def save_survey(call: guava.Call) -> None:
willing = call.get_field("willing_to_participate", "")
if participation_recognizer.classify(willing) == "not willing to participate":
call.hangup(
"Respect their time and thank them for taking the call. "
"Wish them a great day."
)
return
# otherwise, save the surveyWhen the survey task completes, the @agent.on_task_complete("survey") handler runs. Customers respond with all kinds of phrasing — "sure", "now's not a great time", "I'm driving", "yeah I guess so" — so we use IntentRecognizer to map the free-text answer to a clean willing / not willing decision. If they declined, we end the call with a thank-you. The recognizer is instantiated once at module level and reused across calls.
Deriving the NPS category
try:
nps_int = int(nps_raw) if nps_raw else 0
except (ValueError, TypeError):
nps_int = 0
if nps_int >= 9:
nps_category = "promoter"
elif nps_int >= 7:
nps_category = "passive"
else:
nps_category = "detractor"NPS uses three buckets — promoters (9–10), passives (7–8), and detractors (0–6). We compute the category server-side rather than asking the agent to do it, so the categorization is consistent across every call and shows up in the Connect task title and attributes in a form you can filter on.
Linking the survey back to the original contact
connect_client.start_task_contact(
InstanceId=os.environ["CONNECT_INSTANCE_ID"],
ContactFlowId=os.environ["CONNECT_CONTACT_FLOW_ID"],
Name=f"CSAT Survey — {customer_name} ({nps_category})"[:512],
Description=f"Customer: ...\nNPS Score: ...\nIssue Resolved: ...\nFeedback: ..."[:4096],
RelatedContactId=original_contact_id,
Attributes={
"customer_name": customer_name,
"nps_score": str(nps_raw or ""),
"nps_category": nps_category,
"issue_resolved": issue_resolved,
},
)The key field here is RelatedContactId — passing the original support call's ContactId tells Amazon Connect to link the new survey task to that conversation. In Connect's contact search, you can pivot from the support call to its survey task (and back) without joining tables yourself, and historical metrics that group by RelatedContactId will see them as a single customer interaction with a follow-up survey attached.
The Attributes dict is searchable in Connect's contact search — putting nps_score, nps_category, and issue_resolved here means CX leaders can filter and report on surveys directly inside the Connect console.
Customization Ideas
Trigger surveys automatically after every support call
Instead of running this script manually, hook it into your support flow: after a Connect contact ends, fire a Lambda (via EventBridge on Amazon Connect Contact Events) that pulls the customer's name and phone from your CRM and invokes this example with the original ContactId. Surveys go out within minutes of the call ending, while the experience is fresh.
Sample only a fraction of calls Calling every customer is overkill — most teams sample 10–20% of contacts. Add a random gate before invoking this script:
import random
if random.random() < 0.15:
run_csat_survey(...)Skip detractors of detractors If the customer was already escalated or marked dissatisfied during the original call (visible in Connect attributes on the original contact), skip the survey or route them to a manager call instead — surveying angry customers can make things worse.
Push results to a data warehouse
Replace the print(json.dumps(results, indent=2)) line with a write to your warehouse of choice (BigQuery, Snowflake, Redshift) or push to S3 in JSON Lines for downstream analytics — the Connect task gives you the operational view, the warehouse gives you the analytical view.
Customize the survey per product line
Pass a product_line variable into the call and conditionally include extra fields in the checklist — e.g., apparel customers get asked about fit and sizing, footwear customers get asked about comfort and durability. The same agent persona can run very different surveys depending on what the customer just bought.
Complete Example
import guava
import os
import logging
import json
import argparse
import boto3
from datetime import datetime
from guava import Agent, logging_utils
from guava.helpers.openai import IntentRecognizer
logger = logging.getLogger("csat_survey")
connect_client = boto3.client("connect")
participation_recognizer = IntentRecognizer(["willing to participate", "not willing to participate"])
agent = Agent(
name="Jamie",
organization="Pinnacle Gear Co.",
purpose=(
"to conduct a brief customer satisfaction survey about a recent "
"support experience"
),
)
@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.reach_person(contact_full_name=call.get_variable("customer_name"))
@agent.on_reach_person
def on_reach_person(call: guava.Call, outcome: str) -> None:
if outcome == "unavailable":
call.hangup("We were unable to reach the customer. End the call politely.")
return
customer_name = call.get_variable("customer_name")
call.set_task(
"survey",
objective=(
"Conduct a short, friendly customer satisfaction survey about the customer's "
"recent support call with Pinnacle Gear. Keep it conversational and brief."
),
checklist=[
guava.Say(
f"Hi {customer_name}, this is Jamie from Pinnacle Gear. "
"We noticed you recently contacted our support team, and I was hoping "
"to grab one minute of your time for a quick feedback survey. "
"Your input really helps us improve."
),
guava.Field(
key="willing_to_participate",
description=(
"Ask if they have about a minute to share some feedback. "
"Capture 'yes' or 'no'."
),
field_type="text",
required=True,
),
guava.Field(
key="nps_score",
description=(
"On a scale of 1 to 10, how likely are they to recommend Pinnacle Gear "
"support to a friend or colleague? Only ask this if they agreed to participate."
),
field_type="integer",
required=False,
),
guava.Field(
key="issue_resolved",
description=(
"Ask if their issue was fully resolved during the support call. "
"Capture 'yes', 'no', or 'partially'. Only ask if they agreed to participate."
),
field_type="text",
required=False,
),
guava.Field(
key="feedback",
description=(
"Ask if there's anything we could have done better, or any other "
"comments they'd like to share. Only ask if they agreed to participate."
),
field_type="text",
required=False,
),
],
)
@agent.on_task_complete("survey")
def save_survey(call: guava.Call) -> None:
customer_name = call.get_variable("customer_name")
original_contact_id = call.get_variable("original_contact_id")
willing = call.get_field("willing_to_participate", "")
if participation_recognizer.classify(willing) == "not willing to participate":
call.hangup(
"Respect their time and thank them for taking the call. "
"Wish them a great day."
)
return
nps_raw = call.get_field("nps_score")
issue_resolved = call.get_field("issue_resolved", "unknown")
feedback = call.get_field("feedback", "No additional feedback provided.")
# Derive NPS category from score.
try:
nps_int = int(nps_raw) if nps_raw else 0
except (ValueError, TypeError):
nps_int = 0
if nps_int >= 9:
nps_category = "promoter"
elif nps_int >= 7:
nps_category = "passive"
else:
nps_category = "detractor"
results = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"agent": "Jamie",
"organization": "Pinnacle Gear Co.",
"use_case": "post_call_csat_survey",
"original_contact_id": original_contact_id,
"fields": {
"customer_name": customer_name,
"nps_score": nps_raw,
"nps_category": nps_category,
"issue_resolved": issue_resolved,
"feedback": feedback,
},
}
print(json.dumps(results, indent=2))
logger.info("CSAT survey results captured — NPS: %s (%s).", nps_raw, nps_category)
# Create a Connect task linked to the original support call via RelatedContactId.
# This ties the survey results to the interaction for reporting without altering
# the original contact's attributes.
try:
connect_client.start_task_contact(
InstanceId=os.environ["CONNECT_INSTANCE_ID"],
ContactFlowId=os.environ["CONNECT_CONTACT_FLOW_ID"],
Name=f"CSAT Survey — {customer_name} ({nps_category})"[:512],
Description=(
f"Customer: {customer_name}\n"
f"NPS Score: {nps_raw}/10 ({nps_category})\n"
f"Issue Resolved: {issue_resolved}\n"
f"Feedback: {feedback}"
)[:4096],
RelatedContactId=original_contact_id,
References={
"source": {"Value": "guava_csat_survey", "Type": "STRING"},
},
Attributes={
"customer_name": customer_name,
"nps_score": str(nps_raw or ""),
"nps_category": nps_category,
"issue_resolved": issue_resolved,
},
)
logger.info("Amazon Connect CSAT task created and linked to contact %s.", original_contact_id)
except Exception as e:
logger.error("Failed to create Amazon Connect CSAT task: %s", e)
call.hangup(
"Thank the customer sincerely for their feedback and time. "
"Let them know their input helps the team improve. Wish them a great day."
)
if __name__ == "__main__":
logging_utils.configure_logging()
parser = argparse.ArgumentParser(
description=(
"Outbound post-call CSAT survey for Pinnacle Gear Co. "
"Collects NPS score and feedback after a support interaction, then creates "
"a linked Amazon Connect task tied to the original call for reporting."
)
)
parser.add_argument("phone", help="Customer phone number in E.164 format (e.g. +15551234567)")
parser.add_argument("--name", required=True, help="Full name of the customer")
parser.add_argument(
"--contact-id",
required=True,
help="Amazon Connect ContactId of the original support call to link the survey to",
)
args = parser.parse_args()
logger.info(
"Calling %s (%s) for CSAT survey — original contact: %s",
args.name,
args.phone,
args.contact_id,
)
agent.outbound_phone(
from_number=os.environ["GUAVA_AGENT_NUMBER"],
to_number=args.phone,
variables={
"customer_name": args.name,
"original_contact_id": args.contact_id,
},
).run()Questions? hi@goguava.ai
pip install guava-sdk # Install using pip
uv add guava-sdk # Install using uv
poetry add guava-sdk # Install using poetryexport AWS_ACCESS_KEY_ID="your-access-key-id"
export AWS_SECRET_ACCESS_KEY="your-secret-access-key"
export AWS_DEFAULT_REGION="us-east-1" # Match your Connect instance region{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["connect:StartTaskContact"],
"Resource": "arn:aws:connect:*:*:instance/YOUR_INSTANCE_ID/*"
}
]
}# Guava
export GUAVA_API_KEY="your-guava-api-key"
export GUAVA_AGENT_NUMBER="+15551000000" # Your Guava phone number (Jamie's number)
# Amazon Connect
export CONNECT_INSTANCE_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export CONNECT_CONTACT_FLOW_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Survey task flow from Step 2bpython -m examples.integrations.ccaas.amazon_connect.csat_survey \
+15559876543 \
--name "Jane Doe" \
--contact-id "11111111-2222-3333-4444-555555555555"agent = Agent(
name="Jamie",
organization="Pinnacle Gear Co.",
purpose=(
"to conduct a brief customer satisfaction survey about a recent "
"support experience"
),
)
agent.outbound_phone(
from_number=os.environ["GUAVA_AGENT_NUMBER"],
to_number=args.phone,
variables={
"customer_name": args.name,
"original_contact_id": args.contact_id,
},
).run()@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.reach_person(contact_full_name=call.get_variable("customer_name"))
@agent.on_reach_person
def on_reach_person(call: guava.Call, outcome: str) -> None:
if outcome == "unavailable":
call.hangup("We were unable to reach the customer. End the call politely.")
return
# otherwise, set up the survey taskcall.set_task(
"survey",
objective=(
"Conduct a short, friendly customer satisfaction survey about the customer's "
"recent support call with Pinnacle Gear. Keep it conversational and brief."
),
checklist=[
guava.Say(f"Hi {customer_name}, this is Jamie from Pinnacle Gear. ..."),
guava.Field(key="willing_to_participate", ..., required=True),
guava.Field(key="nps_score", ..., field_type="integer", required=False),
guava.Field(key="issue_resolved", ..., required=False),
guava.Field(key="feedback", ..., required=False),
],
)participation_recognizer = IntentRecognizer(["willing to participate", "not willing to participate"])
@agent.on_task_complete("survey")
def save_survey(call: guava.Call) -> None:
willing = call.get_field("willing_to_participate", "")
if participation_recognizer.classify(willing) == "not willing to participate":
call.hangup(
"Respect their time and thank them for taking the call. "
"Wish them a great day."
)
return
# otherwise, save the surveytry:
nps_int = int(nps_raw) if nps_raw else 0
except (ValueError, TypeError):
nps_int = 0
if nps_int >= 9:
nps_category = "promoter"
elif nps_int >= 7:
nps_category = "passive"
else:
nps_category = "detractor"connect_client.start_task_contact(
InstanceId=os.environ["CONNECT_INSTANCE_ID"],
ContactFlowId=os.environ["CONNECT_CONTACT_FLOW_ID"],
Name=f"CSAT Survey — {customer_name} ({nps_category})"[:512],
Description=f"Customer: ...\nNPS Score: ...\nIssue Resolved: ...\nFeedback: ..."[:4096],
RelatedContactId=original_contact_id,
Attributes={
"customer_name": customer_name,
"nps_score": str(nps_raw or ""),
"nps_category": nps_category,
"issue_resolved": issue_resolved,
},
)import random
if random.random() < 0.15:
run_csat_survey(...)import guava
import os
import logging
import json
import argparse
import boto3
from datetime import datetime
from guava import Agent, logging_utils
from guava.helpers.openai import IntentRecognizer
logger = logging.getLogger("csat_survey")
connect_client = boto3.client("connect")
participation_recognizer = IntentRecognizer(["willing to participate", "not willing to participate"])
agent = Agent(
name="Jamie",
organization="Pinnacle Gear Co.",
purpose=(
"to conduct a brief customer satisfaction survey about a recent "
"support experience"
),
)
@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.reach_person(contact_full_name=call.get_variable("customer_name"))
@agent.on_reach_person
def on_reach_person(call: guava.Call, outcome: str) -> None:
if outcome == "unavailable":
call.hangup("We were unable to reach the customer. End the call politely.")
return
customer_name = call.get_variable("customer_name")
call.set_task(
"survey",
objective=(
"Conduct a short, friendly customer satisfaction survey about the customer's "
"recent support call with Pinnacle Gear. Keep it conversational and brief."
),
checklist=[
guava.Say(
f"Hi {customer_name}, this is Jamie from Pinnacle Gear. "
"We noticed you recently contacted our support team, and I was hoping "
"to grab one minute of your time for a quick feedback survey. "
"Your input really helps us improve."
),
guava.Field(
key="willing_to_participate",
description=(
"Ask if they have about a minute to share some feedback. "
"Capture 'yes' or 'no'."
),
field_type="text",
required=True,
),
guava.Field(
key="nps_score",
description=(
"On a scale of 1 to 10, how likely are they to recommend Pinnacle Gear "
"support to a friend or colleague? Only ask this if they agreed to participate."
),
field_type="integer",
required=False,
),
guava.Field(
key="issue_resolved",
description=(
"Ask if their issue was fully resolved during the support call. "
"Capture 'yes', 'no', or 'partially'. Only ask if they agreed to participate."
),
field_type="text",
required=False,
),
guava.Field(
key="feedback",
description=(
"Ask if there's anything we could have done better, or any other "
"comments they'd like to share. Only ask if they agreed to participate."
),
field_type="text",
required=False,
),
],
)
@agent.on_task_complete("survey")
def save_survey(call: guava.Call) -> None:
customer_name = call.get_variable("customer_name")
original_contact_id = call.get_variable("original_contact_id")
willing = call.get_field("willing_to_participate", "")
if participation_recognizer.classify(willing) == "not willing to participate":
call.hangup(
"Respect their time and thank them for taking the call. "
"Wish them a great day."
)
return
nps_raw = call.get_field("nps_score")
issue_resolved = call.get_field("issue_resolved", "unknown")
feedback = call.get_field("feedback", "No additional feedback provided.")
# Derive NPS category from score.
try:
nps_int = int(nps_raw) if nps_raw else 0
except (ValueError, TypeError):
nps_int = 0
if nps_int >= 9:
nps_category = "promoter"
elif nps_int >= 7:
nps_category = "passive"
else:
nps_category = "detractor"
results = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"agent": "Jamie",
"organization": "Pinnacle Gear Co.",
"use_case": "post_call_csat_survey",
"original_contact_id": original_contact_id,
"fields": {
"customer_name": customer_name,
"nps_score": nps_raw,
"nps_category": nps_category,
"issue_resolved": issue_resolved,
"feedback": feedback,
},
}
print(json.dumps(results, indent=2))
logger.info("CSAT survey results captured — NPS: %s (%s).", nps_raw, nps_category)
# Create a Connect task linked to the original support call via RelatedContactId.
# This ties the survey results to the interaction for reporting without altering
# the original contact's attributes.
try:
connect_client.start_task_contact(
InstanceId=os.environ["CONNECT_INSTANCE_ID"],
ContactFlowId=os.environ["CONNECT_CONTACT_FLOW_ID"],
Name=f"CSAT Survey — {customer_name} ({nps_category})"[:512],
Description=(
f"Customer: {customer_name}\n"
f"NPS Score: {nps_raw}/10 ({nps_category})\n"
f"Issue Resolved: {issue_resolved}\n"
f"Feedback: {feedback}"
)[:4096],
RelatedContactId=original_contact_id,
References={
"source": {"Value": "guava_csat_survey", "Type": "STRING"},
},
Attributes={
"customer_name": customer_name,
"nps_score": str(nps_raw or ""),
"nps_category": nps_category,
"issue_resolved": issue_resolved,
},
)
logger.info("Amazon Connect CSAT task created and linked to contact %s.", original_contact_id)
except Exception as e:
logger.error("Failed to create Amazon Connect CSAT task: %s", e)
call.hangup(
"Thank the customer sincerely for their feedback and time. "
"Let them know their input helps the team improve. Wish them a great day."
)
if __name__ == "__main__":
logging_utils.configure_logging()
parser = argparse.ArgumentParser(
description=(
"Outbound post-call CSAT survey for Pinnacle Gear Co. "
"Collects NPS score and feedback after a support interaction, then creates "
"a linked Amazon Connect task tied to the original call for reporting."
)
)
parser.add_argument("phone", help="Customer phone number in E.164 format (e.g. +15551234567)")
parser.add_argument("--name", required=True, help="Full name of the customer")
parser.add_argument(
"--contact-id",
required=True,
help="Amazon Connect ContactId of the original support call to link the survey to",
)
args = parser.parse_args()
logger.info(
"Calling %s (%s) for CSAT survey — original contact: %s",
args.name,
args.phone,
args.contact_id,
)
agent.outbound_phone(
from_number=os.environ["GUAVA_AGENT_NUMBER"],
to_number=args.phone,
variables={
"customer_name": args.name,
"original_contact_id": args.contact_id,
},
).run()