2

API / Backend-for-Frontend Layer

Agreement generation and signature submission endpoints

📄 GET /api/v1/agreements/generate

  • Purpose: Generate loan agreement PDF from template with customer-specific data
  • When Called: On page load of CM09 screen
  • Authentication: JWT Bearer token from login session
  • Response Time: <1 second (template merge + PDF generation)
  • Output: PDF URL + HTML preview text
Request Example
GET /api/v1/agreements/generate?application_id=APP-2025-001234
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response Example (200 OK)
{
  "request_id": "req_cm09_20251123_153000",
  "status": "success",
  "agreement": {
    "id": "AGR-2025-001234",
    "application_id": "APP-2025-001234",
    "pdf_url": "/api/v1/agreements/AGR-2025-001234/download",
    "preview_html": "
This Business Loan Agreement...
"
, "terms": { "principal": 150000, "apr": 4.5, "term_months": 36, "monthly_payment": 4440, "total_repayment": 159840 }, "generated_at": "2025-11-23T15:30:00Z" }, "processing_time_ms": 847 }

âœī¸ POST /api/v1/signatures/submit

  • Purpose: Submit signed agreement with signature image and legal confirmations
  • When Called: When customer clicks "Submit Signed Agreement" button
  • Content-Type: application/json
  • Rate Limit: 5 requests/minute per user (prevent duplicate submissions)
  • Response Time: <2 seconds (includes DocuSign API call)
Request Example
POST /api/v1/signatures/submit
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "application_id": "APP-2025-001234",
  "agreement_id": "AGR-2025-001234",
  "signature_data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEU...",
  "legal_confirmations": {
    "terms_understood": true,
    "information_accurate": true,
    "legally_binding": true,
    "credit_monitoring_consent": true
  },
  "signer_details": {
    "name": "John Smith",
    "signing_date": "2025-11-23",
    "ip_address": "86.134.45.123",
    "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)"
  }
}
Response Example (200 OK)
{
  "request_id": "req_cm09_20251123_153130",
  "status": "success",
  "signature": {
    "id": "SIG-2025-001234",
    "application_id": "APP-2025-001234",
    "agreement_id": "AGR-2025-001234",
    "docusign_envelope_id": "a7f3c5d9-e8b2-4a1c-9f6d-3e4b5a7c8d9e",
    "signed_at": "2025-11-23T15:31:30Z",
    "signed_pdf_url": "/api/v1/agreements/AGR-2025-001234/signed"
  },
  "next_step": {
    "screen": "CM10",
    "action": "view_confirmation",
    "message": "Application submitted successfully!"
  },
  "processing_time_ms": 1847
}

đŸ“Ĩ GET /api/v1/agreements/{id}/download

  • Purpose: Download unsigned agreement PDF
  • Content-Type: application/pdf
  • Authorization: Only document owner can download
  • Response: PDF file with proper headers for browser download

đŸ“Ĩ GET /api/v1/agreements/{id}/signed

  • Purpose: Download signed agreement PDF (after signature submission)
  • Content-Type: application/pdf
  • Features: Includes DocuSign certificate of completion as final page
  • Retention: Stored for 7 years (regulatory requirement)

🔐 Security & Validation

  • Signature Validation: Verify signature image is not blank (minimum 100 pixels of ink)
  • Checkbox Validation: All 4 legal confirmations must be true
  • IP Capture: Record IP address for non-repudiation
  • User Agent: Capture browser/device info for audit
  • Timestamp: Record exact signing time with millisecond precision
  • Idempotency: Duplicate submissions return same signature ID
âš ī¸ Legal Requirement: E-signatures must meet ESIGN Act (US) and eIDAS (EU) requirements. We capture: signer identity, consent to sign electronically, intent to sign, association of signature with record, and audit trail.
3

Business Logic Layer

Agreement generation and signature validation

📝 Agreement Generation Engine

  • Template: Pre-approved legal template with merge fields
  • Data Merge: Insert customer/loan data into template (principal, APR, term, etc.)
  • PDF Generation: Convert HTML template to PDF using Puppeteer/wkhtmltopdf
  • Validation: Ensure all required fields populated before generation
  • Storage: Save unsigned PDF to S3 with unique agreement ID
