By Sagar Shankaran, Founder of CallSphere
Learn how to build an AI agent that processes donations, generates tax-deductible receipts, sends personalized thank-you messages, and manages recurring giving programs for nonprofits.
Key takeaways
Nonprofits live and die by their donor relationships. A first-time donor who receives a generic confirmation email is far less likely to give again than one who receives a timely, personalized acknowledgment that connects their gift to a specific impact. Yet most small nonprofits lack the staff to deliver that level of engagement consistently.
An AI donor engagement agent can handle the full lifecycle: accepting donations through payment APIs, generating compliant tax receipts, crafting personalized thank-you messages based on donor history, and managing recurring giving schedules. The agent operates autonomously for routine transactions while escalating edge cases to human staff.
Before building the agent, define the data structures that represent donors and their contributions.
sequenceDiagram
autonumber
participant Caller as Caller
participant Agent as CallSphere Agent
participant API as CRM API
participant DB as CRM Database
participant Webhook as Webhook Listener
Caller->>Agent: Inbound call begins
Agent->>Agent: STT plus intent detection
Agent->>API: Lookup contact by phone
API->>DB: Read contact record
DB-->>API: Contact and history
API-->>Agent: Personalized context
Agent->>API: Create call activity
Agent->>API: Update deal stage
API->>Webhook: Outbound webhook fires
Webhook-->>Agent: Confirmed
Agent->>Caller: Spoken confirmation
from dataclasses import dataclass, field
from datetime import datetime, date
from enum import Enum
from typing import Optional
from uuid import uuid4
class DonationType(Enum):
ONE_TIME = "one_time"
RECURRING_MONTHLY = "recurring_monthly"
RECURRING_ANNUAL = "recurring_annual"
IN_KIND = "in_kind"
class DonationStatus(Enum):
PENDING = "pending"
COMPLETED = "completed"
FAILED = "failed"
REFUNDED = "refunded"
@dataclass
class Donor:
donor_id: str = field(default_factory=lambda: str(uuid4()))
first_name: str = ""
last_name: str = ""
email: str = ""
phone: Optional[str] = None
total_lifetime_giving: float = 0.0
first_gift_date: Optional[date] = None
last_gift_date: Optional[date] = None
gift_count: int = 0
is_recurring: bool = False
preferred_fund: Optional[str] = None
communication_preference: str = "email"
@dataclass
class Donation:
donation_id: str = field(default_factory=lambda: str(uuid4()))
donor_id: str = ""
amount: float = 0.0
donation_type: DonationType = DonationType.ONE_TIME
status: DonationStatus = DonationStatus.PENDING
fund_designation: str = "General Fund"
payment_method: str = "card"
transaction_ref: Optional[str] = None
receipt_number: Optional[str] = None
created_at: datetime = field(default_factory=datetime.utcnow)
is_tax_deductible: bool = True
The agent needs a tool that integrates with a payment processor. Here we use Stripe as an example, wrapping the API call so the agent can invoke it as a tool.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
import stripe
from agents import Agent, Runner, function_tool
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
@function_tool
async def process_donation(
donor_email: str,
amount_cents: int,
fund: str,
payment_method_id: str,
is_recurring: bool = False,
) -> dict:
"""Process a donation payment through Stripe."""
try:
if is_recurring:
customer = stripe.Customer.create(
email=donor_email,
payment_method=payment_method_id,
invoice_settings={
"default_payment_method": payment_method_id
},
)
subscription = stripe.Subscription.create(
customer=customer.id,
items=[{"price": os.environ["STRIPE_RECURRING_PRICE_ID"]}],
metadata={"fund": fund},
)
return {
"status": "active",
"subscription_id": subscription.id,
"donor_email": donor_email,
}
intent = stripe.PaymentIntent.create(
amount=amount_cents,
currency="usd",
payment_method=payment_method_id,
confirm=True,
receipt_email=donor_email,
metadata={"fund": fund},
)
return {
"status": intent.status,
"transaction_id": intent.id,
"amount": amount_cents / 100,
}
except stripe.error.StripeError as e:
return {"status": "failed", "error": str(e)}
Nonprofits must issue receipts that meet IRS requirements. The agent generates these automatically after a successful payment.
@function_tool
async def generate_receipt(
donor_name: str,
donor_email: str,
amount: float,
donation_date: str,
fund: str,
transaction_id: str,
org_ein: str = "12-3456789",
) -> dict:
"""Generate an IRS-compliant donation receipt."""
receipt_number = f"RCP-{datetime.utcnow().strftime('%Y%m%d')}-{transaction_id[:8]}"
receipt_text = (
f"OFFICIAL DONATION RECEIPT\n"
f"Receipt #: {receipt_number}\n"
f"Organization: Hope Community Foundation\n"
f"EIN: {org_ein}\n\n"
f"Donor: {donor_name}\n"
f"Date: {donation_date}\n"
f"Amount: ${amount:.2f}\n"
f"Designation: {fund}\n"
f"Transaction ID: {transaction_id}\n\n"
f"No goods or services were provided in exchange "
f"for this contribution. This receipt may be used "
f"for tax deduction purposes."
)
return {
"receipt_number": receipt_number,
"receipt_text": receipt_text,
"donor_email": donor_email,
}
The agent crafts thank-you messages that reference the donor's history and the specific impact of their gift.
@function_tool
async def lookup_donor_history(donor_email: str) -> dict:
"""Retrieve donor history for personalized messaging."""
# In production, query your donor database
return {
"first_name": "Sarah",
"total_lifetime_giving": 2500.00,
"gift_count": 8,
"first_gift_date": "2024-06-15",
"preferred_fund": "Youth Programs",
"is_recurring": True,
}
@function_tool
async def send_thank_you(
donor_email: str,
subject: str,
message_body: str,
) -> dict:
"""Send a personalized thank-you email to the donor."""
# In production, use SendGrid, SES, or similar
print(f"Sending to {donor_email}: {subject}")
return {"status": "sent", "recipient": donor_email}
Wire the tools together into a single agent with instructions that guide its behavior across the full donation lifecycle.
donor_agent = Agent(
name="Donor Engagement Agent",
instructions="""You are a donor engagement agent for Hope
Community Foundation. Your responsibilities:
1. Process donations via the payment tool
2. Generate IRS-compliant receipts for every gift
3. Look up donor history to personalize thank-you messages
4. For recurring donors, acknowledge their ongoing commitment
5. For first-time donors, welcome them warmly
6. Always confirm the fund designation before processing
7. If a payment fails, offer to retry or suggest alternatives
8. Never store or repeat full payment card details""",
tools=[
process_donation,
generate_receipt,
lookup_donor_history,
send_thank_you,
],
)
result = Runner.run_sync(
donor_agent,
"Sarah at sarah@example.com wants to donate $100 to Youth Programs "
"using payment method pm_card_visa. Please process the donation, "
"generate a receipt, and send a personalized thank-you.",
)
print(result.final_output)
The agent will call each tool in sequence: process the payment, generate the receipt, look up Sarah's history, then compose and send a thank-you that references her eight previous gifts and ongoing support.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
The agent checks the payment status returned by Stripe. If the status is failed, it informs the donor with a clear message and suggests retrying with a different payment method. The agent never stores card details itself — it works only with tokenized payment method IDs from Stripe.
Yes. You can add a separate tool for logging in-kind donations that skips the payment processing step but still generates a receipt. The receipt for in-kind gifts should describe the donated item rather than listing a dollar amount, since the IRS requires the donor to determine fair market value.
The receipt template includes the organization's EIN, a statement that no goods or services were exchanged, and the exact donation amount and date. For gifts over $250, the IRS requires a written acknowledgment, which this agent provides automatically. State-specific requirements can be added as conditional logic in the receipt generation tool.
#NonprofitAI #DonorManagement #PaymentIntegration #AgenticAI #Python #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
The 2026 desktop AI agent landscape — ServiceNow Project Arc, Anthropic Claude offerings, OpenAI agents, and Google Mariner. A buyer's map.
Step-by-step build of a working agent with the OpenAI Agents SDK — Agent class, tools, handoffs, tracing — plus an eval pipeline that catches regressions before merge.
An agentic-AI perspective on Anthropic Skills system, covering orchestration patterns, tool use, and how agent tooling fits production agent stacks.
Enterprise CIO Guide perspective on Comet's general-availability launch put an agentic browser in front of millions of consumers, and it works better than the demos suggested.
Enterprise CIO Guide perspective on Harvey AI's enterprise rollout numbers show legal agents have moved past the pilot stage at AmLaw 100 firms.
Enterprise CIO Guide perspective on Hippocratic AI's deployment numbers show healthcare voice agents are moving from pilot to production across major US health systems.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.