24 min readDeterministic Automation

Implementation Guide: Automate FOIA Request Triage — Classify Requests, Retrieve Responsive Records, Apply Redactions & Generate Acknowledgment Letters

Step-by-step implementation guide for deploying AI to automate foia request triage — classify requests, retrieve responsive records, apply redactions & generate acknowledgment letters for Government & Defense clients.

Software Procurement

Microsoft Azure OpenAI Service (Azure Government)

Microsoft Azure OpenAI Service (Azure Government)

Microsoft Azure GovernmentGPT-5.4Qty: 100 requests/month (classification and acknowledgment); 50-page document package (exemption analysis)

GPT-5.4: ~$0.005/1K input, ~$0.015/1K output. Classification and acknowledgment for 100 requests/month: ~$20–$40/month. Exemption analysis for 50-page document package: ~$5–$15.

Consumption-based licensing. Required for all AI processing of FOIA requests and responsive records, which contain PII, Privacy Act-protected information, and potentially CUI. All processing must remain within the FedRAMP Moderate or High boundary depending on agency classification level.

FOIAonline (Federal Agency FOIA Portal)

FOIAonline (Federal Agency FOIA Portal)

EPA / GSA (shared service)Free for participating federal agencies

$0

Federal shared FOIA case management system used by multiple agencies. Provides APIs for programmatic case retrieval and status updates. If the client agency uses FOIAonline, it is the primary intake source for the automation pipeline. Other agencies use agency-specific FOIA portals (e.g., DOD FOIA, DHS FOIA) — identify the client's system before configuring the intake step.

GovDelivery / Granicus (FOIA Request Intake)

GovDelivery / Granicus (FOIA Request Intake)

GranicusSaaS per-agency annual

$10,000–$30,000/year

Public-facing FOIA request portal used by many state and local government agencies. Provides a structured intake form that feeds into the automation pipeline. Granicus is StateRAMP authorized, making it appropriate for state government deployments.

Microsoft SharePoint GCC High (Records Repository)

Microsoft SharePoint GCC High (Records Repository)

MicrosoftSharePoint GCC High

Included in M365 GCC High

Primary document repository searched during the records retrieval stage. The pipeline queries SharePoint via Microsoft Graph API (government endpoint) for documents responsive to each FOIA request based on AI-generated search terms.

Microsoft Purview (Records Management & Redaction)

Microsoft Purview (Records Management & Redaction)

MicrosoftM365 E5 GCC High / Purview Compliance Add-onQty: per user/month

$57/user/month (E5 GCC High) or Purview Compliance add-on ~$15/user/month

Included in M365 E5 GCC High or Purview add-on

Microsoft Purview provides sensitivity labels, content search across M365 GCC High, eDiscovery for records retrieval, and — via the Redact feature — AI-assisted redaction of sensitive content in documents before release. Content Search and eDiscovery are the core tools for records retrieval in the FOIA pipeline.

Adobe Acrobat Pro DC (Government — Manual Redaction)

Adobe Acrobat Pro DC (Government — Manual Redaction)

AdobePer-seat annual

$358/user/year (VIP government)

Required for FOIA officers to perform final manual redaction and apply redaction marks to PDF documents before release. AI-identified exemption candidates are reviewed and applied by the FOIA officer using Acrobat's redaction tools. The MSP configures the workflow; FOIA officers perform the final redactions.

Microsoft Power Automate (GCC High)

Microsoft Power Automate (GCC High)

MicrosoftGCC High

Included

Included in M365 GCC High

Orchestrates the end-to-end FOIA workflow: new request received → classification triggered → acknowledgment sent → records search initiated → exemption analysis routed → FOIA officer review queue → response letter generated → records released.