Agreement Generation Logic
async function generateAgreement(applicationId) {
  // 1. Get application data
  const app = await Application.findById(applicationId);
  
  // 2. Load legal template
  const template = await loadTemplate('business_loan_agreement.html');
  
  // 3. Merge data into template
  const mergedHtml = template
    .replace('{{principal}}', formatCurrency(app.chosen_amount))
    .replace('{{apr}}', app.apr)
    .replace('{{term_months}}', app.chosen_term_months)
    .replace('{{monthly_payment}}', formatCurrency(app.monthly_payment))
    .replace('{{borrower_name}}', app.company_name)
    .replace('{{company_number}}', app.company_number)
    .replace('{{loan_purpose}}', app.specific_use);
  
  // 4. Generate PDF
  const pdf = await htmlToPdf(mergedHtml);
  
  // 5. Upload to S3
  const agreementId = generateAgreementId();
  const s3Key = `agreements/${applicationId}/${agreementId}.pdf`;
  await s3.upload(s3Key, pdf);
  
  // 6. Save metadata to database
  await Agreement.create({
    id: agreementId,
    application_id: applicationId,
    s3_key: s3Key,
    status: 'unsigned'
  });
  
  return { agreementId, s3Key, html: mergedHtml };
}

✅ Signature Validation Rules

  • Not Blank: Signature canvas must have at least 100 pixels of ink (prevent blank submissions)
  • All Checkboxes: All 4 legal confirmations must be checked
  • Valid Format: Signature must be valid base64-encoded PNG image
  • Size Limit: Signature image <500KB (prevent huge files)
  • Agreement Exists: Agreement ID must exist and be in "unsigned" status
Validation Logic
function validateSignature(signatureData, confirmations) {
  // Check all confirmations are true
  if (!confirmations.terms_understood || 
      !confirmations.information_accurate ||
      !confirmations.legally_binding ||
      !confirmations.credit_monitoring_consent) {
    throw new ValidationError("All confirmations required");
  }
  
  // Check signature is not blank
  const image = decodeBase64Image(signatureData);
  const inkPixels = countNonWhitePixels(image);
  if (inkPixels < 100) {
    throw new ValidationError("Signature appears blank");
  }
  
  // Check signature size
  if (signatureData.length > 500000) {
    throw new ValidationError("Signature image too large");
  }
  
  return true;
}

🔄 Status Transition Logic

  • Before Signature: application.status = "documents_uploaded" (from CM08)
  • After Signature: application.status = "signed"
  • Agreement Status: agreement.status = "signed" (from "unsigned")
  • Next Status: "completed" (after CM10 confirmation)

📋 DocuSign Envelope Creation

  • Purpose: Create legally-binding e-signature envelope with audit trail
  • Process: Upload agreement PDF → Add signature tab → Send to signer → Capture signature → Return signed PDF
  • Certificate: DocuSign appends "Certificate of Completion" page with audit trail
  • Compliance: Meets ESIGN Act and eIDAS requirements
💡 DocuSign Embedded Signing

Embedded Mode: Customer signs within AINA app (no DocuSign account needed)

Branding: White-label experience with AINA branding

Audit Trail: Captures IP, timestamp, geolocation, authentication method

Certificate: Final page shows complete signing history and validation

4

Integration & Middleware Layer

DocuSign API integration and event processing

📨 DocuSign Embedded Signing Flow

  • Step 1: Create envelope with PDF document
  • Step 2: Add signer (customer name + email)
  • Step 3: Generate embedded signing URL (recipient view)
  • Step 4: Display DocuSign iframe in AINA app
  • Step 5: Capture signature events via webhook
  • Step 6: Download signed PDF from DocuSign
