Welcome to Documa

Documa is an autonomous multimodal audit and procurement fleet. It reads vendor documents - scanned receipts, invoice images, multi-page PDFs - audits them line by line against your contracted purchase orders, and then clears, disputes, or escalates each one.

The agents are not a chat feature bolted onto an invoice tool. Three of them run the audit end to end and decide the outcome themselves: the fleet reads, reconciles, disputes, and clears the documents. Every step is logged, and every decision is traceable to the line item that caused it.

  • Compliant invoices - clear for payout automatically
  • Minor disputes - drafted and dispatched by the fleet alone
  • Genuine exceptions - and only those - reach a human signature

Where everything lives

Live deployment documa-fleet · Cloud Run running
Landing page /
App dashboard /app
Documentation /docs
Health check GET /health
Model gemini-3.5-flash via Gemini API or Vertex AI
Agent framework google-antigravity - the official Antigravity SDK harness
Cloud services Cloud Run, Firestore, Cloud Storage, Eventarc
Firestore collections purchase_orders audit_logs disputes
Document bucket gs://documa-receipts-bucket
License Apache 2.0

How It Works

A document enters Documa one of two ways: dropped into Cloud Storage, or posted to the API. The Cloud Storage path is the autonomous one - nobody clicks anything.

Cloud Storage (document lands)
  -> Eventarc  object.finalized
  -> Cloud Run worker
  -> MultimodalVisionAgent   extracts the document
  -> ContractAuditorAgent    reconciles against the PO
  -> DiscrepancyDispatcher   decides the outcome
  -> Firestore               audit log + dispute record

The three agents run as a sequential pipeline. Each hands typed state to the next through a shared AgentState, and the orchestrator records every step in an execution log that is returned with the response.

Gemma Pre-flight Triage

Every document reaching the fleet costs a full Gemini 3.5 Flash multimodal extraction, whether it is a vendor invoice or a photograph someone dropped into the bucket by mistake. An open Gemma model screens each document first and answers one narrow question: is this a procurement document at all?

A confident negative declines the document before the expensive call is made. The result is returned as UNKNOWN at $0.00 with a triage_note explaining why, and the audit continues to escalation rather than inventing figures.

Advisory by design

Triage never blocks an audit. If Gemma is unavailable, unreachable, or returns anything unparseable, the document proceeds to vision exactly as it would without triage. Only a confident negative short-circuits the pipeline.

Configure with DOCUMA_TRIAGE_MODEL (defaults to gemma-4-26b-a4b-it-maas) or turn it off entirely with DOCUMA_DISABLE_TRIAGE.

The Three Agents

1. MultimodalVisionAgent

Reads the document bytes from Cloud Storage or local disk and extracts a structured invoice: vendor, address, invoice number, PO reference, date, line items, subtotal, tax, grand total, currency, and whether a signature is present. Runs Gemini 3.5 Flash with a Pydantic response schema, so the output is schema-enforced rather than parsed out of prose.

2. ContractAuditorAgent

Fetches the matching purchase order from Firestore and reconciles each billed line against its contracted rate. Lines are matched by item_code first, then by description. Produces an itemized variance carrying the exact dollar impact per line.

3. DiscrepancyDispatcherAgent

Turns the audit into one of three Markdown documents - a payout authorization, a formal vendor price-discrepancy notice with the credit-memo amount already calculated, or a finance escalation alert with reject-or-approve controls.

Antigravity Harness

Documa's agents are synchronous while the Antigravity SDK is async-only, so the SDK is wrapped behind a synchronous BaseAgent interface. The bridge runs coroutines from sync callers and works both off the event loop - CLI, tests, FastAPI def endpoints - and on it, where a bare asyncio.run would raise.

Model access resolves from the environment: a Gemini API key, or Vertex AI when GOOGLE_GENAI_USE_VERTEXAI is set with a project.

Purchase Orders

A purchase order is the contracted baseline an invoice is judged against. It carries approved unit prices per line, a maximum allowed tax, and an approved grand total. Two sample orders are seeded on startup.

POVendorLinesSubtotalMax taxApproved total
PO-9921Acme Industrial Tech 2$3,050.00$200.00$3,250.00
PO-8810Global Logistics Corp 1$1,300.00$100.00$1,400.00

PO-9921 approves 10 x Dell UltraSharp 27-inch Monitor at $180.00 and 5 x Ergonomic Executive Office Chair at $250.00. Every demo scenario is measured against it.

Discrepancy Types

  • OVERCHARGE - the billed unit price exceeds the contracted rate. Variance is the per-unit difference times the billed quantity.
  • QUANTITY_MISMATCH - the billed quantity exceeds the authorized quantity. Variance is the excess quantity at the approved rate.
  • UNAUTHORIZED_ITEM - a line no purchase order ever approved. The whole line total is the variance.
  • MISSING_PO_RECORD - no purchase order matches the reference at all. The full billed amount is treated as unverified and escalated.

Decision Thresholds

The $500 variance threshold is Documa's autonomy boundary. Below it the fleet resolves the dispute itself; above it, or when an unauthorized charge appears, it defers to a person. Documa knows the limit of its own authority.

ConditionAudit statusAction takenHuman
No discrepancies and |variance| <= $1 APPROVEDAUTO_APPROVED_PAYOUTno
Discrepancies, variance <= $500, nothing unauthorized DISCREPANCY_DETECTEDGENERATED_DISCREPANCY_REPORTno
Variance > $500 or any unauthorized item REQUIRES_HUMAN_APPROVALESCALATED_TO_HUMAN_FINANCEyes

