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 Retention and Archiving
[YourCompany.com] · Legal Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for the Document Retention and Archiving automation build. It covers the full current-state process map, both agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The FullSpec team uses this pack to configure, connect, and validate every component. No third-party developer or external agency is involved. All build and integration work is handled by FullSpec.
01Process snapshot
02Agent specifications
Reads each incoming document and assigns a document type from the defined taxonomy, matches it to the correct retention schedule entry, and extracts key metadata including matter number, responsible party, retention period, and calculated destruction date. Outputs a structured metadata record that is written directly to the file in SharePoint and logged to the retention register in Microsoft 365. This agent handles the most error-prone portion of the current manual process by enforcing a single, consistent ruleset at the exact moment of document ingestion, eliminating the variability introduced by different staff members interpreting documents under time pressure. Estimated build time: 16 hours. Complexity: Complex.
// Input document_source: 'sharepoint' | 'dropbox' | 'docusign' document_id: string // native file ID from source system document_url: string // direct download or preview URL received_at: ISO8601 // timestamp of upload or envelope completion matter_reference: string // extracted from filename or envelope metadata where available // Processing // Agent reads document content (text extraction via Microsoft 365 or OCR fallback) // Matches content against embedded document taxonomy ruleset // Resolves document_type -> retention_period from structured ruleset table // Calculates destruction_date = received_at + retention_period_days // Output document_type: string // e.g. 'NDA', 'Employment Contract', 'Court Correspondence' taxonomy_code: string // internal taxonomy identifier, e.g. 'LEGAL-NDA-001' retention_period_days: int // e.g. 2555 (7 years) destruction_date: ISO8601 // calculated end of retention period matter_reference: string // confirmed or extracted matter number responsible_party: string // derived from matter reference or envelope signer archive_folder_path: string // resolved destination path in SharePoint or Dropbox register_row_id: string // row ID written to Microsoft 365 retention register classification_confidence: float // 0.0 to 1.0; below 0.80 triggers human review flag ocr_applied: boolean // true if document required OCR pre-processing
- SharePoint connection requires a service account with Site Member permissions on all monitored document libraries. Do not use a named user account as the connector identity.
- Dropbox Business connection requires a connected app with files.content.read and files.content.write scopes. The monitored folder path must be confirmed before build and stored as a configuration variable, not hardcoded.
- DocuSign trigger requires a Connect webhook subscription on the completed envelope event. The envelope must include a custom field named matter_reference; if absent, the agent falls back to filename parsing and logs a warning.
- The document taxonomy ruleset must be supplied as a structured JSON or spreadsheet before the agent build begins. If the existing retention schedule is inconsistent or incomplete, a rules-cleanup exercise is required first and will extend the build timeline.
- If classification_confidence falls below 0.80, the document is placed in a designated review folder in SharePoint and a Slack alert is sent to the legal administrator. No metadata is written until a human confirms the classification.
- Scanned documents with poor resolution require OCR pre-processing. The Microsoft 365 document intelligence endpoint handles this where file quality permits. Unreadable files are quarantined with an error flag and must not be silently skipped.
- Dedupe logic: before writing a register row, the agent checks for an existing register entry matching document_id. If a duplicate is found, the run is skipped and logged with status 'duplicate_skipped'. This prevents double-entries from webhook retries.
- The retention ruleset must be versioned. Each ruleset version should carry a valid_from date. The agent always uses the ruleset version active on received_at, not the current date at processing time.
- Confirm that Microsoft 365 plan in use supports the Lists API or SharePoint REST API for register writes. SharePoint Online with a Business or Enterprise plan is required. SharePoint on-premises is not supported without an API gateway.
Runs on a daily schedule and sweeps the full retention register in Microsoft 365 to identify every document whose destruction_date is on or before today's date. For each match it composes a structured Slack notification addressed to the correct legal manager, including document name, type, matter reference, destruction date, and the number of days the hold period has been served. The notification contains an approval prompt. Human approval is mandatory before any destructive action occurs. This agent replaces the monthly manual audit and the manual notification step, reducing the window between a destruction date passing and the responsible manager being informed from up to 31 days to under 24 hours. Estimated build time: 12 hours. Complexity: Moderate.
// Input run_date: ISO8601 // today's date at execution time (UTC) register_query_filter: string // destruction_date <= run_date AND status = 'active' // Processing // Agent queries Microsoft 365 retention register via Lists API // Returns all rows where destruction_date <= run_date and status != 'destroyed' and status != 'deferred' // For each matched row, resolves responsible_legal_manager Slack user ID from matter_reference lookup // Constructs Slack Block Kit message with document_name, document_type, matter_reference, // destruction_date, days_overdue, sharepoint_link, and two action buttons: Approve / Defer // Sends one message per document to the resolved Slack channel or DM // Writes alert_sent_at timestamp to register row to prevent duplicate alerts on subsequent runs // Output (per flagged document) document_id: string // register row identifier document_name: string document_type: string matter_reference: string destruction_date: ISO8601 days_overdue: int // run_date minus destruction_date in days responsible_manager_slack_id: string slack_message_ts: string // Slack message timestamp for threading approval response alert_sent_at: ISO8601 // written back to register row // On approval (Slack interactive callback) approval_action: 'approve' | 'defer' approver_slack_id: string approved_at: ISO8601 // If approve: register row status set to 'pending_destruction', destruction_confirmed_by written // If defer: register row status set to 'deferred', deferred_until date optionally supplied // Outcome logged to register immediately; no file is deleted by this agent
- Slack app must have chat:write, users:read, and interactive_components (interactivity) scopes enabled. The app must be installed to the legal workspace and invited to any private channels used for alerts.
- Slack interactive callback URL must be registered in the Slack app manifest and routed to the automation platform's inbound webhook endpoint before go-live. Test the callback in sandbox before switching to production.
- The retention register must have a consistent schema before this agent is built. Confirm column names for destruction_date, status, responsible_manager, and alert_sent_at with the process owner. Any schema change after build requires a workflow update.
- Responsible manager routing: the agent resolves the correct Slack recipient from the matter_reference field. A lookup table mapping matter prefixes to Slack user IDs must be maintained in the automation platform's configuration store. Confirm with Rachel Osei (Legal Operations Manager) that this mapping is complete before go-live.
- If no Slack user ID can be resolved for a document, the alert falls back to a designated legal-alerts channel rather than a DM. Log the fallback as a warning for the process owner to review.
- Deferrals must include an updated destruction_date. If a deferral response contains no new date, the agent sets deferred_until to run_date plus 30 days and logs a note. The document will re-surface on the next daily run after the deferred date.
- The daily schedule should be confirmed against any Microsoft 365 List API rate limits. At 60 documents per month, the register will grow to several hundred rows within a year. Pagination must be implemented from day one.
- This agent does not execute destruction. It only surfaces documents and captures approval intent. The actual file deletion step is outside the scope of this automation and remains a human action. This must be documented clearly in the SOP and confirmed with the process owner before launch.
03End-to-end data flow
// ============================================================
// DOCUMENT RETENTION AND ARCHIVING: END-TO-END DATA FLOW
// ============================================================
// TRIGGER SOURCES (three parallel entry points)
sharepoint_upload_event -> {
document_id,
document_url,
document_source: 'sharepoint',
received_at: timestamp,
uploader_email
}
dropbox_webhook_event -> {
document_id,
document_url,
document_source: 'dropbox',
received_at: timestamp,
folder_path
}
docusign_envelope_completed_webhook -> {
envelope_id,
document_url: signed_document_download_url,
document_source: 'docusign',
received_at: envelope_completed_at,
matter_reference: custom_field['matter_reference'] || null,
signer_name,
signer_email
}
// --- HANDOFF TO: Document Classification Agent ---
// STEP 1: Text extraction
document_content_raw = extract_text(document_url)
// If extract_text returns null or low_quality:
ocr_result = microsoft365_document_intelligence(document_url)
document_content_raw = ocr_result.text
ocr_applied = true
// If ocr_result.confidence < 0.60: quarantine_file(); log_error('unreadable'); halt()
// STEP 2: Taxonomy classification
classification_result = taxonomy_ruleset.match(document_content_raw)
// classification_result fields:
// document_type: string e.g. 'NDA'
// taxonomy_code: string e.g. 'LEGAL-NDA-001'
// retention_period_days: int e.g. 2555
// classification_confidence: float
// STEP 3: Confidence gate
if classification_confidence < 0.80:
move_to_review_folder(sharepoint_review_path)
slack_alert(legal_admin_slack_id, 'classification_review_required', document_id)
log_register_row(status: 'pending_review')
halt()
// STEP 4: Metadata resolution
destruction_date = received_at + retention_period_days
matter_reference = matter_reference || parse_filename(document_id)
responsible_party = matter_lookup_table[matter_reference].owner
archive_folder_path = folder_routing_table[taxonomy_code][matter_reference]
// STEP 5: Write metadata to SharePoint
sharepoint.set_metadata(document_id, {
document_type,
taxonomy_code,
retention_period_days,
destruction_date,
matter_reference,
responsible_party,
classified_at: now()
})
// STEP 6: Move to archive folder
if document_source == 'dropbox':
dropbox.move(document_id, archive_folder_path)
else:
sharepoint.move(document_id, archive_folder_path)
// STEP 7: Write retention register row
register_row_id = microsoft365_list.create_row({
document_id,
document_name,
document_type,
taxonomy_code,
matter_reference,
responsible_party,
archive_folder_path,
received_at,
destruction_date,
status: 'active',
classified_at: now(),
alert_sent_at: null
})
// --- CLASSIFICATION AGENT OUTPUT COMPLETE ---
// Register row is now the single source of truth for this document's lifecycle.
// ============================================================
// DAILY SCHEDULED RUN: Retention Schedule Monitor
// ============================================================
// --- HANDOFF TO: Retention Schedule Monitor ---
// STEP 8: Query retention register
run_date = today() // UTC 07:00
due_documents = microsoft365_list.query({
filter: 'destruction_date <= run_date AND status == active AND alert_sent_at == null'
})
// Pagination: fetch all pages; max 5000 rows per request (Microsoft 365 List API limit)
// STEP 9: Resolve Slack recipients
for each doc in due_documents:
slack_user_id = manager_lookup_table[doc.matter_reference] || fallback_channel_id
days_overdue = date_diff(run_date, doc.destruction_date)
// STEP 10: Send Slack alert
slack_message_ts = slack.post_block_kit_message(slack_user_id, {
document_name: doc.document_name,
document_type: doc.document_type,
matter_reference: doc.matter_reference,
destruction_date: doc.destruction_date,
days_overdue,
sharepoint_link: resolve_link(doc.archive_folder_path, doc.document_id),
actions: ['Approve Destruction', 'Defer']
})
// STEP 11: Update register with alert sent timestamp
microsoft365_list.update_row(doc.register_row_id, {
alert_sent_at: now(),
slack_message_ts
})
// ============================================================
// INTERACTIVE CALLBACK: Legal Manager Response
// ============================================================
// --- HANDOFF FROM: Slack interactive callback ---
slack_callback_payload -> {
action: 'approve' | 'defer',
slack_user_id: approver_slack_id,
message_ts: slack_message_ts,
document_id,
register_row_id,
deferred_until: ISO8601 | null // supplied only on defer action
}
// STEP 12: Write outcome to register
if action == 'approve':
microsoft365_list.update_row(register_row_id, {
status: 'pending_destruction',
destruction_confirmed_by: approver_slack_id,
approved_at: now()
})
slack.post_reply(slack_message_ts, 'Destruction approved. Please proceed and confirm completion.')
if action == 'defer':
deferred_until = deferred_until || run_date + 30_days
microsoft365_list.update_row(register_row_id, {
status: 'deferred',
deferred_until,
destruction_date: deferred_until,
alert_sent_at: null // reset so monitor re-alerts on new date
deferred_by: approver_slack_id
})
slack.post_reply(slack_message_ts, 'Destruction deferred to ' + deferred_until)
// NOTE: No file is deleted by the automation at any point.
// Physical destruction and final register close-out remain human actions.
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Document Retention & Archiving.