DocuSign Integration
async function createDocuSignEnvelope(agreementId, signerDetails) {
  // 1. Get PDF from S3
  const pdfBytes = await s3.download(agreementS3Key);
  
  // 2. Create envelope definition
  const envelopeDefinition = {
    emailSubject: "Sign Your AINA Loan Agreement",
    documents: [{
      documentBase64: pdfBytes.toString('base64'),
      name: "Loan Agreement",
      fileExtension: "pdf",
      documentId: "1"
    }],
    recipients: {
      signers: [{
        email: signerDetails.email,
        name: signerDetails.name,
        recipientId: "1",
        routingOrder: "1",
        tabs: {
          signHereTabs: [{
            documentId: "1",
            pageNumber: "7", // Last page
            xPosition: "100",
            yPosition: "200"
          }]
        }
      }]
    },
    status: "sent"
  };
  
  // 3. Create envelope via DocuSign API
  const envelope = await docusign.createEnvelope(envelopeDefinition);
  
  // 4. Generate embedded signing URL
  const viewRequest = {
    returnUrl: "https://aina.app/cm10-confirmation",
    authenticationMethod: "none",
    email: signerDetails.email,
    userName: signerDetails.name
  };
  
  const view = await docusign.createRecipientView(envelope.envelopeId, viewRequest);
  
  return {
    envelopeId: envelope.envelopeId,
    signingUrl: view.url
  };
}

🔔 Webhook Event Handling

  • Endpoint: POST /api/v1/webhooks/docusign
  • Events: envelope-sent, envelope-completed, recipient-signed
  • Verification: Validate webhook signature (HMAC SHA256)
  • Processing: Update application status, download signed PDF, trigger notifications
Webhook Handler
async function handleDocuSignWebhook(event) {
  // Verify webhook signature
  const isValid = verifyWebhookSignature(event);
  if (!isValid) throw new SecurityError("Invalid webhook");
  
  if (event.event === "envelope-completed") {
    const envelopeId = event.data.envelopeId;
    
    // Download signed PDF from DocuSign
    const signedPdf = await docusign.downloadDocument(envelopeId);
    
    // Upload to S3
    const s3Key = `agreements/${applicationId}/${agreementId}_signed.pdf`;
    await s3.upload(s3Key, signedPdf);
    
    // Update database
    await Agreement.update(agreementId, {
      status: 'signed',
      signed_pdf_s3_key: s3Key,
      docusign_envelope_id: envelopeId,
      signed_at: new Date()
    });
    
    // Update application status
    await Application.update(applicationId, {
      status: 'signed'
    });
    
    // Send notifications
    await sendSignedConfirmation(applicationId);
  }
}

📊 Analytics & Monitoring

  • Signing Metrics: Track time to sign, abandonment rate, completion rate
  • DocuSign Latency: Monitor API response times
  • Error Tracking: Alert on failed envelope creation, webhook failures
  • Conversion: Track CM08 → CM09 → CM10 funnel

🔐 Security Measures

  • Webhook Verification: Validate HMAC signature on all DocuSign webhooks
  • TLS Encryption: All DocuSign API calls use TLS 1.3
  • Access Tokens: OAuth 2.0 with JWT tokens (refresh every hour)
  • IP Whitelist: Only accept webhooks from DocuSign IP ranges
5

External Systems Integration

DocuSign e-signature platform

âœī¸ DocuSign eSignature API

  • Purpose: Legally-binding electronic signatures with audit trail
  • API Version: DocuSign REST API v2.1
  • Authentication: OAuth 2.0 JWT Grant (service account)
  • Environment: Production (api.docusign.net)
  • Account Type: Business Pro ($40/user/month)
📋 DocuSign Features Used

Embedded Signing: Sign within AINA app (no DocuSign account needed)

Templates: Pre-configured signature placement on loan agreements

Webhooks: Real-time notifications when envelope completed

Certificate of Completion: Legal audit trail appended to signed PDF

Mobile Responsive: Optimized signing experience on mobile devices

💰 DocuSign Pricing & Limits

  • Plan: Business Pro - $40/user/month
  • Envelopes: Unlimited envelopes per month
  • API Calls: Rate limit 1,000 calls/hour per account
  • Storage: Signed documents stored for 10 years
  • Support: 24/7 phone + email support
  • SLA: 99.99% uptime guarantee

🔐 Legal Compliance

  • ESIGN Act (US): Electronic Signatures in Global and National Commerce Act compliant
  • eIDAS (EU): Qualified electronic signature standard (Advanced Electronic Signature)
  • UETA (US): Uniform Electronic Transactions Act compliant
  • UK eIDAS: UK Electronic Identification and Trust Services compliant
  • Admissibility: DocuSign signatures admissible in court in 180+ countries