A finance manager's ruling is recorded separately as a HumanDecision of REJECT_OVERCHARGE or APPROVE_EXCEPTION. It never overwrites action_taken, which stays the fleet's own account of what it dispatched.

HTTP API

POST /api/audit/processAudit a document by path or GCS URI
POST /api/audit/uploadMultipart upload, then audit
POST /api/events/gcsEventarc object.finalized handler
GET /api/poList purchase orders
POST /api/poCreate or update a purchase order
GET /api/audit/logsAudit history
GET /api/disputesDispute reports
POST /api/disputes/{id}/approveRecord a human finance decision
GET /api/disputes/{id}/export/pdfPrintable vendor dispute notice
GET /api/audit/export/csvERP-compatible CSV for SAP or QuickBooks

Auditing a document

curl -X POST http://localhost:8085/api/audit/process \
  -H 'Content-Type: application/json' \
  -d '{
    "document_id": "DOC-MINOR-404",
    "file_path_or_url": "receipts/minor_overcharge_invoice.png",
    "po_number_override": "PO-9921"
  }'

The response carries the extracted document, the audit result with its itemized discrepancies, the dispatched report, and the full execution log.

Eventarc Intake

The autonomous path. A file landing in the bucket emits object.finalized, Eventarc delivers it to the Cloud Run service, and the fleet audits it without any user interaction.

gsutil cp overcharged_invoice.png gs://documa-receipts-bucket/

The handler derives a document id from the object name and resolves the document over gs://. The same payload shape can be posted directly to /api/events/gcs for local testing.

Self-Hosting & Deployment

Run locally

python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
export GEMINI_API_KEY="your-key"      # optional; see Configuration
PYTHONPATH=. uvicorn documa.server:app --port 8085

Open the landing page or the dashboard. Without credentials Documa still runs: Firestore and Cloud Storage fall back to in-memory and local-disk equivalents.

Run the agent fleet from the CLI

PYTHONPATH=. python main.py        # runs all four scenarios
PYTHONPATH=. pytest tests/test_audit_fleet.py -v

Deploy to Cloud Run

docker build -t documa .
./deploy.sh                        # enables APIs, builds, deploys

The container is python:3.11-slim and listens on port 8080. deploy.sh reads GOOGLE_CLOUD_PROJECT and GCP_REGION, defaulting to documa-hackathon and us-central1.

Configuration

GEMINI_API_KEYGemini API key. GOOGLE_API_KEY is accepted as an alias.
GOOGLE_GENAI_USE_VERTEXAISet truthy to route through Vertex AI instead of the Gemini API.
GOOGLE_CLOUD_PROJECTGCP project for Firestore and Vertex AI. Defaults to documa-hackathon.
GOOGLE_CLOUD_LOCATIONModel location. Use global for Gemini 3.x - regional endpoints return 404.
GCS_BUCKET_NAMEDocument bucket. Defaults to documa-receipts-bucket.
DOCUMA_STRICT_MODESet truthy to make a failed or unavailable extraction raise instead of falling back.
DOCUMA_TRIAGE_MODELGemma model used for pre-flight triage. Defaults to gemma-4-26b-a4b-it-maas on Vertex.
DOCUMA_DISABLE_TRIAGESet truthy to skip Gemma triage entirely.

Recording a demo

Run with DOCUMA_STRICT_MODE=true. Documa then refuses to fall back to simulated fixtures, so every figure on screen is provably a live extraction.

Guardrails & Provenance

An invoice is untrusted input

The Antigravity local harness enables filesystem and shell tools by default. For an agent whose input is a third-party document that is an injection surface, so Documa disables all twelve non-terminal tools behind a deny-all policy - the vision agent can only perform inference. The system prompt also instructs the model to treat document text as data, never as instructions.

Every extraction is labelled

Each result records an extraction_mode, surfaced in the API and shown in the dashboard as a badge:

  • ANTIGRAVITY_GEMINI - a real Gemini vision call
  • SIMULATED_FALLBACK - an offline demo fixture, not extraction

A document matching no fixture is reported as UNKNOWN at $0.00 with zero confidence, rather than being given invented invoice data. Documa would rather fail visibly than return a number it did not read.

Not production-hardened

This is a hackathon build. CORS is open to all origins and no endpoint carries authentication, including the finance override. Put it behind your own auth before pointing it at real procurement data.

FAQ

Does it need a Gemini API key to run?

No. Without one the fleet runs on offline fixtures so the pipeline is fully demonstrable - but nothing is actually read from the document. Set a key for real extraction, and DOCUMA_STRICT_MODE to guarantee it.

What happens if no purchase order matches?

The audit returns REQUIRES_HUMAN_APPROVAL with a MISSING_PO_RECORD discrepancy covering the full billed amount. Documa never clears a payout it cannot justify against a contract.

Can a human override the fleet?

Yes, on escalated reports - either reject the overcharge and pay the contracted basis, or approve the exception and release the full amount. The ruling is stored alongside the agent's decision, never in place of it.

Which Google services does it use?

Gemini 3.5 Flash for vision, the Antigravity SDK as the agent framework, and Cloud Run, Firestore, Cloud Storage and Eventarc for execution, state, intake and triggering.

Where do uploaded documents go?

Into receipts/ on the service, with the filename stripped to its basename so an upload cannot escape that directory. The repository ignores everything there except the demo fixtures, since uploads may contain personal or customer data.