Requirement objects — each with a type, confidence score, source page number, and a verbatim snippet from the original document. The result is a machine-readable compliance checklist that you can immediately validate a vendor proposal against.
What Gets Extracted
Every requirement extracted from a tender document is represented by theRequirement entity. Each field is populated by the AI before being persisted in the database.
AI Extraction Pipeline
The extraction engine uses Google Gemini 2.5 Flash via the Genkit framework. Gemini is prompted to adopt the Legal & Technical Auditor persona with strict instructions:- Focus on imperatives. The prompt instructs the model to look specifically for phrases like
"deberá","será obligatorio","se requiere","es indispensable","must", and"shall"— the classic markers of exclusionary clauses in public procurement documents. - Ignore filler text. Introductory paragraphs, general descriptions, and non-binding commentary are explicitly excluded from extraction.
- Output in Spanish. All extracted requirement text and reasoning is returned in Spanish regardless of the source language, consistent with the platform’s target market.
- Assign confidence deterministically. A clear mandate (
"deberá") receives confidence1.0; a desirable criterion receives0.5.
TECHNICAL, ADMINISTRATIVE, LEGAL, or FINANCIAL — which the frontend maps to the broader MANDATORY / OPTIONAL / UNKNOWN display types.
All Gemini calls are traced end-to-end using LangSmith via the
traceable SDK wrapper. You can inspect prompt inputs, outputs, latency, and token usage in the LangSmith dashboard if you have observability configured.Large PDF Support
For tender documents longer than 15 pages (LARGE_PDF_THRESHOLD), TenderCheck AI automatically switches from single-pass analysis to a parallelised chunked pipeline.
The pipeline works as follows:
analyzeChunks dispatches all chunks to Gemini simultaneously using Promise.all. The CHUNK_PARALLEL_PROCESSING constant (3) is available in the codebase for future windowed-concurrency tuning but the current implementation sends every chunk in one parallel wave.
Each chunk preserves absolute page numbers (--- PAGE X --- markers) so that source.pageNumber on every extracted requirement refers to the page in the original full document, not the chunk.
If a single chunk fails (e.g., due to a transient AI error), the pipeline logs a warning and continues processing the remaining chunks — the analysis will complete as long as at least one chunk yields requirements.
Supported File Format
The upload component enforces the
application/pdf MIME type client-side before the file reaches the backend.
How to Analyze a Tender
1
Log in to TenderCheck AI
Sign in with your email and password, or use Google Sign-In (OAuth 2.0 with PKCE). You must be authenticated to submit an analysis.
2
Open the dashboard and click Analyze Tender
From the main dashboard, click the Analyze Tender upload zone (labeled Pliego). You can click to open a file picker or drag and drop a PDF directly onto the zone.
3
Select your tender PDF
Choose a PDF up to 50 MB with a machine-readable text layer. Once selected, the upload zone shows a ✅ confirmation with the filename.
4
Optionally select an industry
Use the industry dropdown to scope the analysis to a specific sector (e.g., Digital Services, Construction). This enables the Industry Scope Validation rule. The field is optional — if omitted, Digital Services is used as the default.
5
Submit and wait for results
Click Analyze. The backend parses the PDF, runs the Gemini extraction pipeline (chunked if > 15 pages), generates vector embeddings for each requirement, and saves the analysis. Processing time depends on document length.
6
Review extracted requirements with page citations
Each requirement card shows its full text, type badge, confidence, keywords, and a clickable page badge (e.g., Pág. 3). Clicking the page badge opens the Citation Preview modal, which displays the full page text with the AI-identified fragment highlighted.
Analysis Status
EveryTenderAnalysis record moves through a defined lifecycle tracked by AnalysisStatus.
The current status is displayed as a badge on the analysis header card in the dashboard. A
FAILED analysis can be retried by uploading the document again.