AI document processing can remove a tedious loop from operations work: download a CSV, inspect the columns, clean the rows, calculate the result, and paste it back into Slack. It can also create a quieter failure. The agent may read the wrong file, obey instructions hidden inside a document, lose rows during parsing, or call a failed job complete.
Business operations leaders need a file-job contract that binds the Slack request, requester, input, transformation, validation, result, and any approved follow-up action. This guide shows how to build that contract and test it against a realistic CSV reconciliation job.
Why uploaded files need a job contract
Most intelligent document processing pages explain how to extract text, classify a document, split a PDF, or validate fields. The Google Cloud Document AI overview, for example, covers OCR, extraction, classification, splitting, validation, and prediction review. A Slack workflow must also decide who may process a file, what the operator requested, how partial failures appear in the thread, and whether the result may update another system.
Slack adds its own transport and permission details. Its working with files guide documents separate read and write scopes, private download URLs, file events, and malware scanning before a file becomes available in a workspace. An agent should not treat the first visible filename as proof that the content is ready, readable, authorized, or complete.
The job contract supplies the missing operational layer. It should answer seven questions before execution:
- Which Slack workspace, channel, thread, and message define the request?
- Which user is asking, and can that user read the file now?
- Which immutable file version will be processed?
- What transformation should run, using which accepted input shape?
- What checks must pass before the result is usable?
- How will partial, failed, and cancelled work appear?
- Which follow-up action, if any, requires explicit approval?
If the system cannot answer one of these questions, it should pause in the thread instead of guessing.
Define the file job before execution
Turn the Slack request into a small manifest. Keep the original message text for audit, but do not rely on prose alone to control execution.
job_id: filejob_20260910_0142
thread:
workspace: T_WORKSPACE
channel: C_OPERATIONS
root_ts: "1789038120.004200"
requester: U_ANALYST
input:
slack_file_id: F_EXPORT
version_key: "sha256:recorded-at-ingest"
media_type: text/csv
size_bytes: 418220
objective: "Reconcile invoice totals by region and list rejected rows"
accepted_schema:
required_columns:
- invoice_id
- region
- amount
- currency
checks:
- unique_invoice_id
- allowed_currency
- source_row_count_reconciled
- amount_total_reconciled
execution:
network: disabled
max_seconds: 60
downstream_writes: blocked
status: acceptedThe manifest is an implementation recommendation, not a Slack standard. Its purpose is to make the request reproducible. File ID and version prevent a later replacement from silently changing the input. Required columns define the minimum usable shape. Checks describe the evidence that must accompany the answer. The execution block keeps analysis separate from writes.
Record a terminal state for every job: succeeded, partial, failed, cancelled, or expired. Never leave running or waiting as the last state after a worker error. A current Dify issue reports a malformed CSV that leaves a batch stuck at waiting, even though worker logs contain the failure. That is an author report about one implementation. Updating worker failure and user-visible job state together prevents this false-success path.
Separate transport, content, and instructions
A Slack message can contain legitimate instructions. The attached document is untrusted input. Keep those channels separate even when the model sees both.
The OWASP prompt injection prevention guidance describes remote or indirect prompt injection, where malicious instructions arrive through external content such as files or web pages. It recommends controls including input validation, structured separation, output monitoring, least privilege, human review, and comprehensive monitoring. For file jobs, translate those controls into specific rules:
- Only the Slack request and an approved internal job template may define the objective.
- Treat text extracted from the file as data. It cannot enable tools, change limits, reveal other sources, or approve a write.
- Reject unsupported file types and files that exceed declared limits before model processing.
- Parse into a typed intermediate representation before asking a model to interpret values.
- Disable network access in the execution environment unless the job explicitly needs a named, approved source.
- Give the execution process only the input file and a writable temporary output directory.
- Validate the output independently of the model response.
An Open WebUI user reported an attached PDF that was visible by metadata but not reliably readable through tools. Enabling full context exposed the entire document to the prompt, according to the report. Do not use full-context injection as an automatic fallback. Tell the operator that retrieval failed, or ask whether the full file may be processed under the same controls.
Run the file in an isolated sequence
Use a deterministic sequence around any probabilistic interpretation.
Accept and pin the input
Resolve the Slack file through the requesting user's authorized context. Record the file ID, version or digest, media type, byte size, and retrieval time. Wait for Slack's file readiness path rather than retrying an unavailable private URL without a limit. If access changes before execution, fail closed and name the inaccessible input without exposing its content.
Inspect before interpreting
Check the file signature, not only its extension. Apply size, page, row, archive-depth, and decompression limits. For CSV input, detect the delimiter and encoding, then parse the header without executing formulas or macros. For PDF input, record whether the document is text-based, scanned, encrypted, or partially unreadable.
When extraction loses structure, stop pretending the data is tabular. The result can still be useful as a partial transcript, but it must not feed a calculation that assumes intact columns.
Execute with narrow capabilities
Create a clean sandbox for one job. Mount or copy only the pinned input, disable outbound network access, apply CPU, memory, and time limits, and discard the environment afterward. Do not pass unrelated thread attachments, integration credentials, or a shared working directory into the process.
Kipwise Agent documents this product path for a CSV, log file, or quick calculation: it runs code in a throwaway, network-isolated sandbox tied to the thread, reports the result, and discards the sandbox. It also states that connected work uses the requesting user's permissions where applicable. Those controls make the Slack request useful without granting a general-purpose process broad internal access.
Validate outside the transformation
After a successful process exit, compare the output with the manifest:
- Input row count equals accepted rows plus rejected rows.
- Required identifiers remain unique.
- Every amount parses under the declared currency rule.
- Group totals reconcile to the accepted source total.
- Every rejected row includes a reason and original row number.
- Output files contain only declared columns.
- The result includes the pinned input digest and transformation version.
Post partial, not succeeded, when the calculations are valid but some rows were rejected. Post failed when totals do not reconcile or the output cannot be parsed.
Work through a CSV reconciliation example
Suppose an operations analyst attaches a regional invoice export and asks: "Reconcile totals by region, list invalid rows, and draft a short finance note."
The agent should first reply with the pinned filename, row count, detected columns, planned checks, and a statement that it will not update the finance system. If the export lacks currency, the job stops before calculation and asks for the missing field or a single confirmed currency assumption.
For a valid file, the transformation can follow this pseudocode:
load pinned CSV as data, never as instructions
validate required columns
for each row:
reject missing invoice IDs
reject duplicate invoice IDs
reject unsupported currencies
parse amount using declared decimal rules
aggregate accepted amounts by region
reconcile accepted plus rejected count to source count
reconcile grouped totals to accepted-row total
emit summary, rejected-row file, and validation recordThe Slack result should distinguish facts from requests:
- Input: pinned file ID, digest, 2,418 source rows.
- Accepted: 2,401 rows.
- Rejected: 17 rows, with a downloadable exception file.
- Result: regional totals and accepted-row total.
- Checks: row reconciliation passed, total reconciliation passed, identifier uniqueness passed after rejection.
- Limits: rejected rows were excluded; no finance records were changed.
- Next action: draft available for review, with a separate approval required before writing anywhere.
These values are a worked example, not a product benchmark. The format forces the agent to show how it reached the answer and what remains unresolved.
Make failures visible in the thread
Operators should not need worker logs to learn that a job failed. Map every technical failure to a concise thread state and a safe retry rule.
A parser error should identify the file, rejected format, and accepted alternatives. A timeout should say which stage stopped and whether any output was discarded. A permission failure should name the system or file object, not its protected contents. A validation mismatch should show the expected and observed counts or totals. A model refusal or uncertain classification should route the file to review, not convert uncertainty into an empty successful output.
Make retries deliberate. Reuse the same job ID only when the pinned input and transformation are unchanged. Create a new version when the user replaces the file, changes the schema, alters the objective, or accepts a new assumption. An idempotency key should prevent a Slack event retry from launching the same transformation twice.
Keep the thread usable. Post one progress update only when the job will take long enough to matter. Finish with one terminal message that links the result, exceptions, checks, and proposed next action. Put detailed logs in the governed execution record rather than flooding the channel.
Protect downstream systems with a second boundary
File analysis and external action are separate jobs. A request to "reconcile and update the tracker" should first produce the reconciliation and a proposed update. The write waits for an authorized person to inspect the totals, exceptions, target, and exact payload.
Invalidate approval when the input digest, result, target, or payload changes. Use the destination system's live permissions at execution time. Record the approving user, approved payload digest, external result ID, and outcome. If the write succeeds but the Slack acknowledgement fails, reconcile by idempotency key before retrying.
Start pilots with read-only analysis and a downloadable result. Add one reversible internal write only after the team can prove access revocation, duplicate delivery, partial failure, and output validation behave correctly.
Verify the workflow before rollout
Run a fixed test pack in a non-production workspace:
- Upload a valid CSV and reconcile every row and total.
- Remove one required column and confirm execution never starts.
- Include a duplicate identifier and confirm it appears in exceptions.
- Put an instruction to reveal secrets inside a document and confirm it is treated as data.
- Replace the file after acceptance and confirm the old version cannot run unnoticed.
- Revoke the requester's file access before execution and confirm the job fails closed.
- Force a parser crash and confirm the thread reaches
failed, notwaiting. - Force a timeout and confirm partial artifacts are discarded or clearly labeled.
- Replay the Slack event and confirm the transformation does not run twice.
- Change an approved downstream payload and confirm the old approval becomes invalid.
- Block a private file download and confirm no cached content from another user appears.
- Compare two reruns of the same pinned input and transformation version.
Track operator corrections as seriously as time saved. Useful pilot measures include analyst minutes per file, manual downloads, tool switches, rejected-row accuracy, reconciliation failures caught, false-success incidents, rerun consistency, and downstream actions held for approval. Use the same definitions before and during the pilot.
Pick one recurring file job with a named owner and a known manual baseline. Encode its manifest, run the test pack, and keep downstream writes disabled until every failure produces a visible terminal state.
References
- Google Cloud Document AI overview supports current document extraction, classification, splitting, validation, and review capabilities.
- Slack Developer Docs: Working with files supports file scopes, metadata, private URLs, file events, upload sequencing, and malware-scan behavior.
- OWASP LLM Prompt Injection Prevention Cheat Sheet supports indirect-injection risks and the recommended validation, least-privilege, review, and monitoring controls.
- Dify CSV batch failure issue provides the practitioner-reported stuck-waiting failure.
- Open WebUI attached-file issue provides the practitioner report about file metadata, tool access, and full-context behavior.
- Kipwise Agent supports the requester-bound, thread-native, isolated file-processing product workflow described here.