Prerequisites

  • FOIA officer involvement from day one: The FOIA officer (or Chief FOIA Officer for large agencies) must be a primary stakeholder in this deployment — not just an end user briefed at handoff. FOIA law and exemption determinations are the FOIA officer's legal responsibility. The MSP configures the technical workflow; the FOIA officer defines the exemption criteria, review requirements, and release authorization process.
  • Agency FOIA regulations review: Every federal agency has agency-specific FOIA regulations published in the Code of Federal Regulations (e.g., DoD at 32 CFR Part 286, EPA at 40 CFR Part 2). State agencies have equivalent public records statutes. Review the applicable regulations before configuring any exemption analysis prompts — exemptions differ by agency.
  • Records management system access: Identify all systems that hold agency records potentially responsive to FOIA requests: SharePoint, network file shares, email archives, legacy databases, case management systems. The pipeline's records retrieval is only as complete as the systems it can search. Document any systems NOT searchable by the pipeline and establish a manual search workflow for those systems.
  • Privacy Act system of records notices (SORNs): For agencies subject to the Privacy Act, identify all systems of records (SORNs published in the Federal Register). Records from Privacy Act systems require a Privacy Act review in addition to the FOIA exemption review. Configure the pipeline to flag records that originate from Privacy Act-covered systems.
  • Fee schedule and waiver criteria: Configure the fee determination module with the agency's current FOIA fee schedule (search, duplication, review fees by requester category). Fee schedules are published in agency FOIA regulations. Identify the agency's fee waiver criteria (public interest, news media, educational institution, commercial requesters, etc.).
  • DOJ FOIA guidelines compliance: Review the current DOJ FOIA guidelines (https://www.justice.gov/oip/doj-guidance-documents) for the most current agency guidance on exemption standards, presumption of openness, and foreseeable harm standard. Update exemption analysis prompts to reflect current DOJ guidance.
  • IT admin access: Azure Government subscription, M365 GCC High Global Admin (for Purview Content Search), SharePoint GCC High, Power Automate, FOIA portal API credentials.

Installation Steps

Step 1: Configure the FOIA Request Intake and Classification Pipeline

Build the intake pipeline that receives FOIA requests, classifies them by topic, complexity, and applicable exemptions, and routes them to the appropriate processing queue.

foia_intake_classifier.py
python
# Classifies incoming FOIA requests and routes to appropriate processing
# queue

# foia_intake_classifier.py
# Classifies incoming FOIA requests and routes to appropriate processing queue

from openai import AzureOpenAI
import os, json, datetime

client = AzureOpenAI(
    azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
    api_key=os.environ["AZURE_OPENAI_KEY"],
    api_version="2024-08-01-preview"
)

# FOIA exemption reference (customize per agency regulations)
FOIA_EXEMPTIONS = {
    "Exemption 1": "Classified national defense or foreign policy information",
    "Exemption 2": "Internal agency personnel rules and practices",
    "Exemption 3": "Information prohibited from disclosure by another statute",
    "Exemption 4": "Trade secrets and confidential commercial/financial information",
    "Exemption 5": "Inter/intra-agency communications protected by deliberative process, attorney-client, or attorney work product privilege",
    "Exemption 6": "Personal privacy information — clearly unwarranted invasion of privacy",
    "Exemption 7A": "Law enforcement — interfere with ongoing investigation",
    "Exemption 7B": "Law enforcement — deprive of fair trial",
    "Exemption 7C": "Law enforcement — personal privacy of individuals",
    "Exemption 7D": "Law enforcement — confidential sources",
    "Exemption 7E": "Law enforcement — investigative techniques and procedures",
    "Exemption 7F": "Law enforcement — endanger life or physical safety",
    "Exemption 8": "Financial institution examination information",
    "Exemption 9": "Geological and geophysical information"
}

# Request complexity tiers
COMPLEXITY_TIERS = {
    "Simple": "Single record or narrow date range; likely <50 pages; no likely exemptions",
    "Standard": "Moderate scope; multiple systems may need searching; 1-2 likely exemptions",
    "Complex": "Broad scope; cross-system search required; multiple exemptions likely; >500 pages expected",
    "Voluminous": "Exceptionally broad scope; likely thousands of pages; may require rolling production"
}

def classify_foia_request(request: dict) -> dict:
    """Classify a FOIA request by topic, complexity, and likely exemptions."""

    classification_prompt = f"""You are a FOIA processing specialist at a federal agency.
Classify the following FOIA request for routing and processing planning.

AGENCY: {request.get('agency', 'Federal Agency')}
REQUEST DATE: {request.get('date_received', datetime.date.today().isoformat())}
REQUESTER CATEGORY: {request.get('requester_category', 'Unknown')}
REQUEST TEXT: {request.get('request_text', '')}
ATTACHMENTS: {request.get('attachments_description', 'None')}

Provide classification as JSON:
{{
  "topic_classification": "[primary topic — e.g., Contracts/Procurement, Personnel, Enforcement, Environmental, Financial, Policy, Other]",
  "topic_subtopics": ["list of specific subjects mentioned"],
  "complexity_tier": "[Simple/Standard/Complex/Voluminous]",
  "complexity_rationale": "[why this tier was assigned]",
  "estimated_responsive_pages": "[range estimate, e.g., '10-50' or '500+']",
  "likely_exemptions": [
    {{
      "exemption": "[Exemption number and name]",
      "rationale": "[why this exemption may apply — based on request text]",
      "confidence": "[High/Medium/Low]"
    }}
  ],
  "privacy_act_implicated": true/false,
  "privacy_act_rationale": "[why Privacy Act may apply, or 'Not applicable']",
  "fee_category": "[Commercial/News Media/Educational/Other — based on requester info]",
  "fee_waiver_requested": true/false,
  "expedited_processing_requested": true/false,
  "expedited_processing_basis": "[stated basis or 'Not requested']",
  "systems_to_search": ["suggested systems/locations to search based on request topic"],
  "recommended_search_terms": ["5-10 specific search terms to use in records retrieval"],
  "special_handling_flags": ["any flags — e.g., SENSITIVE TOPIC, LITIGATION RISK, MEDIA REQUESTER, CONGRESSIONAL INTEREST"],
  "routing_queue": "[Simple/Standard/Complex/Voluminous/Expedited/Contested]",
  "estimated_processing_days": "[statutory estimate: Simple=20 days, Complex=may require extension]"
}}

IMPORTANT:
- Do not make final exemption determinations — identify candidates for FOIA officer review
- Flag [FOIA OFFICER JUDGMENT REQUIRED] for any complex exemption analysis
- Flag [LEGAL REVIEW] if litigation, congressional, or sensitive law enforcement topics appear
- All output is for internal FOIA office use only — not for release to requester"""

    response = client.chat.completions.create(
        model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
        messages=[
            {"role": "system", "content": "You are a federal FOIA processing specialist. Classify requests accurately and conservatively — when in doubt, assign higher complexity and flag for human review."},
            {"role": "user", "content": classification_prompt}
        ],
        temperature=0.0,
        max_tokens=2000,
        response_format={"type": "json_object"}
    )

    classification = json.loads(response.choices[0].message.content)
    classification["request_id"] = request.get("request_id", "UNKNOWN")
    classification["classification_timestamp"] = datetime.datetime.utcnow().isoformat()
    classification["classification_status"] = "AI-GENERATED — REQUIRES FOIA OFFICER REVIEW"

    return classification


def assign_tracking_number(agency_prefix: str) -> str:
    """Generate a unique FOIA tracking number."""
    year = datetime.date.today().year
    import random
    seq = random.randint(10000, 99999)  # In production, use a database sequence
    return f"{agency_prefix}-{year}-{seq:05d}"

Step 2: Build the Records Retrieval Pipeline

Configure the Purview Content Search pipeline that executes AI-generated search terms across M365 GCC High to identify potentially responsive records.

records_retrieval.py
python
# Searches M365 GCC High for records responsive to FOIA requests

# records_retrieval.py
# Searches M365 GCC High for records responsive to FOIA requests

import requests
import os, json

# Microsoft Graph API (Government endpoint)
GRAPH_ENDPOINT = "https://graph.microsoft.us/v1.0"

def get_graph_token() -> str:
    """Get access token for Microsoft Graph (Government)."""
    token_url = f"https://login.microsoftonline.us/{os.environ['AZURE_TENANT_ID']}/oauth2/v2.0/token"
    resp = requests.post(token_url, data={
        "client_id": os.environ["AZURE_CLIENT_ID"],
        "client_secret": os.environ["AZURE_CLIENT_SECRET"],
        "scope": "https://graph.microsoft.us/.default",
        "grant_type": "client_credentials"
    })
    resp.raise_for_status()
    return resp.json()["access_token"]


def create_compliance_search(
    request_id: str,
    search_terms: list,
    date_range: dict,
    custodians: list = None
) -> dict:
    """Create a Purview Content Search for FOIA records retrieval."""

    token = get_graph_token()
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

    # Build KQL search query from AI-generated terms
    kql_terms = " OR ".join([f'"{term}"' for term in search_terms])
    date_filter = ""
    if date_range.get("start") and date_range.get("end"):
        date_filter = f" AND (date>={date_range['start']} AND date<={date_range['end']})"

    kql_query = f"({kql_terms}){date_filter}"

    # Create compliance search via Graph API
    # Endpoint: /compliance/ediscovery/cases/{caseId}/searches
    search_payload = {
        "displayName": f"FOIA-{request_id}",
        "description": f"Records retrieval for FOIA request {request_id}",
        "contentQuery": kql_query,
        "dataSourceScopes": "allTenantMailboxes,allTenantSites",  # Search all mailboxes and SharePoint
        "includeAttachments": True,
        "runStatisticsQuery": True
    }

    # Note: Full eDiscovery case creation requires additional API calls
    # This is a simplified representation — full implementation uses
    # the Microsoft Purview Compliance Portal API or PowerShell

    print(f"KQL Query for FOIA {request_id}:")
    print(kql_query)
    print(f"Estimated search scope: All M365 GCC High mailboxes and SharePoint sites")

    return {
        "request_id": request_id,
        "kql_query": kql_query,
        "search_terms": search_terms,
        "date_range": date_range,
        "status": "SEARCH_CREATED — Run in Purview Compliance Portal to retrieve results"
    }


def generate_search_terms_from_request(
    request_text: str,
    classification: dict
) -> list:
    """Refine and expand AI-generated search terms for Purview search."""

    refine_prompt = f"""Generate optimized Microsoft Purview / eDiscovery search terms for this FOIA request.

REQUEST TEXT: {request_text[:2000]}
INITIAL SEARCH TERMS: {classification.get('recommended_search_terms', [])}
TOPIC: {classification.get('topic_classification', '')}
DATE RANGE (if specified in request): Extract from request text or 'Not specified'

Generate:
1. EXACT PHRASE TERMS: Specific phrases that should appear in responsive documents (in quotes)
2. KEYWORD TERMS: Individual keywords likely in responsive documents
3. CUSTODIAN SUGGESTIONS: Job titles or roles likely to hold responsive records
4. DATE RANGE: Extract any date range specified in the request
5. FILE TYPE FILTERS: Any specific document types requested (.pdf, .xlsx, emails, etc.)
6. EXCLUSIONS: Terms to exclude to reduce non-responsive results (use NOT operator)

Format as JSON:
{{
  "exact_phrases": ["phrase 1", "phrase 2"],
  "keywords": ["keyword 1", "keyword 2"],
  "boolean_query": "complete KQL/boolean query combining all terms",
  "custodians": ["job title 1", "job title 2"],
  "date_start": "YYYY-MM-DD or null",
  "date_end": "YYYY-MM-DD or null",
  "file_types": [".pdf", ".docx", "email"],
  "exclusions": ["term to exclude"]
}}"""

    response = client.chat.completions.create(
        model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
        messages=[{"role": "user", "content": refine_prompt}],
        temperature=0.0,
        max_tokens=1000,
        response_format={"type": "json_object"}
    )

    return json.loads(response.choices[0].message.content)

Step 3: Build the Exemption Analysis Decision Support Tool

Generate candidate exemption analysis for FOIA officer review — identifying which portions of responsive records may be exempt from disclosure.

exemption_analyzer.py
python
# Identifies candidate FOIA exemptions in responsive documents for FOIA
# officer review

# exemption_analyzer.py
# Identifies candidate FOIA exemptions in responsive documents for FOIA officer review

EXEMPTION_ANALYSIS_PROMPT = """You are a FOIA processing specialist providing decision support
to a FOIA officer. Review the following document excerpt and identify portions that MAY be
exempt from disclosure under FOIA.

IMPORTANT LEGAL CAVEAT:
- This is decision support ONLY — the FOIA officer makes all final exemption determinations
- Apply the DOJ's "foreseeable harm" standard: withhold only if foreseeable harm would result
- Apply the presumption of openness: when in doubt, lean toward disclosure
- Do not recommend withholding based on speculation — require articulable harm

AGENCY: {agency}
DOCUMENT TYPE: {doc_type}
DOCUMENT DATE: {doc_date}

FOIA EXEMPTIONS TO ANALYZE AGAINST:
{exemptions_text}

FOR EACH IDENTIFIED CANDIDATE EXEMPTION, PROVIDE:
1. LOCATION: Paragraph/sentence location in document
2. QUOTED TEXT: The exact text that may be exempt (max 100 words)
3. CANDIDATE EXEMPTION: Which exemption(s) may apply
4. RATIONALE: Why this exemption may apply — specific articulable harm
5. FORESEEABLE HARM: What harm would result from disclosure?
6. SEGREGABILITY: Can the exempt portion be redacted while releasing the rest?
7. CONFIDENCE: [High/Medium/Low] that a FOIA officer would apply this exemption
8. FOIA OFFICER ACTION: [REDACT CANDIDATE — Review / RELEASE — No exemption applicable / LEGAL REVIEW REQUIRED]

OUTPUT FORMAT: Structured list of candidate exemptions, then a segregability summary.

DOCUMENT EXCERPT:
{document_text}

REMINDER: Output flagged portions for FOIA officer review — do not make final determinations.
All output marked: [AI-ASSISTED ANALYSIS — FOIA OFFICER DETERMINATION REQUIRED]"""

def analyze_document_for_exemptions(
    document_text: str,
    document_metadata: dict,
    agency: str
) -> dict:
    """Analyze a document for candidate FOIA exemptions."""

    exemptions_formatted = "\n".join([
        f"{k}: {v}" for k, v in FOIA_EXEMPTIONS.items()
    ])

    response = client.chat.completions.create(
        model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
        messages=[
            {"role": "system", "content": "You are a federal FOIA compliance specialist. Identify candidate exemptions accurately and conservatively. The presumption is always toward disclosure."},
            {"role": "user", "content": EXEMPTION_ANALYSIS_PROMPT.format(
                agency=agency,
                doc_type=document_metadata.get("type", "Unknown"),
                doc_date=document_metadata.get("date", "Unknown"),
                exemptions_text=exemptions_formatted,
                document_text=document_text[:5000]
            )}
        ],
        temperature=0.0,
        max_tokens=3000
    )

    return {
        "document_id": document_metadata.get("id", "Unknown"),
        "exemption_analysis": response.choices[0].message.content,
        "analysis_timestamp": datetime.datetime.utcnow().isoformat(),
        "status": "PENDING FOIA OFFICER REVIEW",
        "legal_disclaimer": "AI-generated candidate analysis only. FOIA officer must make all final withholding determinations."
    }

Step 4: Generate Acknowledgment Letters and Fee Determination Notices

Automate the generation of the required FOIA acknowledgment letter (due within 20 working days under the statute) and fee estimate notices.

foia_letter_generator.py
python
# Generates FOIA acknowledgment letters, fee notices, and response letters

# foia_letter_generator.py
# Generates FOIA acknowledgment letters, fee notices, and response letters

ACKNOWLEDGMENT_LETTER_PROMPT = """Generate a FOIA acknowledgment letter for the following request.
The letter must comply with the agency's FOIA regulations and DOJ FOIA guidelines.

AGENCY: {agency}
AGENCY ADDRESS: {agency_address}
REQUEST DATE: {request_date}
TRACKING NUMBER: {tracking_number}
REQUESTER NAME: {requester_name}
REQUESTER ADDRESS: {requester_address}
COMPLEXITY TIER: {complexity_tier}
FEE CATEGORY: {fee_category}
EXPEDITED PROCESSING REQUESTED: {expedited}
ESTIMATED PROCESSING DAYS: {est_days}

Generate a formal acknowledgment letter that includes:
1. Confirmation of receipt with tracking number
2. Summary of the request as understood (paraphrase, do not reproduce verbatim)
3. Complexity tier designation (Simple/Complex/Voluminous) if applicable
4. Estimated response timeframe (statutory 20 working days + extension notice if Complex)
5. Fee category determination and fee estimate if applicable
   - Notify if fees are expected to exceed $25 (requires requester notification per OMB guidelines)
   - Notify of fee waiver determination if waiver was requested
6. Expedited processing determination (if requested)
7. Contact information for questions
8. FOIA officer name and title (use [FOIA OFFICER NAME] placeholder)
9. Appeal rights notice

FORMAT: Formal government letter format. Professional, clear, and courteous.
Do not include any information about the substance of the records to be searched.

LETTER DATE: {letter_date}"""

def generate_acknowledgment_letter(
    request: dict,
    classification: dict,
    tracking_number: str,
    agency_info: dict
) -> str:
    """Generate a FOIA acknowledgment letter."""

    response = client.chat.completions.create(
        model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
        messages=[
            {"role": "system", "content": "You are a federal FOIA officer generating compliance letters. Letters must be legally accurate, clearly written, and meet all statutory requirements."},
            {"role": "user", "content": ACKNOWLEDGMENT_LETTER_PROMPT.format(
                agency=agency_info.get("name"),
                agency_address=agency_info.get("address"),
                request_date=request.get("date_received"),
                tracking_number=tracking_number,
                requester_name=request.get("requester_name"),
                requester_address=request.get("requester_address"),
                complexity_tier=classification.get("complexity_tier"),
                fee_category=classification.get("fee_category"),
                expedited=classification.get("expedited_processing_requested"),
                est_days=classification.get("estimated_processing_days"),
                letter_date=datetime.date.today().strftime("%B %d, %Y")
            )}
        ],
        temperature=0.1,
        max_tokens=2000
    )

    return f"[DRAFT — REQUIRES FOIA OFFICER REVIEW AND SIGNATURE BEFORE MAILING]\n\n{response.choices[0].message.content}"


def calculate_foia_fees(
    classification: dict,
    fee_schedule: dict,
    estimated_pages: int
) -> dict:
    """Calculate FOIA processing fees per agency fee schedule."""

    requester_category = classification.get("fee_category", "Other")
    fee_waiver_requested = classification.get("fee_waiver_requested", False)

    # Standard fee categories per OMB FOIA fee guidelines
    fees = {}

    if requester_category == "Commercial":
        # Commercial requesters pay all fees
        fees["search_hours"] = 2  # Estimated
        fees["search_rate"] = fee_schedule.get("search_rate_clerical", 28.00)
        fees["review_hours"] = 1
        fees["review_rate"] = fee_schedule.get("review_rate", 65.00)
        fees["duplication_pages"] = max(0, estimated_pages - 0)  # No free pages
        fees["duplication_rate"] = fee_schedule.get("duplication_rate", 0.10)

    elif requester_category in ["News Media", "Educational", "Scientific"]:
        # News media / educational / scientific — only duplication fees (after 100 free pages)
        fees["search_hours"] = 0  # No search fee
        fees["search_rate"] = 0
        fees["review_hours"] = 0
        fees["review_rate"] = 0
        fees["duplication_pages"] = max(0, estimated_pages - 100)  # 100 free pages
        fees["duplication_rate"] = fee_schedule.get("duplication_rate", 0.10)

    else:
        # Other requesters (individuals, nonprofits) — search and duplication (after 2hrs/100pgs free)
        fees["search_hours"] = max(0, 2 - 2)  # 2 free hours
        fees["search_rate"] = fee_schedule.get("search_rate_clerical", 28.00)
        fees["duplication_pages"] = max(0, estimated_pages - 100)  # 100 free pages
        fees["duplication_rate"] = fee_schedule.get("duplication_rate", 0.10)

    total = (fees.get("search_hours", 0) * fees.get("search_rate", 0) +
             fees.get("review_hours", 0) * fees.get("review_rate", 0) +
             fees.get("duplication_pages", 0) * fees.get("duplication_rate", 0))

    fees["total_estimated"] = round(total, 2)
    fees["fee_waiver_requested"] = fee_waiver_requested
    fees["requires_advance_payment"] = total > 250  # >$250 requires advance payment per OMB
    fees["requires_notification"] = total > 25      # >$25 requires requester notification

    return fees

Step 5: Configure the Power Automate FOIA Workflow

Build the end-to-end Power Automate (GCC High) flow that orchestrates the complete FOIA processing workflow from intake to response.

Power Automate: FOIA Processing Workflow (GCC High)

  • TRIGGER: New item created in SharePoint GCC High list "FOIA Requests Received" (Front desk or FOIA portal webhook deposits new requests here)

STEP 1: Assign tracking number

  • Azure Function: assign_tracking_number(agency_prefix)
  • Update SharePoint item with tracking number

STEP 2: AI classification

  • Input: request text, requester info, date
  • Output: classification JSON
  • Update SharePoint item with classification results
AI classification API call
http
HTTP POST: Azure Function classify_foia_request

STEP 3: Generate and send acknowledgment letter

Generate acknowledgment letter API call
http
HTTP POST: Azure Function generate_acknowledgment_letter
  • Save draft to SharePoint: /FOIA/[TrackingNumber]/Draft-Acknowledgment.docx
  • Send approval request to FOIA Officer — Subject: "FOIA Acknowledgment Review: [TrackingNumber] — Due within 5 business days" | Body: Tracking number, requester, complexity tier, fee estimate, draft letter link

STEP 4: FOIA Officer reviews and approves acknowledgment

  • On approval: send letter to requester (email + certified mail tracking for postal requests)
  • Update SharePoint item status: "Acknowledged"
  • Start the 20-working-day statutory clock
  • Send task to FOIA Records Coordinator — Subject: "Records Search Required: [TrackingNumber]" | Body: KQL search query, recommended search terms, systems to search, due date
  • Create Purview Content Search case (or link to manual search instructions)

STEP 6: Records review routing (after search results received)

  • For each responsive document: trigger exemption analysis
  • Route document + analysis to FOIA Officer review queue
  • Track: total documents, reviewed, pending, redacted, released

STEP 7: Deadline monitoring

  • Daily check: days remaining to 20-day statutory deadline
  • At 10 days remaining: alert FOIA Officer
  • At 5 days remaining: alert Chief FOIA Officer
  • If extension required: generate extension letter for FOIA Officer review

STEP 8: Final response letter generation

Generate final response letter API call
http
HTTP POST: Azure Function generate_response_letter
  • Input: released records list, withheld records list, exemptions applied
  • Route for FOIA Officer review and signature

STEP 9: Records release

  • Compile responsive records (redacted) into release package
  • Generate index of released records and withheld records with exemptions cited
  • Send to requester via specified delivery method
  • Update SharePoint item status: "Closed — Full Release / Partial Release / Full Denial"
  • Archive complete case file

REPORTING

  • Monthly: Auto-generate FOIA Annual Report statistics from SharePoint list (Required under FOIA — 5 U.S.C. § 552(e))

Custom AI Components

FOIA Annual Report Statistics Generator

Type: Prompt + Data Integration Automatically compiles the statistics required for the agency's annual FOIA report to DOJ.

Implementation:

Annual FOIA report statistics query
python
# required under 5 U.S.C. § 552(e)

# Annual FOIA report statistics — required under 5 U.S.C. § 552(e)
# DOJ requires specific statistics in annual reports

ANNUAL_REPORT_STATS_QUERY = """
SELECT
  COUNT(*) as total_requests_received,
  SUM(CASE WHEN status = 'Full Release' THEN 1 ELSE 0 END) as full_releases,
  SUM(CASE WHEN status = 'Partial Release' THEN 1 ELSE 0 END) as partial_releases,
  SUM(CASE WHEN status = 'Full Denial' THEN 1 ELSE 0 END) as full_denials,
  SUM(CASE WHEN status = 'No Records' THEN 1 ELSE 0 END) as no_records,
  AVG(processing_days) as avg_processing_days,
  SUM(fees_collected) as total_fees_collected,
  SUM(fees_waived) as total_fees_waived,
  SUM(CASE WHEN exemption_1_applied = 1 THEN 1 ELSE 0 END) as exemption_1_count,
  SUM(CASE WHEN exemption_5_applied = 1 THEN 1 ELSE 0 END) as exemption_5_count,
  SUM(CASE WHEN exemption_6_applied = 1 THEN 1 ELSE 0 END) as exemption_6_count,
  SUM(CASE WHEN expedited_granted = 1 THEN 1 ELSE 0 END) as expedited_granted_count,
  SUM(CASE WHEN appeal_filed = 1 THEN 1 ELSE 0 END) as appeals_filed
FROM foia_cases
WHERE YEAR(date_received) = [REPORT_YEAR]
"""

Appeal Response Drafter

Type: Prompt Generates draft responses to FOIA administrative appeals, for FOIA Appeals Officer review.

Implementation

text
SYSTEM PROMPT:
You are a federal FOIA appeals specialist drafting an administrative appeal response.
The requester has appealed the agency's initial determination.

APPEAL GROUNDS: {appeal_grounds}
ORIGINAL DETERMINATION: {original_determination}
EXEMPTIONS APPLIED: {exemptions_applied}
RESPONSIVE RECORDS: {records_summary}

Draft an administrative appeal response that:
1. Acknowledges the appeal and identifies the tracking numbers
2. States the agency's final determination on appeal
3. For each exemption upheld on appeal: provide a more detailed legal justification
   citing DOJ guidance and relevant case law
4. For any exemption NOT upheld on appeal: explain what records are being released
5. Notifies the requester of their right to seek judicial review in U.S. District Court
6. Provides contact information for the agency's FOIA Public Liaison

IMPORTANT:
- All legal citations must be verified by agency counsel before release
- Mark all case law citations with [VERIFY CITATION]
- This is a DRAFT — requires FOIA Appeals Officer and agency counsel review
- Final determination has legal consequence — must not be sent without attorney approval
Warning

All legal citations must be verified by agency counsel before release. Mark all case law citations with [VERIFY CITATION]. This is a DRAFT — requires FOIA Appeals Officer and agency counsel review. Final determination has legal consequence — must not be sent without attorney approval.

Testing & Validation

  • Classification accuracy test: Submit 20 test FOIA requests representing the full range of agency request types (personnel, contracts, enforcement, environmental, financial). Have the FOIA officer manually classify the same 20 requests. Compare: complexity tier agreement (target ≥85%), exemption candidate identification (target ≥80% recall — must not miss likely exemptions).
  • Tracking number uniqueness test: Generate 1,000 tracking numbers and verify zero duplicates. Run the sequence number generation against the database sequence, not a random number.
  • Acknowledgment letter compliance test: Have agency counsel review 5 AI-generated acknowledgment letters for statutory compliance. Verify: tracking number present, request summary accurate (not verbatim), fee category correct, statutory timeframe cited, appeal rights included, expedited processing addressed if requested.
  • Fee calculation test: Run fee calculations for one requester from each category (Commercial, News Media, Educational, Other). Compare against manual calculation using the agency's published fee schedule. Must match exactly.
  • Records retrieval coverage test: For a test FOIA request on a known topic, run the AI-generated KQL query in Purview and verify it retrieves the known responsive documents. Identify any responsive documents the query misses — refine search term generation.
  • Exemption analysis false positive rate: Apply the exemption analyzer to 10 documents with no actual exempt content. Verify the AI does not flag non-exempt content as exempt candidates. Any false positive requires prompt refinement.
  • 20-day deadline tracking test: Create a test request dated 25 working days ago. Verify the Power Automate flow correctly identifies it as overdue and has generated alerts to the FOIA Officer and Chief FOIA Officer.
  • Annual report statistics test: Run the statistics query against test data with known outcomes and verify all counts match expected values exactly.
  • Section 508 accessibility test: Verify all generated letters and released documents are Section 508 accessible (screen reader compatible, proper heading structure, alt text for any images). Run through the PAC PDF Accessibility Checker before finalizing release documents.
  • Privacy Act flag test: Include a test request that clearly implicates Privacy Act-covered records (e.g., personnel files, medical records). Verify the classification pipeline flags privacy_act_implicated: true and routes to the appropriate review queue.

Client Handoff

Handoff Meeting Agenda (90 minutes — Chief FOIA Officer + FOIA Officers + Privacy Officer + IT Lead)

1
Workflow architecture review (15 min): Review the complete end-to-end Power Automate flow | Identify each step where human review is mandatory (acknowledgment, exemption determination, final response, appeal response) | Confirm no automated releases without FOIA officer approval
2
Classification and routing demonstration (15 min): Submit 3 live test requests and show real-time classification | Walk through the classification output and routing queue assignment | Demonstrate the search term generation for each request type
3
Exemption analysis tool review (20 min): Apply the exemption analyzer to a sample document | FOIA officer reviews the output and provides feedback on accuracy | Emphasize: AI identifies candidates, FOIA officer makes all final determinations | Review the legal disclaimer language on all AI outputs
4
Letter generation and fee calculation (15 min): Generate a sample acknowledgment letter for FOIA officer review | Walk through fee calculation for each requester category | Confirm fee schedule is correctly configured per agency regulations
5
Compliance and reporting (15 min): Review annual report statistics generation | Confirm the backlog management workflow (deadline alerts, escalation) | Review DOJ FOIA reporting requirements and how the pipeline supports them
6
Documentation handoff

Maintenance

Daily Tasks (Automated)

  • Deadline monitoring flow checks all open cases for approaching 20-day deadlines
  • New request intake and classification runs automatically on receipt

Weekly Tasks

  • FOIA Officer reviews classification accuracy for the week — flag any misclassifications for prompt refinement
  • Check Power Automate flow run history for errors

Monthly Tasks

  • Generate FOIA backlog report from SharePoint case list
  • Review Azure OpenAI consumption costs
  • Compile monthly FOIA statistics for Chief FOIA Officer

Quarterly Tasks

  • Review exemption analysis accuracy with FOIA officer — adjust prompts if DOJ guidance has been updated
  • Review and update fee schedule if agency has published new FOIA fee regulations
  • DOJ FOIA.gov data submission (quarterly statistics due to DOJ)

Annual Tasks

  • FOIA Annual Report compilation and DOJ submission (due February 1 each year)
  • Full prompt library review against current DOJ FOIA guidelines
  • Privacy Impact Assessment annual review for the FOIA processing system
  • FOIA officer training refresh — annual training on AI-assisted FOIA tools

Alternatives

Palantir AIP for Government (Enterprise FOIA Automation)

Palantir's AI Platform for Government has been deployed for FOIA automation at several large federal agencies, providing AI-assisted classification, redaction suggestion, and response generation within a FedRAMP High authorized boundary. Best for: Large agencies processing 10,000+ FOIA requests per year. Tradeoffs: Enterprise pricing ($500K+/year); implementation timeline of 6–12 months; significant change management for FOIA staff.

Relativity (eDiscovery Platform for FOIA)

Relativity

RelativityeDiscovery Platform

$50,000–$200,000+/year

Relativity is the market-leading eDiscovery platform used for document review in litigation, adapted by some agencies for FOIA records review. Provides AI-assisted relevance review, privilege identification, and redaction workflows. Best for: Agencies already using Relativity for litigation support who want to leverage the same platform for FOIA. Tradeoffs: Not FedRAMP authorized (requires private cloud deployment for sensitive data); more expensive than the custom Azure pipeline for FOIA-only use.

Manual FOIA Processing + AI Letter Drafting Only (Conservative)

For agencies with legal concerns about AI involvement in exemption analysis, deploy AI assistance only for acknowledgment letter drafting, fee calculation, and annual report statistics — the three lowest-legal-risk automation points. The classification, records retrieval, and exemption analysis remain entirely manual. Best for: Agencies with risk-averse legal counsel or prior FOIA litigation that makes them sensitive to any AI involvement in exemption decisions. Provides meaningful efficiency gains at significantly lower legal risk.

Want early access to the full toolkit?