Skip to content
Learn Agentic AI
Learn Agentic AI14 min read2 views

Building a Donor Engagement Agent: Donation Processing, Receipts, and Thank-You Messages

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.

Why Donor Engagement Needs an Agentic Approach

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.

Designing the Donor Data Model

Before building the agent, define the data structures that represent donors and their contributions.

flowchart TD
    START["Building a Donor Engagement Agent: Donation Proce…"] --> A
    A["Why Donor Engagement Needs an Agentic A…"]
    A --> B
    B["Designing the Donor Data Model"]
    B --> C
    C["Building the Payment Processing Tool"]
    C --> D
    D["Generating Tax-Deductible Receipts"]
    D --> E
    E["Personalizing Thank-You Messages"]
    E --> F
    F["Assembling the Donor Engagement Agent"]
    F --> G
    G["FAQ"]
    G --> DONE["Key Takeaways"]
    style START fill:#4f46e5,stroke:#4338ca,color:#fff
    style DONE fill:#059669,stroke:#047857,color:#fff
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

Building the Payment Processing Tool

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.

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

Generating Tax-Deductible Receipts

Nonprofits must issue receipts that meet IRS requirements. The agent generates these automatically after a successful payment.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

@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,
    }

Personalizing Thank-You Messages

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}

Assembling the Donor Engagement Agent

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 [email protected] 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.

FAQ

How does the agent handle failed payments?

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.

Can this agent handle in-kind donations?

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.

How do you ensure receipt compliance across states?

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

Share
C

Written by

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like

AI Interview Prep

7 AI Coding Interview Questions From Anthropic, Meta & OpenAI (2026 Edition)

Real AI coding interview questions from Anthropic, Meta, and OpenAI in 2026. Includes implementing attention from scratch, Anthropic's progressive coding screens, Meta's AI-assisted round, and vector search — with solution approaches.

Learn Agentic AI

Fine-Tuning LLMs for Agentic Tasks: When and How to Customize Foundation Models

When fine-tuning beats prompting for AI agents: dataset creation from agent traces, SFT and DPO training approaches, evaluation methodology, and cost-benefit analysis for agentic fine-tuning.

AI Interview Prep

7 Agentic AI & Multi-Agent System Interview Questions for 2026

Real agentic AI and multi-agent system interview questions from Anthropic, OpenAI, and Microsoft in 2026. Covers agent design patterns, memory systems, safety, orchestration frameworks, tool calling, and evaluation.

Learn Agentic AI

Building a Multi-Agent Data Pipeline: Ingestion, Transformation, and Analysis Agents

Build a three-agent data pipeline with ingestion, transformation, and analysis agents that process data from APIs, CSVs, and databases using Python.

Learn Agentic AI

Adaptive Thinking in Claude 4.6: How AI Agents Decide When and How Much to Reason

Technical exploration of adaptive thinking in Claude 4.6 — how the model dynamically adjusts reasoning depth, its impact on agent architectures, and practical implementation patterns.

Learn Agentic AI

How NVIDIA Vera CPU Solves the Agentic AI Bottleneck: Architecture Deep Dive

Technical analysis of NVIDIA's Vera CPU designed for agentic AI workloads — why the CPU is the bottleneck, how Vera's architecture addresses it, and what it means for agent performance.