Home Blog Reviews Best Picks Guides Tools Glossary Advertise Subscribe Free
Tech Frontline Aug 23, 2026 6 min read

Step-by-Step Tutorial: Building a Secure AI-Powered Document Approval Workflow

Follow this hands-on guide to design and deploy a secure, AI-powered document approval pipeline from scratch in 2026.

T
Tech Daily Shot Team
Published Aug 23, 2026

Automating document approval workflows with AI can dramatically improve efficiency, accuracy, and compliance—if done securely. In this practical tutorial, we'll guide you through building a secure, AI-powered document approval workflow from scratch, including user authentication, AI-based document classification, and multi-level approvals with audit trails.

As we covered in our complete guide to automating document approval workflows with AI, this area deserves a deeper look—especially when it comes to building secure, robust solutions. This tutorial provides a hands-on, code-first approach for developers and architects ready to build or upgrade their own approval systems.

Prerequisites

Overview

We'll build a secure workflow with these core components:

  1. User authentication (JWT-based)
  2. Secure document upload and storage
  3. AI-powered document classification (using OpenAI GPT-4 API)
  4. Multi-level approval logic
  5. Audit logging and access control

For a broader comparison of tools and platforms, see Top AI Tools for Document Approval Automation in 2026: A Hands-On Comparison.


Step 1: Project Setup and Secure Configuration

  1. Clone the Starter Repository
    git clone https://github.com/your-org/secure-ai-doc-approval.git
    cd secure-ai-doc-approval

    (If starting from scratch, create a new directory and initialize a Git repo.)

  2. Create a Python Virtual Environment
    python3 -m venv venv
    source venv/bin/activate
  3. Install Dependencies
    pip install fastapi[all] sqlalchemy psycopg2-binary python-jose[cryptography] python-multipart openai
  4. Set Up Environment Variables

    Create a .env file:

    OPENAI_API_KEY=your-openai-key
    DATABASE_URL=postgresql://user:password@localhost:5432/approvaldb
    SECRET_KEY=your-very-secret-jwt-key
    ALGORITHM=HS256
    ACCESS_TOKEN_EXPIRE_MINUTES=60
        

    Tip: Never commit secrets to git. Use .gitignore!

  5. Initialize the Database (PostgreSQL)
    docker run --name approvaldb -e POSTGRES_PASSWORD=password -e POSTGRES_USER=user -e POSTGRES_DB=approvaldb -p 5432:5432 -d postgres:14

Step 2: Implement Secure User Authentication

We'll use JWT tokens for secure, stateless authentication. FastAPI makes this straightforward.

  1. Create User and Token Models (models.py)
    
    from sqlalchemy import Column, Integer, String, Boolean
    from sqlalchemy.ext.declarative import declarative_base
    
    Base = declarative_base()
    
    class User(Base):
        __tablename__ = "users"
        id = Column(Integer, primary_key=True, index=True)
        username = Column(String, unique=True, index=True)
        hashed_password = Column(String)
        is_approver = Column(Boolean, default=False)
        is_admin = Column(Boolean, default=False)
        
  2. JWT Auth Utilities (auth.py)
    
    from datetime import datetime, timedelta
    from jose import jwt
    
    def create_access_token(data: dict, secret: str, algorithm: str, expires_delta: timedelta):
        to_encode = data.copy()
        expire = datetime.utcnow() + expires_delta
        to_encode.update({"exp": expire})
        return jwt.encode(to_encode, secret, algorithm=algorithm)
        
  3. Register/Login Endpoints (main.py)
    
    from fastapi import FastAPI, Depends, HTTPException
    from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
    from sqlalchemy.orm import Session
    from auth import create_access_token
    import models
    
    app = FastAPI()
    oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
    
    @app.post("/token")
    def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
        user = authenticate_user(db, form_data.username, form_data.password)
        if not user:
            raise HTTPException(status_code=400, detail="Incorrect username or password")
        access_token = create_access_token(
            data={"sub": user.username},
            secret=SECRET_KEY,
            algorithm=ALGORITHM,
            expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        )
        return {"access_token": access_token, "token_type": "bearer"}
        

    Note: Implement authenticate_user and get_db as needed.

