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
GET /health
gemini-3.5-flash via Gemini API or Vertex AI
google-antigravity - the official Antigravity SDK harness
purchase_orders audit_logs disputes
gs://documa-receipts-bucket
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.
| PO | Vendor | Lines | Subtotal | Max tax | Approved total |
|---|---|---|---|---|---|
| PO-9921 | Acme Industrial Tech | 2 | $3,050.00 | $200.00 | $3,250.00 |
| PO-8810 | Global 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.
| Condition | Audit status | Action taken | Human |
|---|---|---|---|
| No discrepancies and |variance| <= $1 | APPROVED | AUTO_APPROVED_PAYOUT | no |
| Discrepancies, variance <= $500, nothing unauthorized | DISCREPANCY_DETECTED | GENERATED_DISCREPANCY_REPORT | no |
| Variance > $500 or any unauthorized item | REQUIRES_HUMAN_APPROVAL | ESCALATED_TO_HUMAN_FINANCE | yes |
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 URIPOST /api/audit/uploadMultipart upload, then auditPOST /api/events/gcsEventarc object.finalized handlerGET /api/poList purchase ordersPOST /api/poCreate or update a purchase orderGET /api/audit/logsAudit historyGET /api/disputesDispute reportsPOST /api/disputes/{id}/approveRecord a human finance decisionGET /api/disputes/{id}/export/pdfPrintable vendor dispute noticeGET /api/audit/export/csvERP-compatible CSV for SAP or QuickBooksAuditing 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.