✅ Legal Validity: DocuSign e-signatures are legally equivalent to wet signatures in UK, EU, US, and 180+ countries. The Certificate of Completion provides irrefutable proof of signing and audit trail.

📄 API Endpoints Used

  • POST /envelopes: Create new signature envelope
  • POST /envelopes/{id}/views/recipient: Generate embedded signing URL
  • GET /envelopes/{id}/documents/{docId}: Download signed PDF
  • GET /envelopes/{id}: Get envelope status
  • POST /connect: Configure webhooks (Connect)

🔄 Alternative Providers Considered

  • Adobe Sign: Similar features, $30/user/month (slightly cheaper)
  • HelloSign (Dropbox Sign): $15/user/month (budget option, fewer features)
  • PandaDoc: $19/user/month (good for proposals, less for legal docs)
  • SignNow: $8/user/month (very basic, no embedded signing)
âš ī¸ Why DocuSign? DocuSign is the market leader with the strongest legal defensibility, broadest international compliance, and best embedded signing experience. For financial lending, legal validity is paramount - worth the premium pricing.
6

Data Persistence Layer

Agreement and signature metadata storage

📊 agreements Table Schema

Column Type Description Example
id VARCHAR(50) Primary key AGR-2025-001234
application_id VARCHAR(50) Foreign key APP-2025-001234
unsigned_pdf_s3_key VARCHAR(500) S3 path to unsigned PDF agreements/APP.../AGR....pdf
signed_pdf_s3_key VARCHAR(500) S3 path to signed PDF agreements/...signed.pdf
status VARCHAR(20) unsigned/signed signed
docusign_envelope_id VARCHAR(100) DocuSign envelope UUID a7f3c5d9-e8b2-4a1c...
generated_at TIMESTAMP When PDF generated 2025-11-23 15:30:00
signed_at TIMESTAMP When customer signed 2025-11-23 15:31:30

📊 signatures Table Schema

Column Type Description Example
id VARCHAR(50) Primary key SIG-2025-001234
agreement_id VARCHAR(50) Foreign key AGR-2025-001234
signature_image_s3_key VARCHAR(500) Canvas signature PNG signatures/.../SIG....png
signer_name VARCHAR(200) Full legal name John Smith
signer_ip_address VARCHAR(45) IP at signing time 86.134.45.123
signer_user_agent TEXT Browser/device info Mozilla/5.0 (iPhone...)
signed_at TIMESTAMP(3) Exact timestamp (ms) 2025-11-23 15:31:30.847
legal_confirmations JSONB 4 checkbox states {"terms_understood": true...}
SQL: Create Tables
CREATE TABLE agreements (
    id VARCHAR(50) PRIMARY KEY,
    application_id VARCHAR(50) NOT NULL REFERENCES applications(id),
    unsigned_pdf_s3_key VARCHAR(500) NOT NULL,
    signed_pdf_s3_key VARCHAR(500),
    status VARCHAR(20) DEFAULT 'unsigned',
    docusign_envelope_id VARCHAR(100),
    generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    signed_at TIMESTAMP
);

CREATE TABLE signatures (
    id VARCHAR(50) PRIMARY KEY,
    agreement_id VARCHAR(50) NOT NULL REFERENCES agreements(id),
    signature_image_s3_key VARCHAR(500) NOT NULL,
    signer_name VARCHAR(200) NOT NULL,
    signer_ip_address VARCHAR(45) NOT NULL,
    signer_user_agent TEXT,
    signed_at TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
    legal_confirmations JSONB NOT NULL
);

CREATE INDEX idx_agreements_app ON agreements(application_id);
CREATE INDEX idx_signatures_agreement ON signatures(agreement_id);

💾 Insert Signature Record

SQL: Record Signature
INSERT INTO signatures (
    id,
    agreement_id,
    signature_image_s3_key,
    signer_name,
    signer_ip_address,
    signer_user_agent,
    legal_confirmations
) VALUES (
    'SIG-2025-001234',
    'AGR-2025-001234',
    'signatures/APP-2025-001234/SIG-2025-001234.png',
    'John Smith',
    '86.134.45.123',
    'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)',
    '{"terms_understood": true, "information_accurate": true, "legally_binding": true, "credit_monitoring_consent": true}'::jsonb
);