Step 3: Secure Document Upload and Storage

  1. Create Document Model (models.py)
    
    from sqlalchemy import ForeignKey, DateTime, Text
    from sqlalchemy.orm import relationship
    import datetime
    
    class Document(Base):
        __tablename__ = "documents"
        id = Column(Integer, primary_key=True, index=True)
        owner_id = Column(Integer, ForeignKey("users.id"))
        filename = Column(String)
        content = Column(Text)
        status = Column(String, default="pending")
        created_at = Column(DateTime, default=datetime.datetime.utcnow)
        owner = relationship("User")
        
  2. Document Upload Endpoint (main.py)
    
    from fastapi import File, UploadFile, Depends
    
    @app.post("/documents/upload")
    def upload_document(file: UploadFile = File(...), current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
        contents = file.file.read().decode("utf-8")
        doc = models.Document(
            owner_id=current_user.id,
            filename=file.filename,
            content=contents,
            status="pending"
        )
        db.add(doc)
        db.commit()
        db.refresh(doc)
        return {"document_id": doc.id, "status": doc.status}
        

    Security Note: Always validate file types and scan for malware in production.

Step 4: Integrate AI-Powered Document Classification

  1. Install OpenAI SDK
    pip install openai
  2. AI Classification Utility (ai.py)
    
    import openai
    import os
    
    openai.api_key = os.getenv("OPENAI_API_KEY")
    
    def classify_document(text: str) -> str:
        prompt = f"Classify the following document for approval workflow (e.g., 'invoice', 'contract', 'policy', 'other'):\n\n{text[:1000]}"
        response = openai.ChatCompletion.create(
            model="gpt-4",
            messages=[
                {"role": "system", "content": "You are a document classification assistant."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=10
        )
        label = response['choices'][0]['message']['content'].strip().lower()
        return label
        
  3. Call AI Classifier After Upload (main.py)
    
    from ai import classify_document
    
    @app.post("/documents/upload")
    def upload_document(...):
        ...
        doc_type = classify_document(contents)
        doc.status = "pending"
        doc.type = doc_type
        db.add(doc)
        db.commit()
        db.refresh(doc)
        return {"document_id": doc.id, "type": doc_type, "status": doc.status}
        

Step 5: Build the Approval Workflow Logic

  1. Approval Model (models.py)
    
    class Approval(Base):
        __tablename__ = "approvals"
        id = Column(Integer, primary_key=True)
        document_id = Column(Integer, ForeignKey("documents.id"))
        approver_id = Column(Integer, ForeignKey("users.id"))
        status = Column(String)  # 'approved' or 'rejected'
        timestamp = Column(DateTime, default=datetime.datetime.utcnow)
        document = relationship("Document")
        approver = relationship("User")
        
  2. Approval Endpoint (main.py)
    
    from fastapi import Body
    
    @app.post("/documents/{doc_id}/approve")
    def approve_document(doc_id: int, approved: bool = Body(...), current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
        doc = db.query(models.Document).filter_by(id=doc_id).first()
        if not doc:
            raise HTTPException(status_code=404, detail="Document not found")
        if not current_user.is_approver:
            raise HTTPException(status_code=403, detail="Not authorized")
        approval = models.Approval(
            document_id=doc_id,
            approver_id=current_user.id,
            status="approved" if approved else "rejected"
        )
        db.add(approval)
        doc.status = "approved" if approved else "rejected"
        db.commit()
        return {"document_id": doc.id, "status": doc.status}
        

    Tip: For multi-level approvals, add an approval_level field and check previous approvals before updating status.

Step 6: Add Audit Logging and Access Controls

  1. Audit Log Model (models.py)
    
    class AuditLog(Base):
        __tablename__ = "audit_logs"
        id = Column(Integer, primary_key=True)
        user_id = Column(Integer, ForeignKey("users.id"))
        action = Column(String)
        document_id = Column(Integer, ForeignKey("documents.id"))
        timestamp = Column(DateTime, default=datetime.datetime.utcnow)
        
  2. Log Actions in Endpoints
    
    def log_action(db: Session, user_id: int, action: str, document_id: int):
        log = models.AuditLog(user_id=user_id, action=action, document_id=document_id)
        db.add(log)
        db.commit()
    
    log_action(db, current_user.id, "approved" if approved else "rejected", doc_id)
        
  3. Restrict Access Based on Roles
    
    def get_current_approver_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
        user = get_current_user(token, db)
        if not user.is_approver:
            raise HTTPException(status_code=403, detail="Approver access required")
        return user
        

Step 7: Secure Deployment (Docker + HTTPS)

  1. Dockerize the App (Dockerfile)
    
    FROM python:3.10-slim
    WORKDIR /app
    COPY requirements.txt .
    RUN pip install -r requirements.txt
    COPY . .
    CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
        
  2. Run with Docker Compose (docker-compose.yml)
    
    version: "3.9"
    services:
      db:
        image: postgres:14
        restart: always
        environment:
          POSTGRES_USER: user
          POSTGRES_PASSWORD: password
          POSTGRES_DB: approvaldb
        ports:
          - 5432:5432
      app:
        build: .
        ports:
          - "8000:8000"
        depends_on:
          - db
        env_file:
          - .env
        
  3. Enable HTTPS (Reverse Proxy Example with Caddy)
    
    :443
    reverse_proxy app:8000
    tls you@yourdomain.com
        

    For production, always use HTTPS. Consider using a managed certificate provider or a reverse proxy like Caddy or Nginx.

Common Issues & Troubleshooting

Next Steps

You now have a working, secure AI-powered document approval workflow! To further enhance your solution:

For more platform options, benchmarks, and security guidance, revisit our 2026 Guide to Automating Document Approval Workflows With AI.

Screenshots (Descriptions)

tutorial AI workflow document approval security how-to

Related Articles

Tech Frontline
Essential API Integrations for AI Workflow Automation in 2026: From ERPs to Niche SaaS
Aug 23, 2026
Tech Frontline
Securing AI Workflow Automation Endpoints: API Key Management and Secrets Handling (2026 Tutorial)
Aug 22, 2026
Tech Frontline
Integrating AI Workflow Automation with Modern Project Management Tools: A 2026 Developer's Guide
Aug 22, 2026
Tech Frontline
How to Set Up Automated Guardrails for AI Workflow Automation (2026 Tutorial)
Aug 22, 2026
Free & Interactive

Tools & Software

100+ hand-picked tools personally tested by our team — for developers, designers, and power users.

🛠 Dev Tools 🎨 Design 🔒 Security ☁️ Cloud
Explore Tools →
Step by Step

Guides & Playbooks

Complete, actionable guides for every stage — from setup to mastery. No fluff, just results.

📚 Homelab 🔒 Privacy 🐧 Linux ⚙️ DevOps
Browse Guides →
Advertise with Us

Put your brand in front of 10,000+ tech professionals

Native placements that feel like recommendations. Newsletter, articles, banners, and directory features.

✉️
Newsletter
10K+ reach
📰
Articles
SEO evergreen
🖼️
Banners
Site-wide
🎯
Directory
Priority

Stay ahead of the tech curve

Join 10,000+ professionals who start their morning smarter. No spam, no fluff — just the most important tech developments, explained.