Skip to content
Learn Agentic AI11 min read0 views

AI Agent for Accounting Firms: Client Document Collection and Tax Season Management

Build an AI agent that automates document collection for accounting firms, tracks tax filing deadlines, manages client portal access, and provides real-time status updates during the hectic tax season.

Tax Season Is a Document Collection Crisis

Every January, accounting firms begin the same stressful cycle: chasing clients for W-2s, 1099s, mortgage interest statements, and dozens of other documents. Staff spend hours making phone calls and sending emails that say "we still need your..." The documents trickle in over weeks, creating bottlenecks that push everything toward the April deadline. An AI agent transforms this reactive document chase into a proactive, automated workflow.

This tutorial builds an agent that tracks which documents each client needs, sends reminders, processes submissions, and keeps clients informed about their filing status.

Tax Client Data Model

Accounting firms need to track each client's filing type, required documents, submission status, and deadlines. The data model captures these relationships.

from dataclasses import dataclass, field
from datetime import datetime, date, timedelta
from enum import Enum
from typing import Optional


class FilingType(Enum):
    INDIVIDUAL_1040 = "1040"
    BUSINESS_1120 = "1120"        # C-Corp
    PARTNERSHIP_1065 = "1065"
    S_CORP_1120S = "1120S"
    TRUST_1041 = "1041"


class DocumentStatus(Enum):
    NEEDED = "needed"
    REQUESTED = "requested"
    RECEIVED = "received"
    REVIEWED = "reviewed"
    ISSUE = "issue"            # document has a problem


class FilingStatus(Enum):
    NOT_STARTED = "not_started"
    GATHERING_DOCS = "gathering_docs"
    IN_PREPARATION = "in_preparation"
    REVIEW = "review"
    READY_TO_FILE = "ready_to_file"
    FILED = "filed"
    EXTENDED = "extended"


@dataclass
class RequiredDocument:
    name: str
    description: str
    status: DocumentStatus = DocumentStatus.NEEDED
    received_date: Optional[date] = None
    issue_note: str = ""


@dataclass
class TaxClient:
    id: str
    name: str
    phone: str
    email: str
    filing_type: FilingType
    filing_status: FilingStatus = FilingStatus.GATHERING_DOCS
    documents: list[RequiredDocument] = field(default_factory=list)
    deadline: date = date(2026, 4, 15)
    assigned_preparer: str = ""
    notes: str = ""
    last_contact: Optional[date] = None

Document Requirements by Filing Type

Different filing types require different sets of documents. We define these requirements so the agent knows exactly what to request from each client.

See AI Voice Agents Handle Real Calls

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

DOCUMENT_REQUIREMENTS = {
    FilingType.INDIVIDUAL_1040: [
        RequiredDocument("W-2", "Wage and income statements from all employers"),
        RequiredDocument("1099-INT", "Interest income from banks and investments"),
        RequiredDocument("1099-DIV", "Dividend income statements"),
        RequiredDocument("1099-NEC", "Non-employee compensation (freelance income)"),
        RequiredDocument("1098", "Mortgage interest statement"),
        RequiredDocument("Property Tax Bills", "Annual property tax statements"),
        RequiredDocument("Charitable Donations", "Receipts for charitable contributions over $250"),
        RequiredDocument("Health Insurance (1095)", "Health insurance coverage form"),
        RequiredDocument("Prior Year Return", "Last year's tax return for reference"),
    ],
    FilingType.BUSINESS_1120: [
        RequiredDocument("Income Statement", "Profit and loss statement for the fiscal year"),
        RequiredDocument("Balance Sheet", "Year-end balance sheet"),
        RequiredDocument("Bank Statements", "All business bank statements (12 months)"),
        RequiredDocument("Payroll Reports", "Annual payroll summary and W-3"),
        RequiredDocument("Depreciation Schedule", "Fixed asset and depreciation details"),
        RequiredDocument("Accounts Receivable", "Outstanding receivables aging report"),
        RequiredDocument("Accounts Payable", "Outstanding payables aging report"),
        RequiredDocument("Loan Documents", "Business loan statements and interest paid"),
    ],
}


def create_client_checklist(filing_type: FilingType) -> list[RequiredDocument]:
    """Create a fresh document checklist for a client based on filing type."""
    templates = DOCUMENT_REQUIREMENTS.get(filing_type, [])
    return [
        RequiredDocument(doc.name, doc.description)
        for doc in templates
    ]

Deadline Tracking

Tax season has firm deadlines with serious consequences for missing them. The agent must track deadlines and prioritize accordingly.

TAX_DEADLINES = {
    FilingType.PARTNERSHIP_1065: date(2026, 3, 16),
    FilingType.S_CORP_1120S: date(2026, 3, 16),
    FilingType.INDIVIDUAL_1040: date(2026, 4, 15),
    FilingType.BUSINESS_1120: date(2026, 4, 15),
    FilingType.TRUST_1041: date(2026, 4, 15),
}


def get_deadline_status(client: TaxClient) -> dict:
    today = date.today()
    days_left = (client.deadline - today).days
    docs_needed = sum(
        1 for d in client.documents
        if d.status in (DocumentStatus.NEEDED, DocumentStatus.REQUESTED)
    )
    docs_total = len(client.documents)
    docs_received = docs_total - docs_needed

    if days_left < 0:
        urgency = "OVERDUE"
    elif days_left <= 7:
        urgency = "CRITICAL"
    elif days_left <= 30:
        urgency = "APPROACHING"
    else:
        urgency = "ON_TRACK"

    return {
        "client": client.name,
        "deadline": client.deadline.isoformat(),
        "days_left": max(days_left, 0),
        "urgency": urgency,
        "documents_received": f"{docs_received}/{docs_total}",
        "filing_status": client.filing_status.value,
        "recommendation": (
            "File extension" if urgency in ("OVERDUE", "CRITICAL") and docs_needed > 3
            else "Prioritize outstanding documents" if urgency == "CRITICAL"
            else "On track"
        ),
    }