🔄 Update Application Status

SQL: Mark as Signed
UPDATE applications
SET
    current_status = 'signed',
    signed_at = CURRENT_TIMESTAMP,
    updated_at = CURRENT_TIMESTAMP
WHERE id = 'APP-2025-001234';

UPDATE agreements
SET
    status = 'signed',
    signed_pdf_s3_key = 'agreements/.../AGR-2025-001234_signed.pdf',
    docusign_envelope_id = 'a7f3c5d9-e8b2-4a1c-9f6d-3e4b5a7c8d9e',
    signed_at = CURRENT_TIMESTAMP
WHERE id = 'AGR-2025-001234';

📋 Audit Log (Elasticsearch)

JSON: Signature Event
{
  "event_type": "agreement_signed",
  "application_id": "APP-2025-001234",
  "agreement_id": "AGR-2025-001234",
  "signature_id": "SIG-2025-001234",
  "customer_id": "CUST-001234",
  "timestamp": "2025-11-23T15:31:30.847Z",
  "signer_details": {
    "name": "John Smith",
    "ip_address": "86.134.45.123",
    "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)"
  },
  "legal_confirmations": {
    "terms_understood": true,
    "information_accurate": true,
    "legally_binding": true,
    "credit_monitoring_consent": true
  },
  "docusign": {
    "envelope_id": "a7f3c5d9-e8b2-4a1c-9f6d-3e4b5a7c8d9e",
    "certificate_url": "https://docusign.net/certificate/..."
  }
}

📊 Data Retention Policy

  • Signed PDFs: Retained permanently in S3 (legal requirement)
  • Signature Images: Retained for 7 years (regulatory compliance)
  • Audit Logs: Retained for 7 years in Elasticsearch
  • DocuSign Copies: DocuSign retains for 10 years (their policy)
  • Unsigned PDFs: Can be purged after 90 days if agreement signed
📊 E-Signature Sequence Flow
👤
Customer
1. Navigates to CM09
đŸ’ģ
Browser
đŸ’ģ
Browser
2. GET /agreements/generate
🔒
API
🔒
API
3. Merge data into template
📝
Agreement Engine
📝
Agreement Engine
4. Generate PDF
📄
PDF Generator
📄
PDF Generator
5. Upload to S3
â˜ī¸
AWS S3
â˜ī¸
AWS S3
6. Return agreement HTML
👤
Customer
👤
Customer
7. Reads & checks boxes
đŸ’ģ
Browser
👤
Customer
8. Draws signature on canvas
âœī¸
Canvas
👤
Customer
9. Clicks Submit
đŸ’ģ
Browser
đŸ’ģ
Browser
10. POST /signatures/submit
🔒
API
🔒
API
11. Validate signature & checkboxes
✅
Validator
✅
Validator
12. Create DocuSign envelope
âœī¸
DocuSign
âœī¸
DocuSign
13. Apply signature to PDF
📄
PDF Signer
📄
PDF Signer
14. Save to database
💾
PostgreSQL
💾
PostgreSQL
15. Return success
👤
Customer
👤
Customer
16. Redirect to CM10
✅
Confirmation
âš ī¸ Error Scenarios & Handling

Blank Signature

Customer submits without drawing anything on canvas

Response: 400 Bad Request
UI: "Please provide your signature"
Action: Highlight signature canvas, prevent submission

Missing Checkboxes

One or more legal confirmations not checked

Response: 400 Bad Request
UI: "Please confirm all statements to proceed"
Action: Highlight unchecked boxes in red

DocuSign API Failure

DocuSign service unavailable or rate limit exceeded

Response: 503 Service Unavailable
UI: "Signature service temporarily unavailable. Please try again in a moment"
Action: Retry 3 times with exponential backoff, then show error

Agreement Not Found

Agreement ID doesn't exist or was deleted

Response: 404 Not Found
UI: "Agreement expired. Please restart application"
Action: Regenerate agreement from CM07 data

Already Signed

Customer tries to sign agreement that's already signed

Response: 409 Conflict
UI: "This agreement has already been signed"
Action: Redirect to CM10 (confirmation), show signed PDF

