Reads each PDF that arrives by email and files it into the right Box folder by what it says, or leaves it for you when unsure.

UsesBoxGmailOpenAI

File emailed PDFs into the right Box folder

Mail gets filed by who sent it, so the contract from your accountant ends up in the accounts folder. This reads each PDF that arrives by email for what kind of document it is, what it concerns, its reference, its date and any total, and an AI picks one of the Box folders you have described from those answers alone. The sender and subject are never shown to it, so an invoice is filed as an invoice whoever sent it. Each copy keeps its name plus a reference to its email, so a second Invoice.pdf never clashes with the first.

When the answer is not exactly one of your folders, or nothing could be read, nothing is copied and the email is labelled 'Needs sorting', a short queue you work through by hand. So is an older email Gmail cannot open because the same sender's newer one is still in the inbox. The run's last message lists each one. If reading, choosing or copying fails, the email is labelled the same way and the run ends as failed, saying what went wrong.

You need Gmail, Box and OpenAI connected, and up to three folders described in advance: a name, its Box folder and a line saying what belongs there. The Gmail search starts as any email from the past week with a PDF attached.

GmailGmailWhen an email arrives.When an email arrives.
ValuesSet documentSearch to has:attachment filename:pdf newer_than:7d.Set documentSearch to has:attachment filename:pdf newer_than:7d.
ValuesSet categoryOneName to blank.Set categoryOneName to blank.
ValuesSet categoryOneFolder to blank.Set categoryOneFolder to blank.
ValuesSet categoryOneAbout to blank.Set categoryOneAbout to blank.
ValuesSet categoryTwoName to blank.Set categoryTwoName to blank.
ValuesSet categoryTwoFolder to blank.Set categoryTwoFolder to blank.
ValuesSet categoryTwoAbout to blank.Set categoryTwoAbout to blank.
ValuesSet categoryThreeName to blank.Set categoryThreeName to blank.
ValuesSet categoryThreeFolder to blank.Set categoryThreeFolder to blank.
ValuesSet categoryThreeAbout to blank.Set categoryThreeAbout to blank.
ValuesSet processedLabel to Document filing.Set processedLabel to Document filing.
ValuesSet needsSortingLabel to Needs sorting.Set needsSortingLabel to Needs sorting.
ValuesSet maxDocumentsPerRun to 5.Set maxDocumentsPerRun to 5.
MathSet documentCap to maxDocumentsPerRun multiplied by 1.Set documentCap to maxDocumentsPerRun multiplied by 1.
Decisions & LoopsIf documentCap is greater than 25If documentCap is greater than 25
Yes
Decisions & LoopsStop and report a problem: The 'maxDocumentsPerRun' step needs a whole number between 1 and 25 - it is how many documents one run is allowed to handle.Stop and report a problem: The 'maxDocumentsPerRun' step needs a whole number between 1 and 25 - it is how many documents one run is allowed to handle.
No
No steps
Decisions & LoopsIf documentCap is less than 1If documentCap is less than 1
Yes
Decisions & LoopsStop and report a problem: The 'maxDocumentsPerRun' step needs a whole number between 1 and 25 - it is how many documents one run is allowed to handle.Stop and report a problem: The 'maxDocumentsPerRun' step needs a whole number between 1 and 25 - it is how many documents one run is allowed to handle.
No
No steps
ValuesSet destinations to an empty list.Set destinations to an empty list.
Decisions & LoopsIf categoryOneName isn't empty and categoryOneFolder isn't emptyIf categoryOneName isn't empty and categoryOneFolder isn't empty
Yes
ListsAdd a record of name, folder, and about to the end of destinations and save it as destinations.Add a record of name, folder, and about to the end of destinations and save it as destinations.
No
No steps
Decisions & LoopsIf categoryTwoName isn't empty and categoryTwoFolder isn't emptyIf categoryTwoName isn't empty and categoryTwoFolder isn't empty
Yes
ListsAdd a record of name, folder, and about to the end of destinations and save it as destinations.Add a record of name, folder, and about to the end of destinations and save it as destinations.
No
No steps
Decisions & LoopsIf categoryThreeName isn't empty and categoryThreeFolder isn't emptyIf categoryThreeName isn't empty and categoryThreeFolder isn't empty
Yes
ListsAdd a record of name, folder, and about to the end of destinations and save it as destinations.Add a record of name, folder, and about to the end of destinations and save it as destinations.
No
No steps
ValuesSet filingDestinations to an empty list.Set filingDestinations to an empty list.
Decisions & LoopsFor each destination in destinations:For each destination in destinations:
TextTrim destination.folder and save it as folderValue.Trim destination.folder and save it as folderValue.
Decisions & LoopsIf folderValue contains /folder/ (ignoring case)If folderValue contains /folder/ (ignoring case)
Yes
TextTrim the first item of the last item of folderValue split by /folder/ split by / and save it as folderValue.Trim the first item of the last item of folderValue split by /folder/ split by / and save it as folderValue.
No
No steps
Decisions & LoopsIf folderValue is empty or folderValue multiplied by 1 is not folderValueIf folderValue is empty or folderValue multiplied by 1 is not folderValue
Yes
ValuesShow in the output: Ignoring the '@destination.name' category: '@destination.folder' is not a Box folder. Paste the folder's address in Box, or just the number after '/folder/'.Show in the output: Ignoring the '@destination.name' category: '@destination.folder' is not a Box folder. Paste the folder's address in Box, or just the number after '/folder/'.
No
ListsAdd a record of name, folder, and about to the end of filingDestinations and save it as filingDestinations.Add a record of name, folder, and about to the end of filingDestinations and save it as filingDestinations.
Decisions & LoopsIf filingDestinations is emptyIf filingDestinations is empty
Yes
Decisions & LoopsStop and report a problem: Fill in at least one category - a name in 'categoryOneName' and the Box folder it files into in 'categoryOneFolder' - then run it again.Stop and report a problem: Fill in at least one category - a name in 'categoryOneName' and the Box folder it files into in 'categoryOneFolder' - then run it again.
No
No steps
ValuesSet choicesText to blank.Set choicesText to blank.
Decisions & LoopsFor each destination in filingDestinations:For each destination in filingDestinations:
ValuesSet choiceLine to - destination.name.Set choiceLine to - destination.name.
TextTrim destination.about and save it as choiceAbout.Trim destination.about and save it as choiceAbout.
Decisions & LoopsIf choiceAbout isn't emptyIf choiceAbout isn't empty
Yes
ValuesChange choiceLine to choiceLine: choiceAbout.Change choiceLine to choiceLine: choiceAbout.
No
No steps
ValuesChange choicesText to choicesText@choiceLineChange choicesText to choicesText@choiceLine .
GmailGmailList emails matching '@documentSearch has:attachment -label:@processedLabel -label:@needsSortingLabel'. Save it as found.List emails matching '@documentSearch has:attachment -label:@processedLabel -label:@needsSortingLabel'. Save it as found.
ValuesSet handledCount to 0.Set handledCount to 0.
ValuesSet filedCount to 0.Set filedCount to 0.
ValuesSet messagesLeft to 0.Set messagesLeft to 0.
ValuesSet leftForYou to an empty list.Set leftForYou to an empty list.
ValuesSet problems to an empty list.Set problems to an empty list.
Decisions & LoopsFor each message in found.emails:For each message in found.emails:
Decisions & LoopsIf handledCount is greater than or equal to documentCapIf handledCount is greater than or equal to documentCap
Yes
ValuesAdd 1 to messagesLeft.Add 1 to messagesLeft.
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
ValuesSet messageDescription to an email.Set messageDescription to an email.
Decisions & LoopsIf a step below fails, save the error as messageErrorIf a step below fails, save the error as messageError
Try
ValuesSet messageId to blank.Set messageId to blank.
Decisions & LoopsIf a step below fails, save the error as idErrorIf a step below fails, save the error as idError
Try
TextTrim message.id and save it as messageId.Trim message.id and save it as messageId.
On error
No steps
Decisions & LoopsIf messageId is emptyIf messageId is empty
Yes
ValuesShow in the output: Skipped a message Gmail returned without an id..Show in the output: Skipped a message Gmail returned without an id..
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
ValuesSet rawFrom to blank.Set rawFrom to blank.
Decisions & LoopsIf a step below fails, save the error as fromErrorIf a step below fails, save the error as fromError
Try
TextTrim message.from and save it as rawFrom.Trim message.from and save it as rawFrom.
On error
No steps
ValuesSet senderAddress to rawFrom.Set senderAddress to rawFrom.
Decisions & LoopsIf rawFrom contains < (ignoring case)If rawFrom contains < (ignoring case)
Yes
TextTrim the first item of the last item of rawFrom split by < split by > and save it as senderAddress.Trim the first item of the last item of rawFrom split by < split by > and save it as senderAddress.
No
No steps
ValuesSet subject to (no subject).Set subject to (no subject).
Decisions & LoopsIf a step below fails, save the error as subjectErrorIf a step below fails, save the error as subjectError
Try
TextTrim message.subject and save it as subjectText.Trim message.subject and save it as subjectText.
Decisions & LoopsIf subjectText isn't emptyIf subjectText isn't empty
Yes
ValuesChange subject to subjectText.Change subject to subjectText.
No
No steps
On error
No steps
TextBuild the text '@subject' from @senderAddress and save it as messageDescription.Build the text '@subject' from @senderAddress and save it as messageDescription.
Decisions & LoopsIf senderAddress doesn't contain @ (ignoring case)If senderAddress doesn't contain @ (ignoring case)
Yes
GmailGmailAdd label processedLabel to email messageId.Add label processedLabel to email messageId.
GmailGmailAdd label needsSortingLabel to email messageId.Add label needsSortingLabel to email messageId.
ListsAdd messageDescription: its sender could not be read to the end of leftForYou and save it as leftForYou.Add messageDescription: its sender could not be read to the end of leftForYou and save it as leftForYou.
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
ValuesSet latestId to blank.Set latestId to blank.
GmailGmailFind email matching 'from:senderAddress'. Save it as latestFromSender.Find email matching 'from:senderAddress'. Save it as latestFromSender.
Decisions & LoopsIf a step below fails, save the error as latestErrorIf a step below fails, save the error as latestError
Try
TextTrim latestFromSender.email.id and save it as latestId.Trim latestFromSender.email.id and save it as latestId.
On error
No steps
Decisions & LoopsIf latestId is not messageIdIf latestId is not messageId
Yes
GmailGmailAdd label processedLabel to email messageId.Add label processedLabel to email messageId.
GmailGmailAdd label needsSortingLabel to email messageId.Add label needsSortingLabel to email messageId.
ListsAdd messageDescription: only a sender's newest email can be opened, and a newer one from this sender is in the inbox to the end of leftForYou and save it as leftForYou.Add messageDescription: only a sender's newest email can be opened, and a newer one from this sender is in the inbox to the end of leftForYou and save it as leftForYou.
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
GmailGmailDownload the attachments on the latest email from senderAddress. Save it as downloaded.Download the attachments on the latest email from senderAddress. Save it as downloaded.
GmailGmailAdd label processedLabel to email messageId.Add label processedLabel to email messageId.
ValuesSet attachmentNumber to 0.Set attachmentNumber to 0.
ValuesSet namesOnMessage to an empty list.Set namesOnMessage to an empty list.
Decisions & LoopsFor each attachment in downloaded.documents:For each attachment in downloaded.documents:
Values Set fileName to blank. Set fileName to blank.
Decisions & Loops If a step below fails, save the error as nameError If a step below fails, save the error as nameError
Try
TextTrim attachment.filename and save it as fileName.Trim attachment.filename and save it as fileName.
On error
No steps
Values Set attachmentLabel to (unnamed attachment). Set attachmentLabel to (unnamed attachment).
Decisions & Loops If fileName isn't empty If fileName isn't empty
Yes
ValuesChange attachmentLabel to fileName.Change attachmentLabel to fileName.
No
No steps
Decisions & Loops If fileName in lowercase doesn't end with .pdf (ignoring case) If fileName in lowercase doesn't end with .pdf (ignoring case)
Yes
ValuesShow in the output: Skipped attachmentLabel: only PDF attachments are treated as documents.Show in the output: Skipped attachmentLabel: only PDF attachments are treated as documents.
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
Values Set documentRef to blank. Set documentRef to blank.
Decisions & Loops If a step below fails, save the error as refError If a step below fails, save the error as refError
Try
TextTrim attachment.document_ref and save it as documentRef.Trim attachment.document_ref and save it as documentRef.
On error
No steps
Decisions & Loops If documentRef is empty If documentRef is empty
Yes
ValuesShow in the output: Could not open attachmentLabel; it was left alone.Show in the output: Could not open attachmentLabel; it was left alone.
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
Values Set documentDetails to an empty list. Set documentDetails to an empty list.
Values Set failure to blank. Set failure to blank.
Values Set setAsideReason to blank. Set setAsideReason to blank.
Decisions & Loops If a step below fails, save the error as readError If a step below fails, save the error as readError
Try
AI AgentRun an AI agent: Read the attached document and pull out each of these fields, in this order: - What kind… and save it as reading.Run an AI agent: Read the attached document and pull out each of these fields, in this order: - What kind… and save it as reading.
Decisions & LoopsFor each answer in reading.output.fields:For each answer in reading.output.fields:
Values Set answerName to blank. Set answerName to blank.
Decisions & Loops If a step below fails, save the error as answerNameError If a step below fails, save the error as answerNameError
Try
TextTrim answer.name and save it as answerName.Trim answer.name and save it as answerName.
On error
No steps
Values Set answerValue to blank. Set answerValue to blank.
Decisions & Loops If a step below fails, save the error as answerValueError If a step below fails, save the error as answerValueError
Try
TextTrim answer.value and save it as answerValue.Trim answer.value and save it as answerValue.
On error
Decisions & LoopsIf a step below fails, save the error as answerListErrorIf a step below fails, save the error as answerListError
Try
TextJoin answer.value with , and save it as answerValue.Join answer.value with , and save it as answerValue.
On error
No steps
Decisions & Loops If answerValue isn't empty If answerValue isn't empty
Yes
ListsAdd answerName: answerValue truncated to 300 characters using … as the ellipsis to the end of documentDetails and save it as documentDetails.Add answerName: answerValue truncated to 300 characters using … as the ellipsis to the end of documentDetails and save it as documentDetails.
No
No steps
On error
TextBuild the text it could not be read (@readError.message) and save it as failure.Build the text it could not be read (@readError.message) and save it as failure.
Values Set chosenName to blank. Set chosenName to blank.
Values Set chosenFolder to blank. Set chosenFolder to blank.
Decisions & Loops If documentDetails isn't empty If documentDetails isn't empty
Yes
ValuesSet choiceBrief to:Set choiceBrief to:Decide which folder a document belongs in, using only what the document itself says. The folders:
ValuesSet choiceRules to:Set choiceRules to:Reply with exactly one folder name from the list above and nothing else - no explanation, no punctuation. If the document does not clearly belong in exactly one of them, or you are not sure, reply with the single word UNCLEAR. Do not guess.
ValuesSet choicePrompt to choiceBrief@choicesTextSet choicePrompt to choiceBrief@choicesText What the document says: documentDetails joined by a new line@choiceRules.
ValuesSet verdict to blank.Set verdict to blank.
Decisions & LoopsIf a step below fails, save the error as choiceErrorIf a step below fails, save the error as choiceError
Try
OpenAIOpenAIAsk OpenAI: choicePrompt. Save it as chosen.Ask OpenAI: choicePrompt. Save it as chosen.
TextMake chosen.response_text lowercase and save it as verdict.Make chosen.response_text lowercase and save it as verdict.
On error
TextBuild the text the folder could not be chosen (@choiceError.message) and save it as failure.Build the text the folder could not be chosen (@choiceError.message) and save it as failure.
TextTrim verdict with replaced by " and save it as verdict.Trim verdict with replaced by " and save it as verdict.
Decisions & LoopsIf verdict ends with . (ignoring case)If verdict ends with . (ignoring case)
Yes
TextTrim verdict truncated to the length of verdict minus 1 characters and save it as verdict.Trim verdict truncated to the length of verdict minus 1 characters and save it as verdict.
No
No steps
Decisions & LoopsFor each destination in filingDestinations:For each destination in filingDestinations:
Decisions & Loops If destination.name in lowercase is verdict If destination.name in lowercase is verdict
Yes
ValuesChange chosenName to destination.name.Change chosenName to destination.name.
ValuesChange chosenFolder to destination.folder.Change chosenFolder to destination.folder.
No
No steps
Else if @failure is empty
ValuesChange setAsideReason to nothing could be read out of it.Change setAsideReason to nothing could be read out of it.
Otherwise
No steps
Decisions & Loops If chosenFolder is empty and failure is empty If chosenFolder is empty and failure is empty
Yes
Decisions & LoopsIf setAsideReason is emptyIf setAsideReason is empty
Yes
ValuesChange setAsideReason to its contents did not clearly match one of your folders.Change setAsideReason to its contents did not clearly match one of your folders.
No
No steps
No
No steps
Decisions & Loops If chosenFolder isn't empty If chosenFolder isn't empty
Yes
ValuesAdd 1 to attachmentNumber.Add 1 to attachmentNumber.
TextReplace the pattern \.pdf$ with in attachmentLabel (ignoring case) and save it as fileStem.Replace the pattern \.pdf$ with in attachmentLabel (ignoring case) and save it as fileStem.
TextBuild the text @fileStem (@messageId).pdf and save it as boxName.Build the text @fileStem (@messageId).pdf and save it as boxName.
Decisions & LoopsIf namesOnMessage contains boxNameIf namesOnMessage contains boxName
Yes
TextBuild the text @fileStem (@messageId @attachmentNumber).pdf and save it as boxName.Build the text @fileStem (@messageId @attachmentNumber).pdf and save it as boxName.
No
No steps
ListsAdd boxName to the end of namesOnMessage and save it as namesOnMessage.Add boxName to the end of namesOnMessage and save it as namesOnMessage.
ValuesSet storedLink to blank.Set storedLink to blank.
ValuesSet uploadProblem to blank.Set uploadProblem to blank.
Decisions & LoopsIf a step below fails, save the error as uploadErrorIf a step below fails, save the error as uploadError
Try
BoxBoxUpload chosenFolder to Box folder documentRef. Save it as stored.Upload chosenFolder to Box folder documentRef. Save it as stored.
TextTrim stored.file_link and save it as storedLink.Trim stored.file_link and save it as storedLink.
On error
TextSplit uploadError.message by a new line and save it as uploadErrorLines.Split uploadError.message by a new line and save it as uploadErrorLines.
ListsGet the first item of uploadErrorLines and save it as uploadProblem.Get the first item of uploadErrorLines and save it as uploadProblem.
Decisions & LoopsIf a step below fails, save the error as lookupErrorIf a step below fails, save the error as lookupError
Try
BoxBoxList items in Box folder chosenFolder. Save it as folderItems.List items in Box folder chosenFolder. Save it as folderItems.
ListsKeep each item in folderItems.items where item.name is boxName and save it as sameName.Keep each item in folderItems.items where item.name is boxName and save it as sameName.
Decisions & LoopsIf sameName isn't emptyIf sameName isn't empty
Yes
TextBuild the text https://app.box.com/file/@sameName[0].id and save it as storedLink.Build the text https://app.box.com/file/@sameName[0].id and save it as storedLink.
No
No steps
On error
No steps
Decisions & LoopsIf storedLink isn't emptyIf storedLink isn't empty
Yes
ValuesAdd 1 to filedCount.Add 1 to filedCount.
ValuesAdd 1 to handledCount.Add 1 to handledCount.
ValuesShow in the output: Filed attachmentLabel under chosenName as boxName.Show in the output: Filed attachmentLabel under chosenName as boxName.
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
TextBuild the text it could not be copied into the '@chosenName' folder in Box (@uploadProblem) and save it as failure.Build the text it could not be copied into the '@chosenName' folder in Box (@uploadProblem) and save it as failure.
No
No steps
Values Add 1 to handledCount. Add 1 to handledCount.
Decisions & Loops If a step below fails, save the error as sortingLabelError If a step below fails, save the error as sortingLabelError
Try
GmailGmailAdd label needsSortingLabel to email messageId.Add label needsSortingLabel to email messageId.
On error
ListsAdd attachmentLabel on messageDescription: the '@needsSortingLabel' label could not be added (sortingLabelError.message) to the end of problems and save it as problems.Add attachmentLabel on messageDescription: the '@needsSortingLabel' label could not be added (sortingLabelError.message) to the end of problems and save it as problems.
Decisions & Loops If failure isn't empty If failure isn't empty
Yes
ListsAdd attachmentLabel on messageDescription: failure to the end of problems and save it as problems.Add attachmentLabel on messageDescription: failure to the end of problems and save it as problems.
Decisions & LoopsIf filedCount is 0If filedCount is 0
Yes
Decisions & LoopsStop and report a problem: Stopped before filing anything: attachmentLabel on messageDescription was not filed because failure. It is labelled '@needsSortingLabel' for you. Check the connection or setting this points to; the rest of your mail is left for the next run.Stop and report a problem: Stopped before filing anything: attachmentLabel on messageDescription was not filed because failure. It is labelled '@needsSortingLabel' for you. Check the connection or setting this points to; the rest of your mail is left for the next run.
No
No steps
No
ListsAdd attachmentLabel on messageDescription: setAsideReason to the end of leftForYou and save it as leftForYou.Add attachmentLabel on messageDescription: setAsideReason to the end of leftForYou and save it as leftForYou.
Values Show in the output: Left for you: attachmentLabel. Show in the output: Left for you: attachmentLabel.
On error
ListsAdd messageDescription: messageError.message to the end of problems and save it as problems.Add messageDescription: messageError.message to the end of problems and save it as problems.
Decisions & LoopsIf a step below fails, save the error as setAsideErrorIf a step below fails, save the error as setAsideError
Try
GmailGmailAdd label processedLabel to email message.Add label processedLabel to email message.
GmailGmailAdd label needsSortingLabel to email message.Add label needsSortingLabel to email message.
On error
No steps
Decisions & LoopsIf filedCount is 0If filedCount is 0
Yes
Decisions & LoopsStop and report a problem: Stopped before filing anything: messageDescription could not be handled (messageError.message). The rest of your mail is left for the next run.Stop and report a problem: Stopped before filing anything: messageDescription could not be handled (messageError.message). The rest of your mail is left for the next run.
No
No steps
Decisions & LoopsIf handledCount is 0 and leftForYou is empty and problems is emptyIf handledCount is 0 and leftForYou is empty and problems is empty
Yes
Decisions & LoopsFinish the run: No new documents arrived to sort.Finish the run: No new documents arrived to sort.
No
No steps
ValuesSet reportLines to an empty list.Set reportLines to an empty list.
ListsAdd Filed filedCount document(s) into Box. to the end of reportLines and save it as reportLines.Add Filed filedCount document(s) into Box. to the end of reportLines and save it as reportLines.
Decisions & LoopsIf messagesLeft is greater than 0If messagesLeft is greater than 0
Yes
ListsAdd messagesLeft more email(s) are waiting: this run reached its limit of maxDocumentsPerRun documents, and the next run carries on from there. to the end of reportLines and save it as reportLines.Add messagesLeft more email(s) are waiting: this run reached its limit of maxDocumentsPerRun documents, and the next run carries on from there. to the end of reportLines and save it as reportLines.
No
No steps
Decisions & LoopsIf leftForYou isn't emptyIf leftForYou isn't empty
Yes
TextJoin leftForYou with "Join leftForYou with - and save it as leftForYouList.
ListsAdd Labelled '@needsSortingLabel' for you to file by hand:Add Labelled '@needsSortingLabel' for you to file by hand: - leftForYouList to the end of reportLines and save it as reportLines.
No
No steps
Decisions & LoopsIf problems isn't emptyIf problems isn't empty
Yes
TextJoin problems with "Join problems with - and save it as problemList.
ListsAdd These were not filed because something went wrong, and are labelled '@needsSortingLabel' too:Add These were not filed because something went wrong, and are labelled '@needsSortingLabel' too: - problemList to the end of reportLines and save it as reportLines.
No
No steps
TextJoin reportLines with a new line and save it as runReport.Join reportLines with a new line and save it as runReport.
Decisions & LoopsIf problems isn't emptyIf problems isn't empty
Yes
Decisions & LoopsStop and report a problem: runReport.Stop and report a problem: runReport.
No
No steps
Decisions & LoopsFinish the run: runReport.Finish the run: runReport.