Agent Tools

from agents import Agent, Runner, function_tool

CLIENTS_DB = {
    "chen": TaxClient(
        "c1", "Robert Chen", "555-0301", "robert@email.com",
        FilingType.INDIVIDUAL_1040,
        documents=create_client_checklist(FilingType.INDIVIDUAL_1040),
        assigned_preparer="Amy Liu",
    ),
}

# Simulate some documents received
CLIENTS_DB["chen"].documents[0].status = DocumentStatus.RECEIVED  # W-2
CLIENTS_DB["chen"].documents[0].received_date = date(2026, 2, 1)
CLIENTS_DB["chen"].documents[8].status = DocumentStatus.RECEIVED  # Prior year


@function_tool
def check_client_status(client_name: str) -> str:
    """Check a client's document submission status and deadline."""
    key = client_name.lower().split()[-1]
    client = CLIENTS_DB.get(key)
    if not client:
        return f"Client '{client_name}' not found."
    status = get_deadline_status(client)
    outstanding = [
        d.name for d in client.documents
        if d.status in (DocumentStatus.NEEDED, DocumentStatus.REQUESTED)
    ]
    result = (
        f"Client: {status['client']}\n"
        f"Filing: {client.filing_type.value}\n"
        f"Deadline: {status['deadline']} ({status['days_left']} days left)\n"
        f"Documents: {status['documents_received']} received\n"
        f"Status: {status['urgency']}\n"
    )
    if outstanding:
        result += f"Still needed: {', '.join(outstanding)}\n"
    result += f"Recommendation: {status['recommendation']}"
    return result


@function_tool
def send_document_reminder(client_name: str, documents: str) -> str:
    """Send a reminder to a client about outstanding documents."""
    key = client_name.lower().split()[-1]
    client = CLIENTS_DB.get(key)
    if not client:
        return f"Client not found."
    doc_list = [d.strip() for d in documents.split(",")]
    client.last_contact = date.today()
    return (
        f"Reminder sent to {client.name} ({client.email})\n"
        f"Documents requested: {', '.join(doc_list)}\n"
        f"Message: 'Hi {client.name.split()[0]}, we still need the following "
        f"documents to complete your {client.filing_type.value} filing: "
        f"{', '.join(doc_list)}. Your deadline is {client.deadline.isoformat()}. "
        f"Please upload them to your client portal or email them to us.'"
    )


@function_tool
def mark_document_received(
    client_name: str, document_name: str
) -> str:
    """Mark a document as received for a client."""
    key = client_name.lower().split()[-1]
    client = CLIENTS_DB.get(key)
    if not client:
        return "Client not found."
    for doc in client.documents:
        if document_name.lower() in doc.name.lower():
            doc.status = DocumentStatus.RECEIVED
            doc.received_date = date.today()
            remaining = sum(
                1 for d in client.documents if d.status == DocumentStatus.NEEDED
            )
            return (
                f"Marked '{doc.name}' as received for {client.name}. "
                f"{remaining} documents still outstanding."
            )
    return f"Document '{document_name}' not found in {client.name}'s checklist."


@function_tool
def request_extension(client_name: str, reason: str) -> str:
    """File for a tax deadline extension for a client."""
    key = client_name.lower().split()[-1]
    client = CLIENTS_DB.get(key)
    if not client:
        return "Client not found."
    client.filing_status = FilingStatus.EXTENDED
    new_deadline = date(2026, 10, 15)
    client.deadline = new_deadline
    return (
        f"Extension request initiated for {client.name}.\n"
        f"New deadline: {new_deadline.isoformat()}\n"
        f"Reason: {reason}\n"
        f"Note: Extension extends time to file, not time to pay. "
        f"Estimated tax payments may still be due by the original deadline."
    )


accounting_agent = Agent(
    name="Tax Season Assistant",
    instructions="""You are a professional tax season assistant for an accounting firm.

1. When a client calls, use check_client_status to see their current
   document status and deadline urgency.
2. If documents are outstanding, explain which ones are still needed
   and why they are important.
3. Use send_document_reminder for clients who need follow-up.
4. When a client says they have submitted a document, use
   mark_document_received to update their record.
5. If the deadline is critical and documents are unlikely to arrive
   in time, discuss the option to request_extension.

IMPORTANT:
- Never provide specific tax advice. Direct tax questions to the
  assigned preparer.
- Be understanding about document collection — many clients find
  the process confusing.
- Emphasize the deadline and consequences of late filing.""",
    tools=[check_client_status, send_document_reminder, mark_document_received, request_extension],
)

FAQ

How do I integrate the document upload portal with the agent?

Set up a webhook from your client portal (such as TaxDome, Canopy, or a custom system) that fires when a document is uploaded. The webhook handler calls mark_document_received with the client name and document type. This keeps the agent's status view in sync with actual uploads without manual intervention.

Can the agent handle bulk reminders during crunch time?

Yes. Build a batch reminder function that queries all clients with outstanding documents and a deadline within 30 days, then sends personalized reminders for each. Run this as a weekly cron job during January through March, increasing to daily in April. The agent handles individual follow-ups, while the batch process handles proactive outreach at scale.

How does the extension decision work in practice?

The agent does not decide whether to file an extension — that is the preparer's judgment. The agent identifies clients at risk (critical urgency with many missing documents) and flags them for the preparer. If the preparer approves, the agent handles filing the extension request and communicating the new deadline to the client.


#Accounting #TaxSeason #DocumentCollection #ClientPortal #Python #AgenticAI #LearnAI #AIEngineering

Share this article
C

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.