PDF Generation Failed

Error generating PDF from template

Response: 500 Internal Server Error
UI: "Unable to generate agreement. Please try again"
Action: Log error to Sentry, retry PDF generation

S3 Upload Failed

Network error or S3 service unavailable

Response: 503 Service Unavailable
UI: "Upload failed. Please try again"
Action: Retry upload 3 times, fallback to backup storage

Session Expired

JWT token expired during signing process

Response: 401 Unauthorized
UI: "Session expired. Please log in again"
Action: Save signature to localStorage, redirect to login, restore after login AINA Architecture - CM09: E-Signature
CM09

Electronic Signature

Journey: Customer Mobile
Duration: ~1 minute
AI Agent: None (User signature)
External APIs: DocuSign Embedded Signing
1

User Interface Layer

Agreement display with canvas-based signature capture

📄 Loan Agreement Display

  • Scrollable Agreement: Full loan agreement text in scrollable container (max-height 140px)
  • Key Terms Highlighted: Loan amount (ÂŖ150,000), APR (4.5%), term (36 months), monthly payment (ÂŖ4,440)
  • Legal Language: Formal legal contract language with numbered clauses
  • Download Option: "Download PDF" button to save copy for records
  • Formatting: Bold text for emphasis on critical terms, clear paragraph breaks
📋 Agreement Sections Included

1. Loan Amount: Principal amount and currency

2. Interest Rate: APR and calculation method

3. Repayment Terms: Monthly payment, term, total repayment

4. Purpose: Approved use of funds

5. Security: Personal guarantee and asset charge details

6. Early Repayment: Terms for early settlement (no penalty)

7. Default: Consequences of non-payment

✅ Legal Confirmations (Checkboxes)

  • Checkbox 1: "I confirm that I have read and understood the loan agreement terms and conditions in full"
  • Checkbox 2: "I confirm that all information provided in this application is true, accurate, and complete"
  • Checkbox 3: "I understand that this is a legally binding agreement and that I am personally liable for repayment as a director"
  • Checkbox 4: "I consent to AINA performing credit checks and monitoring my business performance throughout the loan term"
  • Required: All 4 checkboxes must be checked before signing
  • Visual Feedback: Checkboxes turn blue with checkmark icon when clicked

âœī¸ Signature Canvas

  • HTML5 Canvas: 311px × 140px signature capture area
  • Touch/Mouse Support: Works with finger on mobile or mouse on desktop
  • Placeholder Text: "Sign here with your finger or mouse" before any strokes
  • Clear Button: "× Clear" button to erase and start over
  • Visual Guidelines: Dotted line showing where to sign
  • Real-time Drawing: Black ink stroke follows finger/cursor smoothly
Canvas Implementation
const canvas = document.getElementById('signatureCanvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;
let lastX = 0;
let lastY = 0;

canvas.addEventListener('mousedown', (e) => {
  isDrawing = true;
  [lastX, lastY] = [e.offsetX, e.offsetY];
});

canvas.addEventListener('mousemove', (e) => {
  if (!isDrawing) return;
  
  ctx.strokeStyle = '#000000';
  ctx.lineWidth = 2;
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';
  
  ctx.beginPath();
  ctx.moveTo(lastX, lastY);
  ctx.lineTo(e.offsetX, e.offsetY);
  ctx.stroke();
  
  [lastX, lastY] = [e.offsetX, e.offsetY];
});

canvas.addEventListener('mouseup', () => {
  isDrawing = false;
});

â„šī¸ Signer Information

  • Signer Name: Display customer's full legal name (e.g., "John Smith")
  • Signing Date: Auto-populated current date (e.g., "12 November 2025")
  • IP Address: Captured for audit trail (displayed partially: "86.134.x.x")
  • Legal Notice: "Your signature will be encrypted and legally binding"

đŸŽŦ Submit Button

  • Button Text: "Submit Signed Agreement"
  • Enabled State: Only enabled when all 4 checkboxes checked AND signature present
  • Visual Feedback: Button turns from gray to gradient blue when enabled
  • Loading State: Shows spinner with "Submitting..." text during API call
  • Success: Redirects to CM10 (Confirmation) after successful submission