- The loop is one file at a time: open the email, download the attachment, rename it to the house convention, drag it into the right client folder.
- The renaming is not the work. Deciding which client owns this file, and what the name has to say so somebody can find it in a year, is the work.
- The prompt turns a folder of inconsistent names into a rename plan you read and approve before a single file moves.
- The script fetches the attachment, derives the name from what is inside the document, and writes it to the right folder with a collision rule you chose.
- Filed the same way every time means findable without asking anybody, which is the real reason to file every incoming document without touching it.
Welcome to The Loop. Every Wednesday we take one real, repetitive workflow that someone does by hand and unwind it into something you can run. This week: the forty files that land in your inbox and your shared folders, and the person who downloads, renames and files every one of them.
The boring task you repeat is a specification in disguise. Nobody at your company wrote down the filing rules, because filing has never felt like a process. It felt like knowing where things go. But every one of those forty files is the same handful of decisions applied again: which client this belongs to, what the name has to say, which folder is correct, what to do when a file with that name is already sitting there. Say those decisions out loud once and you have a spec. The dragging stops being yours the moment the spec exists.
This is the oddly specific automate lane rather than the troubleshooting lane, and the specificity earns its keep. The generic version of this article is about document management, which helps nobody. This one is about one PDF arriving as a reply to a thread, and what has to be true before it can land in the right place with the right name.
The Loop, in Full
See the loop clearly before automating it, because the part that looks like the job takes three seconds and the part nobody mentions takes the rest. Dana runs the office for an eight person design and build contractor. Files arrive all day and from everywhere: a supplier invoice attached to a reply, a signed change order from the e-signature tool, a certificate of insurance a subcontractor finally sent, a permit PDF from the city portal, a site photo set somebody shared from a phone. The house convention is written on a sticky note on her monitor and in no other place: date first in year-month-day order, then the client, then the document type, then the reference number. So she opens the email, downloads the attachment to a downloads folder already holding three hundred files called things like scan_0043.pdf and invoice (2).pdf, opens the PDF to find out who it is actually from, renames it, then opens the shared folder, navigates two levels down to the client, three if the client has more than one job running, and drags it in. Forty times a week, about two minutes each, plus the ones that need a second look.
- Open the email or notification and download the attachment.
- Open the file to work out which client and which job it belongs to.
- Rename it to the house convention, from memory, with the date in the right order.
- Navigate to the client folder, then to the job subfolder, then to the document type.
- Drag it in, and decide what to do when something with that name is already there.
Every step is easy, which is exactly why it has survived for three years and will survive another three. None of it requires judgment that Dana has not already made four hundred times, and the one step that does carry real risk, step five, gets the least attention because the other four have already eaten the afternoon. That is the signature of a loop worth unwinding: the mechanical parts have crowded out the decision, and mechanical parts are what you hand to a bot that files your paperwork so the decision gets looked at properly.
The Manual Tax
The cost is two minutes a file, plus the files that end up in the wrong place, plus the week a document spends in a downloads folder before anybody can find it.
The aggregate version is well documented, and the interesting part is that the cost shows up on the retrieval side rather than the filing side. In its report on the social economy, the McKinsey Global Institute estimated that the average interaction worker spends 28 percent of the workweek managing email and nearly 20 percent looking for internal information or tracking down colleagues who can help, and noted that a searchable record of knowledge can cut the search time by as much as 35 percent. Two minutes a file times forty files is about an hour and twenty minutes a week, which is annoying. The fifth of the week that everyone else loses looking for what Dana filed is the actual bill, and it is paid by people who never touched the loop.
- Errors. There are two, and they fail differently. A file in the wrong client folder is effectively deleted: nobody will ever look there, and nobody knows to. A file with the wrong name is worse in a quieter way, because it is present, it is findable by browsing, and it is wrong about itself, so the invoice dated the day it was scanned rather than the day it was issued lands in the wrong month and the reconciliation does not balance. Then there is the collision, which is the one that actually destroys something: the second version of a change order saved over the first, with no trace that a first existed.
- Latency. A document is worth the most on the day it arrives and the certificate of insurance is the clean example, because it is the difference between a subcontractor being allowed on site on Monday and not. While a file sits in a downloads folder it exists for exactly one person. The team behaves as though it never arrived, so somebody emails the supplier again, the supplier resends, and now there are two copies of it waiting to collide.
A filing convention that lives in one person's head is not a convention. It is a dependency, and it takes vacations.
So the goal is not a faster afternoon. The goal is that a file arriving is the same event as a file being filed, with no human in between and no window where the document exists but nobody can find it. That is what it looks like to sort and name every incoming file automatically, and the hour saved is the smallest part of the benefit.
Unwinding the Loop
Automating starts with description, not code. Write this loop down and the upload call turns out to be four lines, while three rules you have never written anywhere turn out to be the entire article.
| Part of the loop | What it is for this workflow |
|---|---|
| Trigger | A file arriving, from any of its doors: an attachment on an email matching a filter, a file dropped into a watched inbox folder, a notification from a portal. One file in, one filed file out. |
| Input | The file itself, plus the envelope it came in: sender address and domain, subject line, thread, and whatever the sender happened to call it. Treat the sender's filename as a hint, never as data. |
| Decision | Which client and job owns this file. Stated as a lookup table mapping sender domain, project code and reference number to a destination, with a named review folder for anything that matches nothing. |
| Decision | What the name has to say. A sortable date, the client, the document type, and one identifier taken from inside the document, in a fixed order with one separator. |
| Decision | What happens on a name collision: new version, duplicate to skip, or stop and ask. Never overwrite, and decide this before the first file moves. |
| Output | The file in its destination folder under its derived name, plus one log line recording where it came from, where it went, and which rule sent it there. |
| Success signal | The number of files in the review folder, and the number anybody had to move by hand afterward. The first should be small and the second should be zero. |
Three of those rows are plumbing and four are judgment, and the judgment rows are the ones no tool will ever guess for you. Writing the spec before any step is configured is also exactly how BYOBot turns a described task into a running workflow, so the description is not the preamble to the build, it is most of the build. If you have read our installment on reconciling invoices against a sheet the two compose neatly: that one assumes the invoice is somewhere sensible with a name that tells the truth, and this one is how it got that way.
Tell BYOBot where your files arrive and what a correctly filed one looks like, and it will design the build: the routing table, the naming rule, and what happens to a file it cannot place.
The Build: One Loop, Three Ways to Run It
The same described loop becomes three builds, and which one you want depends only on how hands-off you need it to be. Start at the top and stop whenever it is good enough.
The Prompt
Start with the backlog rather than the inflow, because the three hundred files already sitting in the wrong place are what makes the convention feel hopeless. Paste a directory listing into any chat tool and have it produce a rename plan: not the renaming, the plan. You read it, you argue with four lines of it, and only then does anything move. This is also the cheapest way to discover that your convention has three variants.
Below is a listing of files from a shared folder, one per line, in the form: current_name | date_modified | sender Our naming convention is: YYYY-MM-DD_Client_DocType_Reference.ext DocType is one of: INVOICE, CHANGEORDER, COI, PERMIT, PHOTOS, CONTRACT, OTHER Reference is the invoice number, PO number or permit number. Use NOREF when there is not one. Produce a table with these columns, one row per file: current_name | proposed_name | client | doctype | date_source | confidence | why Rules: 1. date_source must say where the date came from: IN_FILENAME, FROM_SENDER_LINE, MODIFIED_DATE, or NONE. Prefer a date printed in the name over the modified date, and say so when you had to fall back. 2. Never invent a reference number. If it is not visible in the name, write NOREF and set confidence LOW. 3. confidence is HIGH, MEDIUM or LOW. Anything you had to guess the client for is LOW, no exceptions. 4. Flag any two files whose proposed_name is identical in a separate COLLISIONS list, with both current names. 5. Do not propose a rename for anything you bucketed as OTHER. List those separately under NEEDS_A_HUMAN. Then list the three naming calls you were least sure about and what rule would have settled them.
Run that against a listing of your worst folder and the output does two useful things at once. It gives you a plan you can apply in an afternoon, and the NEEDS_A_HUMAN pile at the bottom is a free audit of your convention: those are the document types your sticky note never covered. Fix the rule there, in writing, before you automate anything, because a build inherits whatever ambiguity you hand it. The rows marked HIGH are safe to apply in bulk, and everything after that is the argument for letting a build stop retyping what the document already says.
The Script
Here is the honest version: almost none of this needs AI at runtime. Fetching an attachment, deriving a name from fields you already have, checking whether a path exists and uploading a file are API calls, and a model in the middle of that chain only adds a way to be confidently wrong about where your contract went. The pieces are documented and the free tiers cover a volume like Dana's comfortably. Pulling the attachment bytes out of a message is a single call in the Gmail API, and if you want the job to react within seconds rather than on a sweep, the push notification guide covers watching a mailbox properly. The write at the far end, including the mode that refuses to clobber an existing path, is one upload endpoint in the Dropbox HTTP API, and the equivalent exists in every file service worth using. AI earns exactly one job here: reading a document that carries no usable label, so the client and the reference come out of the page rather than out of a filename a stranger invented.
ROUTES = load_table("config/routing.csv")
# sender_domain, project_code, ref_prefix -> client, folder
def file_it(msg, att):
raw = fetch_attachment(msg.id, att.id)
hint = {"from": msg.sender, "subj": msg.subject,
"name": att.filename}
route = match(ROUTES, hint)
if not route:
fields = read_document(raw) # the one AI step
route = match(ROUTES, fields)
hint.update(fields)
if not route:
return park(raw, "_review", why="no route matched")
name = "{date}_{client}_{type}_{ref}{ext}".format(
date = iso_date(hint) or park_for_date(raw),
client= route.client,
type = doctype(hint),
ref = reference(hint) or "NOREF",
ext = extension(att.filename))
path = route.folder + "/" + name
if exists(path):
if sha256(raw) == sha256(get(path)):
return log("duplicate skipped", path)
path = next_version(path) # never overwrite
upload(path, raw, mode="add") # add, not overwrite
log("filed", src=msg.id, dest=path, rule=route.id)
Four details carry the value. The routing lives in a CSV a non-builder can edit, so taking on a new client is a row rather than a deploy. The AI step runs only when the cheap rules fail, which keeps the cost near zero and the behavior predictable. Nothing is ever overwritten: an identical file is skipped on a content hash, a different file with the same name becomes a new version, and a file with no derivable date is parked rather than filed under a guess. And every run writes a log line naming the rule that made the decision, so when a document turns up somewhere surprising you can find out why in one search. BYOBot will generate the working version against your actual mailbox and folder structure, including the review report, so you can keep every client folder filed without opening it.
The Schedule
Now make it boring. The script already does the work, so this tier is about two jobs that only matter once filing is nobody's responsibility. The first is a sweep every few minutes over whatever arrived, which is the ordinary path. The second is the one people skip: a daily pass over the review folder that mails one summary of everything the build refused to file and why, because an unfiled pile that nobody reads is just a slower version of the downloads folder. Both run from a cron entry on any always-on machine, or from the free schedule trigger in GitHub Actions if you would rather not own a server. If files also arrive by being dropped straight into a shared folder, point the same build at the service's own change notifications, which is what Dropbox webhooks are for, and you get the same routing on both doors.
| Build | What runs it | Setup | Runs unattended? |
|---|---|---|---|
| The prompt | You, in any AI chat tool, turning a messy folder into a rename plan | Minutes | No |
| The script | Fetch, route, name and upload with no overwrite, no model in the main path | An hour, once | On demand |
| The schedule | A sweep every few minutes plus one daily report of what got parked | A few extra minutes | Yes |
Steal This Build
Here is the whole loop as a spec you can copy into your own stack this afternoon, or hand straight to a system that will turn the description into a running workflow for you.
- Trigger: a file arriving at any of its doors, with the envelope it came in attached.
- Route: to a client and folder from a table somebody non-technical can edit, and to a review folder when nothing matches.
- Name: from a sortable date, the client, the document type and one reference taken from inside the file.
- Protect: skip a content-identical duplicate, version a real conflict, and never overwrite anything.
- Report: one log line per file naming the rule that placed it, and one daily summary of everything parked.
The shape is universal. Somewhere in your week there is a thing that arrives, needs identifying, needs a name, and needs to go in exactly one correct place: a resume against a requisition, a receipt against an expense report, a lab result against a sample, a photo set against a property listing. The spec barely changes. One arrival, a routing table you can edit without a developer, a derived identifier rather than an inherited one, a collision rule chosen in advance, and a visible pile for the ones that need a person. That pattern covers most of what gets filed as the repetitive work you want off your plate, and the version you write for attachments is the one you will reuse everywhere else.
Tell BYOBot about a loop in your week
Describe the task you keep doing by hand and BYOBot will design the full playbook: the prompt, the script, and the schedule that runs it for you.
Frequently Asked Questions
- Four things, in a fixed order: a sortable date, the party the file belongs to, what kind of document it is, and one identifier from inside the document such as an invoice number or a purchase order. Put the date first in year-month-day form so an alphabetical listing is also a chronological one, use a single separator throughout, and never put a value in the name that you cannot derive the same way every time. A name is a query you are writing in advance, so include the things you will later search for and leave out the things you will not. Writing the rule down is also the step that lets you hand your document filing to a workflow instead of to whoever is least busy.
- With a lookup table, not with a rule written into the workflow. Keep one table that maps the things you can see on an incoming file, the sender domain, a project code, a matter number, a purchase order prefix, to a client and a destination folder. The workflow reads the table on every run, so adding a client is a row edit rather than a rebuild. When no row matches, the file goes to a review folder with a note saying why, because a file filed in the wrong client folder is worse than a file that is visibly unfiled. That one habit is most of what makes it safe to let a build put files away for you.
- You decide in advance, and the choice is the whole design. If the second file is genuinely a new version, add a version suffix and keep both. If it is the same document arriving twice, which is common when a supplier copies three people on the same email, compare a hash of the contents and skip the duplicate instead of writing a second copy. Overwriting on a name collision is the one option to rule out, because it destroys a document silently and leaves no evidence that anything was lost.
- Not for the filing itself, and that is the honest part. Fetching an attachment, renaming a file and uploading it to a folder are plain API calls with no model involved, and keeping them deterministic is what makes the job safe to leave running unattended. AI earns its place at exactly one point in this loop: reading a document that carries no usable label, so you can pull the client, the document type and the reference number out of the page itself rather than out of a filename somebody else invented. Everything downstream of that extraction should be code, and the extraction should write down how confident it was.
