Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
Developer Handover Pack
Document Management and Filing
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document gives the FullSpec build team everything needed to construct, connect, and validate the Document Management and Filing automation. It covers the current-state step map with bottlenecks identified, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. The process owner side is responsible for confirming Drive folder structure, supplying sample documents for classifier tuning, and granting API credentials before build begins. FullSpec handles all build, integration, testing, and handover work.
01Process snapshot
02Agent specifications
Reads the content of an incoming file attachment and its associated email context (subject line, sender address, email body) to determine the document type, extract the relevant party name, and identify the document date. When a DocuSign trigger fires, the agent reads the envelope metadata and completed PDF in place of email context. It produces a structured metadata object that the Filing and Routing Agent consumes. If classification confidence falls below the configured threshold, the agent routes to a Slack exception alert rather than passing metadata downstream. Estimated build time: 8 hours. Complexity: Moderate.
// Input gmail.message.id : string // unique message identifier gmail.message.subject : string // email subject line gmail.message.from : string // sender address gmail.message.body_text : string // plain-text email body gmail.attachment.filename : string // original filename gmail.attachment.mime_type: string // e.g. application/pdf gmail.attachment.content : base64 // raw file content docusign.envelope_id : string // present when trigger is DocuSign docusign.envelope_status : string // expected value: 'completed' docusign.completed_pdf : base64 // signed document content // Output (on confident classification) metadata.document_type : string // e.g. 'Invoice', 'Contract', 'HR Form', 'Compliance' metadata.party_name : string // e.g. 'Acme Supplies Ltd' metadata.document_date : string // ISO 8601, e.g. '2025-06-15' metadata.confidence_score : float // 0.0 to 1.0; threshold >= 0.75 to proceed metadata.source : string // 'gmail' or 'docusign' metadata.raw_filename : string // original filename passed downstream metadata.file_content : base64 // attachment content passed downstream // On low confidence (score < 0.75) exception.flag : boolean // true exception.reason : string // human-readable explanation for Slack alert exception.raw_filename : string exception.file_content : base64
- Gmail API tier requirement: Gmail must be on Google Workspace (Business Starter or above) to support the Gmail watch/push notification endpoint. Free Gmail accounts do not support server-side push and will require polling instead, which adds latency.
- DocuSign trigger: Use the DocuSign Connect webhook (available on Standard plan and above). Confirm admin-level API credentials and Connect configuration access before build starts. Without admin access the webhook cannot be registered.
- AI classification model: Use an LLM call (prompt-engineered) with the file content and email context as input. The prompt must include a defined list of accepted document_type values agreed with the operations team before build. Do not allow open-ended type inference.
- Confidence threshold: The default confidence threshold is 0.75. This value must be exposed as a configurable parameter in the workflow so the operations team can adjust it without a code change.
- Dedupe behaviour: Cache the gmail.message.id and docusign.envelope_id of each processed document in the error/state log. On re-trigger (e.g. Gmail webhook fires twice for the same message), check the cache and skip processing if the ID has already been handled.
- Fallback on extraction failure: If the file content cannot be parsed (e.g. corrupted PDF, zero-byte attachment), the agent must route immediately to the exception Slack alert and log the failure. It must never pass empty metadata downstream.
- Sample document requirement: At least 30 labelled sample documents covering all expected document types must be provided before the prompt can be tuned. Confirm availability with the operations team during Discovery.
Receives the structured metadata object from the Document Classification Agent, constructs the standardised file name using the agreed naming convention, uploads the renamed file to the correct subfolder in Google Drive, creates a log entry in the Notion document register, and sends a Slack confirmation message to the relevant document owner. This agent also handles the exception branch: when the Classification Agent flags a low-confidence result, this agent posts a Slack alert to the Operations Coordinator with the original file attached and the reason for flagging, without filing anything automatically. Estimated build time: 10 hours. Complexity: Moderate.
// Input (standard path) metadata.document_type : string // from Classification Agent metadata.party_name : string metadata.document_date : string // ISO 8601 metadata.confidence_score : float metadata.source : string metadata.raw_filename : string metadata.file_content : base64 // Input (exception path) exception.flag : boolean // true exception.reason : string exception.raw_filename : string exception.file_content : base64 // Output (standard path) drive.file_name : string // e.g. 'AcmeSupplies_Invoice_2025-06-15.pdf' drive.folder_path : string // e.g. '/Clients/Acme Supplies/Invoices/2025' drive.file_id : string // Google Drive file ID of uploaded document drive.file_url : string // shareable Drive link notion.page_id : string // ID of created Notion log entry notion.database_id : string // target Notion database slack.channel_id : string // channel or DM where confirmation posted slack.message_ts : string // Slack message timestamp // Output (exception path) slack.alert_channel : string // Operations Coordinator channel or DM slack.alert_message : string // includes filename, exception.reason, and file attachment slack.alert_ts : string
- Google Drive folder mapping: A folder mapping table must be built and confirmed before this agent goes live. The table maps each document_type value to a Drive folder ID. Using folder IDs (not path strings) avoids breakage if folders are renamed. Store the mapping as a configuration object in the workflow, not hardcoded in the step logic.
- Naming convention format: The agreed standard is PartyName_DocumentType_YYYY-MM-DD (e.g. AcmeSupplies_Invoice_2025-06-15). Spaces in party names must be stripped or replaced with underscores. Special characters must be sanitised before the filename is constructed.
- Notion schema: The Notion document register database must include the following fields before integration: File Name (title), Document Type (select), Party Name (text), Document Date (date), Drive Link (url), Filed At (date, auto-populated), Source (select: gmail/docusign), Confidence Score (number). Confirm this schema is in place with the operations team before the Filing Agent build begins.
- Slack owner routing: A lookup table mapping document_type to the Slack user ID of the relevant owner must be agreed and configured. For example, invoices route to the Finance Coordinator; HR forms route to the HR lead. This table must be populated before go-live.
- Google Drive OAuth scope: Requires https://www.googleapis.com/auth/drive.file at minimum. If the build needs to browse existing folder structure programmatically, https://www.googleapis.com/auth/drive.readonly is also required. Confirm scopes with the workspace admin.
- Notion integration token: Use an internal Notion integration token scoped to the document register database only. Do not use a full-workspace token. The token must be stored in the shared credential store, not in the workflow step configuration directly.
- Idempotency on Drive upload: Before uploading, query the target folder for an existing file with the same constructed name. If a match exists, append a suffix (_v2, _v3) rather than overwriting silently. Log the duplicate detection event in the error table.
03End-to-end data flow
// ─────────────────────────────────────────────────────────────────
// TRIGGER: New document event detected
// ─────────────────────────────────────────────────────────────────
EVENT_SOURCE: 'gmail' | 'docusign'
IF EVENT_SOURCE == 'gmail':
gmail.message.id -> dedupe_cache.check(message.id)
gmail.message.subject -> classification_agent.input.subject
gmail.message.from -> classification_agent.input.from_email
gmail.message.body_text -> classification_agent.input.body_text
gmail.attachment.filename -> classification_agent.input.raw_filename
gmail.attachment.mime_type-> classification_agent.input.mime_type
gmail.attachment.content -> classification_agent.input.file_content
IF EVENT_SOURCE == 'docusign':
docusign.envelope_id -> dedupe_cache.check(envelope_id)
docusign.envelope_status -> assert('completed')
docusign.completed_pdf -> classification_agent.input.file_content
docusign.envelope_id -> classification_agent.input.raw_filename
// ─────────────────────────────────────────────────────────────────
// AGENT HANDOFF 1: Classification Agent processes the document
// ─────────────────────────────────────────────────────────────────
classification_agent.input.file_content -> llm_prompt.document_body
classification_agent.input.body_text -> llm_prompt.email_context
classification_agent.input.from_email -> llm_prompt.sender_hint
llm_prompt.response -> parse:
metadata.document_type : string // 'Invoice' | 'Contract' | 'HR Form' | 'Compliance' | ...
metadata.party_name : string
metadata.document_date : string // ISO 8601
metadata.confidence_score : float // 0.0 – 1.0
// ──────────────────────────────────────────────��──────────────────
// DECISION: Confidence threshold check
// ─────────────────────────────────────────────────────────────────
IF metadata.confidence_score >= 0.75:
-> STANDARD PATH: pass to Filing and Routing Agent
ELSE:
exception.flag = true
exception.reason = llm_prompt.low_confidence_explanation
exception.raw_filename= classification_agent.input.raw_filename
exception.file_content= classification_agent.input.file_content
-> EXCEPTION PATH: pass to Filing and Routing Agent (exception branch)
// ─────────────────────────────────────────────────────────────────
// AGENT HANDOFF 2: Filing and Routing Agent — standard path
// ─────────────────────────────────────────────────────────────────
// Step A: Construct standardised file name
metadata.party_name -> sanitise(strip_spaces, remove_special_chars)
metadata.document_type -> sanitise()
metadata.document_date -> format('YYYY-MM-DD')
drive.file_name = '{party_name}_{document_type}_{document_date}.pdf'
// e.g. 'AcmeSupplies_Invoice_2025-06-15.pdf'
// Step B: Resolve target Drive folder
folder_map[metadata.document_type] -> drive.folder_id
// folder_map is a config object: { 'Invoice': '1BxK...', 'Contract': '2CyL...' }
// Step C: Dedupe check before upload
drive.list_files(folder_id, query: drive.file_name)
IF match_found: drive.file_name += '_v{n}'
// Step D: Upload file to Google Drive
metadata.file_content -> drive.upload(
name : drive.file_name,
parent : drive.folder_id,
mimeType : 'application/pdf'
)
drive.file_id <- upload_response.id
drive.file_url <- upload_response.webViewLink
// Step E: Create Notion log entry
notion.create_page(
database_id : notion.database_id,
'File Name' : drive.file_name,
'Document Type': metadata.document_type,
'Party Name' : metadata.party_name,
'Document Date': metadata.document_date,
'Drive Link' : drive.file_url,
'Filed At' : now(),
'Source' : metadata.source,
'Confidence' : metadata.confidence_score
)
notion.page_id <- create_page_response.id
// Step F: Send Slack confirmation to document owner
owner_map[metadata.document_type] -> slack.channel_id
slack.post_message(
channel : slack.channel_id,
text : 'Filed: {drive.file_name} | Folder: {drive.folder_path} | {drive.file_url}'
)
slack.message_ts <- post_message_response.ts
// ─────────────────────────────────────────────────────────────────
// AGENT HANDOFF 2 (exception branch): Filing and Routing Agent
// ─────────────────────────────────────────────────────────────────
slack.post_message(
channel : ops_coordinator.slack_user_id,
text : 'Unclassified document requires manual review.',
attachments : exception.file_content,
fields : { filename: exception.raw_filename, reason: exception.reason }
)
error_log.insert(
event_type : 'classification_exception',
filename : exception.raw_filename,
reason : exception.reason,
timestamp : now()
)
// ─────────────────────────────────────────────────────────────────
// END OF FLOW
// ─────────────────────────────────────────────────────────────────04Recommended build stack
More documents for this process
Every document generated for Document Management & Filing.