by devbyibrahim

Load a PDF form, let the user fill text fields, checkboxes, and radio buttons, then download the filled PDF
TetreesAI backend API — a service for parsing fillable PDF forms and filling them programmatically via a session-based workflow.
npm install
npm run build
npm start
The server listens on port 9016 by default.
npm run dev
Starts the server with hot-reload (e.g. via ts-node-dev or nodemon).
| Variable | Required | Default | Description |
|---|---|---|---|
PORT | No | 9016 | Port the HTTP server listens on |
CLIENT_URL | Yes | — | Allowed CORS origin (e.g. https://app.tetrees.ai) |
Create a .env file in the project root:
PORT=9016
CLIENT_URL=http://localhost:3000
All endpoints are available under two equivalent prefixes:
/api/pdf/<endpoint>/<endpoint>All request bodies must be Content-Type: application/json.
All successful responses return HTTP 200 with a envelope.
All error responses return .
Spin up an isolated sandbox and run it server-side — no local setup.
Push this product straight into your AI IDE, web builder or cloud IDE.
Connect Tetrees to a compatible AI IDE, list products you own, and request the verified ZIP without exposing seller upload controls.
No reviews yet.
Sign in to join the discussion
Loading discussion…
{ "data": ... }{ "error": "...", "code": "..." }Full paths: POST /api/pdf/upload · POST /upload
Accepts a base64-encoded PDF string, parses its AcroForm fields, and opens a server-side session. Returns the session ID, the list of detected form fields (names, types, current values, and options where applicable), and the total page count.
The session is used in the subsequent POST /fill call. Sessions are automatically purged every 15 minutes.
| Field | Type | Required | Description |
|---|---|---|---|
pdf | string | ✅ Yes | Base64-encoded PDF file content. Must be a valid base64 string and a valid PDF. |
filename | string | No | Optional original filename for reference. |
curl -X POST http://localhost:9016/upload \
-H "Content-Type: application/json" \
-d '{
"pdf": "JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwKL0xlbmd0aCAzIDAgUgo+PgpzdHJlYW0K",
"filename": "application-form.pdf"
}'
200 OK{
"data": {
"sessionId": "a3f1c2d4-e5b6-47a8-9c0d-1e2f3a4b5c6d",
"fields": [
{
"name": "firstName",
"type": "text",
"value": ""
},
{
"name": "lastName",
"type": "text",
"value": ""
},
{
"name": "agreeToTerms",
"type": "checkbox",
"value": false
},
{
"name": "country",
"type": "dropdown",
"value": "",
"options": ["USA", "Canada", "UK", "Australia"]
}
],
"pageCount": 2
}
}
| HTTP | code | Cause |
|---|---|---|
| 400 | NO_FILE | pdf field is missing or empty. |
| 400 | INVALID_BASE64 | The string is not valid base64. |
| 400 | BAD_REQUEST | Zod validation failure (e.g. wrong field types). |
| 422 | NOT_PDF | The decoded bytes are not a valid PDF. |
| 422 | NO_FIELDS | The PDF has no AcroForm fields (flat/scanned PDF). |
| 500 | PARSE_FAILED | Unexpected error during PDF parsing. |
# Negative case: expects 400 — missing required pdf field
curl -X POST http://localhost:9016/upload \
-H "Content-Type: application/json" \
-d '{
"filename": "no-pdf-field.pdf"
}'
Full paths: POST /api/pdf/upload · POST /upload
Identical to POST /upload — this is the /api/pdf/ prefixed alias for the same handler. Accepts a base64-encoded PDF string, parses its AcroForm fields, and opens a server-side session.
| Field | Type | Required | Description |
|---|---|---|---|
pdf | string | ✅ Yes | Base64-encoded PDF file content. Must be a valid base64 string and a valid PDF. |
filename | string | No | Optional original filename for reference. |
curl -X POST http://localhost:9016/api/pdf/upload \
-H "Content-Type: application/json" \
-d '{
"pdf": "JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwKL0xlbmd0aCAzIDAgUgo+PgpzdHJlYW0K",
"filename": "application-form.pdf"
}'
200 OK{
"data": {
"sessionId": "b7e2d1f3-a4c5-48b9-8e0f-2d3c4b5a6e7f",
"fields": [
{
"name": "firstName",
"type": "text",
"value": ""
},
{
"name": "lastName",
"type": "text",
"value": ""
},
{
"name": "agreeToTerms",
"type": "checkbox",
"value": false
},
{
"name": "country",
"type": "dropdown",
"value": "",
"options": ["USA", "Canada", "UK", "Australia"]
}
],
"pageCount": 2
}
}
| HTTP | code | Cause |
|---|---|---|
| 400 | NO_FILE | pdf field is missing or empty. |
| 400 | INVALID_BASE64 | The string is not valid base64. |
| 400 | BAD_REQUEST | Zod validation failure (e.g. wrong field types). |
| 422 | NOT_PDF | The decoded bytes are not a valid PDF. |
| 422 | NO_FIELDS | The PDF has no AcroForm fields (flat/scanned PDF). |
| 500 | PARSE_FAILED | Unexpected error during PDF parsing. |
# Negative case: expects 400 — missing required pdf field
curl -X POST http://localhost:9016/api/pdf/upload \
-H "Content-Type: application/json" \
-d '{
"filename": "no-pdf-field.pdf"
}'
Full paths: POST /api/pdf/fill · POST /fill
Accepts a session ID (obtained from a prior POST /upload call) and a map of field names to values. Fills the PDF form fields in-memory and returns the completed PDF as a base64-encoded string along with a suggested filename.
Sessions expire after 15 minutes of inactivity. If the session is not found or has expired, a 404 is returned.
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string | ✅ Yes | UUID of the active session returned by POST /upload. |
values | Record<string, string | boolean> | ✅ Yes | Map of field names to their fill values. Text fields take strings; checkboxes take booleans. |
curl -X POST http://localhost:9016/fill \
-H "Content-Type: application/json" \
-d '{
"sessionId": "a3f1c2d4-e5b6-47a8-9c0d-1e2f3a4b5c6d",
"values": {
"firstName": "Jane",
"lastName": "Doe",
"agreeToTerms": true,
"country": "Canada"
}
}'
200 OK{
"data": {
"pdf": "JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwKL0xlbmd0aCAzIDAgUgo+PgpzdHJlYW0K...",
"filename": "filled-application-form.pdf"
}
}
| HTTP | code | Cause |
|---|---|---|
| 400 | BAD_REQUEST | Zod validation failure (e.g. missing sessionId or values). |
| 404 | SESSION_EXPIRED | Session not found or has expired (older than 15 minutes). |
| 500 | FILL_FAILED | Unexpected error while filling the PDF. |
# Negative case: expects 404 — session not found or expired
curl -X POST http://localhost:9016/fill \
-H "Content-Type: application/json" \
-d '{
"sessionId": "00000000-0000-0000-0000-000000000000",
"values": {
"firstName": "Ghost"
}
}'
# Negative case: expects 400 — missing required values field
curl -X POST http://localhost:9016/fill \
-H "Content-Type: application/json" \
-d '{
"sessionId": "a3f1c2d4-e5b6-47a8-9c0d-1e2f3a4b5c6d"
}'
Full paths: POST /api/pdf/fill · POST /fill
Identical to POST /fill — this is the /api/pdf/ prefixed alias for the same handler. Accepts a session ID and a map of field values, fills the PDF, and returns the completed document as a base64-encoded string.
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string | ✅ Yes | UUID of the active session returned by POST /upload. |
values | Record<string, string | boolean> | ✅ Yes | Map of field names to their fill values. Text fields take strings; checkboxes take booleans. |
curl -X POST http://localhost:9016/api/pdf/fill \
-H "Content-Type: application/json" \
-d '{
"sessionId": "b7e2d1f3-a4c5-48b9-8e0f-2d3c4b5a6e7f",
"values": {
"firstName": "John",
"lastName": "Smith",
"agreeToTerms": true,
"country": "USA"
}
}'
200 OK{
"data": {
"pdf": "JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwKL0xlbmd0aCAzIDAgUgo+PgpzdHJlYW0K...",
"filename": "filled-application-form.pdf"
}
}
| HTTP | code | Cause |
|---|---|---|
| 400 | BAD_REQUEST | Zod validation failure (e.g. missing sessionId or values). |
| 404 | SESSION_EXPIRED | Session not found or has expired (older than 15 minutes). |
| 500 | FILL_FAILED | Unexpected error while filling the PDF. |
# Negative case: expects 404 — session not found or expired
curl -X POST http://localhost:9016/api/pdf/fill \
-H "Content-Type: application/json" \
-d '{
"sessionId": "00000000-0000-0000-0000-000000000000",
"values": {
"firstName": "Ghost"
}
}'
# Negative case: expects 400 — missing required sessionId field
curl -X POST http://localhost:9016/api/pdf/fill \
-H "Content-Type: application/json" \
-d '{
"values": {
"firstName": "John"
}
}'
code | HTTP | Description |
|---|---|---|
NO_FILE | 400 | No PDF file provided. |
INVALID_BASE64 | 400 | The provided string is not valid base64. |
BAD_REQUEST | 400 | Request body failed Zod schema validation. |
NOT_PDF | 422 | The provided file is not a valid PDF. |
NO_FIELDS | 422 | The PDF has no AcroForm fields (scanned or flat PDF). |
SESSION_EXPIRED | 404 | Session not found or has expired. |
FILL_FAILED | 500 | Failed to fill the PDF (unexpected error). |
PARSE_FAILED | 500 | Failed to parse the PDF (unexpected error). |
INTERNAL_ERROR | 500 | An unexpected error occurred. |
CLIENT_URL environment variable| Layer | Technology |
|---|---|
| Framework | React 18 (Vite) |
| Language | TypeScript |
| Styling | Tailwind CSS v4 |
| HTTP Client | Axios |
| Build Tool | Vite |
/upload and /fill endpointsnpm install
npm run dev
Create a .env file in the project root:
VITE_API_BASE_URL=https://your-backend-url
All API calls are made relative to this base URL. The prefix VITE_ is required for Vite to expose the variable to the browser bundle.
npm run build
# Output is written to dist/
The app is organised around a strict separation of concerns. Each layer has one job and does not reach past its boundary.
src/
├── api/ # Raw HTTP calls (Axios) — one file per resource
├── components/ # Presentational React components
│ └── fields/ # Individual field-type renderers (text, checkbox, radio, dropdown)
├── hooks/ # Stateful business logic as custom hooks
├── lib/ # Shared Axios instance / low-level config
├── pages/ # Route-level page components
├── types/ # Shared TypeScript interfaces and enums
└── utils/ # Pure helper functions (no React, no side-effects)
User action
│
▼
usePdfFiller (hook) ← owns all app state (status machine + formData + error)
│
├─► fileToBase64 (util) ← converts File → base64 string before upload
│
├─► pdfApi.ts (api) ← uploadPdf() / fillPdf() / downloadBase64Pdf()
│ │
│ └─► Axios (lib/api.ts) → Backend REST API
│
└─► App.tsx ← reads state, renders the correct UI phase
│
├─► PdfUploader ← drag-and-drop / file-picker, calls handleUpload
└─► FormRenderer ← renders field list, calls handleFill on submit
│
└─► fields/ ← one component per FieldType
usePdfFiller drives the entire UI through a single status value. There are no scattered isLoading booleans or ad-hoc flags.
idle ──upload──► uploading ──success──► ready ──submit──► filling ──success──► done
▲ │ │ │
│ └──error──► error ◄──┘ └──error──► error
└──────────────────────── reset() ──────────────────────────────────────────┘
| Status | What the user sees |
|---|---|
idle | PDF upload dropzone |
uploading | Upload dropzone with loading spinner |
ready | Dynamic form populated with detected fields |
filling | Form with submit button in loading state |
done | Success screen; PDF download triggered automatically |
error | Error banner + option to reset |
Single hook owns all state — usePdfFiller is the only place where status, formData, and error live. Components receive values and callbacks; they never mutate state directly.
API layer is thin and typed — src/api/pdfApi.ts handles Axios calls, unwraps the { success, data } response envelope, and normalises errors into plain Error objects. Nothing above this layer knows about HTTP.
Field rendering is extensible — FormRenderer maps over the fields array returned by the backend. Each FieldType (text, checkbox, radio, dropdown) is handled by its own component inside src/components/fields/, making it trivial to add new field types without touching existing code.
Base64 transport — PDFs are converted to base64 strings client-side (fileToBase64 util) before upload, and the filled PDF is returned as base64 and decoded back to a Blob for download. This keeps the API surface to plain JSON with no multipart form handling.
Error normalisation — extractError in pdfApi.ts inspects Axios errors and maps common backend signals (session expiry, 404, "not found") to user-friendly messages before they surface in the UI.
| File | Responsibility |
|---|---|
src/App.tsx | Root component; renders the correct UI phase based on status |
src/hooks/usePdfFiller.ts | All app state and async orchestration |
src/api/pdfApi.ts | uploadPdf, fillPdf, downloadBase64Pdf |
src/lib/api.ts | Shared Axios instance with baseURL from env |
src/components/PdfUploader.tsx | File input / drag-and-drop UI |
src/components/FormRenderer.tsx | Renders the field list and submit button |
src/components/fields/ | Per-type field components |
src/types/pdf.types.ts | PdfField, ParsedFormResponse, FillResponse |
src/utils/fileToBase64.ts | File → base64 conversion utility |
src/pages/HomePage.tsx | Top-level page wrapper |
The sandbox audition completed and the detected runnable path passed.
This Express backend / api completed archive review with strong static results. Structure, dependency manifests, documentation, functional source, and common risk patterns were checked by the Tetrees verification pipeline; runtime phases are stated separately. Final verified scores after isolated runtime evidence: overall 8.8 and security 9.
Deterministic AVCP artifact review
Pipeline avcp-2026-08-04.1 · SHA-256 52927b4ec78b7ab1…
This version-scoped review deterministically inspects the submitted archive for structure, dependencies, documentation, functional source, and common malicious or high-risk signals. Build and test phases are reported as passed only after an isolated sandbox audition. It is not a guarantee of perfect security.
Reviewed Aug 7, 2026
The full install guide and integration prompts unlock after purchase.

Game Source Code & Interactive Templates
$6