AWS Builder Center

Build a smart invoice processor with Amazon Quick in 30 minutes

Build an end-to-end invoice-processing automation on Amazon Quick Automate — from setting up the AWS action connectors and IAM role to extracting invoice data with Textract and Bedrock, with the real-world gotchas you'll hit along the way.

Introduction

Every finance team has the same nightmare: a shared inbox (or S3 bucket) filling up with invoice PDFs that need to be opened, read, categorized, checked for anomalies, and routed for approval. It's tedious. It's error-prone. And it's exactly the kind of workflow that AI should handle.
Amazon Quick Automate (the scheduled tasks engine inside Amazon Quick ) lets you orchestrate multi-step AI workflows using plain English. No Lambda functions to write. No Step Functions state machines to debug. You describe what you want done, set a schedule, and Quick's agent handles the rest — calling AWS services, processing data, and delivering results straight to your Activity Feed.
In this post, we'll build a Smart Invoice Processor that:
  1. Watches an S3 bucket for new invoice PDFs
  2. Extracts text using Amazon Textract
  3. Analyzes and categorizes each invoice using Amazon Bedrock (Claude)
  4. Flags anomalies (high amounts, duplicates, missing fields)
  5. Posts a formatted summary to your Activity Feed — every 30 minutes, hands-free
Let's build it.

Prerequisites

Before you start, make sure you have:
✅ An AWS account with permissions to create S3 buckets, IAM roles, and Quick action connectors.
✅ An S3 bucket to store your invoices. I've named mine my-anycompany-invoices-inbox, but you can use any name you like (S3 bucket names are globally unique, so you'll likely need your own twist). Inside the bucket, create two folders:
incoming/ — where new invoices land for processing
processed/ — where the workflow moves invoices once they're done
✅ Pick one AWS Region and stick to it. Create the bucket, the action connectors, and run your actions in the same Region (Textract reads the document from S3 in its own Region, so a mismatch will break the flow).
✅ Amazon Bedrock access with the Claude model enabled (Claude 3 Sonnet or later)
💡 Tip: If you haven't enabled the AWS service connectors yet, open Amazon Quick → Settings → Capabilities → Connections and enable the ones you need. Most AWS services are available as MCP tool integrations.

Setup: AWS Actions and the IAM Role Behind Them

Before you build anything in the canvas, Quick Automate needs permission to call AWS services on your behalf. This is the part that trips most people up, so we'll get it right first.
There are two different kinds of S3 (and AWS) integration in Quick, and they are not interchangeable:
The connectors/integrations you see in the regular UI are for data ingestion and knowledge bases — they will never appear as an action your agent can call.
AWS service action connectors are what an automation actually invokes (S3, Textract, Bedrock, …). These are created in the admin console and require an IAM identity for authentication.
So setup is two moves: (1) create an IAM role Quick can assume, then (2) create the AWS action connectors in the Quick admin console that use that role.

Step 1 — Create the IAM role


The role that your action connectors will use need two policies.

a) Trust policy — lets the Quick service assume the role. The service principal is quicksight.amazonaws.com (Quick Suite is built on QuickSight, which keeps that principal). The aws:SourceAccount condition is a confused-deputy guard that scopes assumption to your account:
{ "Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowQuickToAssume",
"Effect": "Allow",
"Principal":
{ "Service": "quicksight.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "aws:SourceAccount": "<YOUR_ACCOUNT_ID>" } } }
]
}
b) Permissions policy — what the role is actually allowed to do. Grant only the actions your workflow uses. For an invoice-processing flow (read a document from S3, OCR it with Textract, summarize with Bedrock) that's:
{
"Version": "2012-10-17",
"Statement":
[
{ "Sid": "S3InvoiceBucket",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket", "s3:PutObject"],
"Resource": [ "arn:aws:s3:::<YOUR_BUCKET>", "arn:aws:s3:::<YOUR_BUCKET>/``" ] }
,
{ "Sid": "Textract",
"Effect": "Allow",
"Action": [ "textract:DetectDocumentText", "textract:AnalyzeDocument", "textract:StartDocumentAnalysis", "textract:GetDocumentAnalysis" ],
"Resource": "``" }
,
{ "Sid": "BedrockInvoke",
"Effect": "Allow",
"Action": ["bedrock:InvokeModel"],
"Resource": "*" }
]
}
Let's start with the IAM permissions policy
  1. Sign in to the AWS Management Console and open the IAM console (search "IAM" in the top search bar).
  2. In the left navigation pane, choose Policies.
  3. Choose Create policy (top right).
  4. In the Policy editor, switch from Visual to JSON (toggle in the top-right of the editor).
  5. Delete the placeholder content and paste your policy JSON (the S3 / Textract / Bedrock permissions from the Setup section).
  6. Choose Next.
  7. Give the policy a Policy name (e.g. Quick_Workflow_ProcessingInvoices) and an optional description.
  8. Choose Create policy.

Creating the IAM role and attaching the policy

1) In the IAM console left nav, choose Roles, then Create role.
2) For Trusted entity type, choose Custom trust policy (this lets you paste the quicksight.amazonaws.com trust policy directly).
3) In the Custom trust policy editor, paste your trust policy JSON, then choose Next.
4) On the Add permissions page, search for the policy you just created (e.g. Quick_Workflow_ProcessingInvoices), select its checkbox, and choose Next
5) Enter a Role name (e.g. Quick-InvoicesWorkflow) and an optional description.
6) Review the trusted entities and permissions, then choose Create role.
7) Open the role and copy its ARN (e.g. arn:aws:iam::<YOUR_ACCOUNT_ID>:role/Quick-InvoicesWorkflow) — you'll need it when you create the action connectors in the Quick admin console.
Tip: you can verify the role without running the workflow using the IAM policy simulator (aws iam simulate-principal-policy). It tells you allowed or implicitDeny for a given action/resource — much faster than trial-and-error in the canvas.

Step 2 — Create the AWS action connectors in the Quick admin console

With the role in place, register the connectors:
1) Open the Amazon Quick Suite console and choose Manage Account.
2) In the left nav, choose Permissions.
3) Choose AWS action connectors.
4) Create a connector for each service your workflow needs (S3, Textract, Bedrock), and bind it to the IAM role from Step 1.
4.1 ) Click "New action"
4.2) Click the connector
4.3) Click Next
4.4) Give it a name and in Role ARN paste the value that you copied from the previous step (Create role-step 7)
4.5) Repeat the same process for Bedrock and Textract
5) Now you should have 3 connectors. These connectors are the things that show up as actions when you build a Quick Automate agent. (Action connectors are a Quick Automate feature specifically — they won't appear for other agent types.)

Step 3: Create an Automation Group

To create a new automation workflow, you first need to create an Automation Group to organize workflows from a specific domain and enable easier sharing across users and groups.
1) Go to Automations > Groups
2) Click on Create Group
3) Give a Name eg. Invoice-Processing-Group
4) In the Add actions screen, select the Textract, Amazon S3 and Bedrock Runtime connectors, and click on Next
5) Skip the next section "Create your first credentials (optional) and click Done

Create a new project

1) Click on Create Project
2) Give it a name and choose the group that you created in the previous step
3) Click on Create
4) Click on Start Building to begin building your automation
5) Provide instructions to generate the template
You can use the following full automation prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
## Objective: Smart Invoice Processor

You are an invoice processing agent. Every time you run, perform the following steps:

### 1. Check S3 for New Invoices
- List all PDF files in the S3 bucket `my-company-invoices-inbox` under the `incoming/` prefix.
- Compare against files in the `processed/` prefix to identify NEW files only (files that exist in `incoming/` but not in `processed/`).
- If no new files are found, post a brief "No new invoices" status to the Activity Feed and stop.

### 2. Extract Text from Each New Invoice
- For each new PDF file found:
- Download it from S3.
- Use Amazon Textract to extract all text content from the document.
- Store the extracted text for analysis.

### 3. Analyze Each Invoice with Bedrock
- For each extracted invoice text, use Amazon Bedrock (Claude) to analyze and extract the following structured data:
- **Vendor Name**: The company that issued the invoice
- **Invoice Number**: The unique invoice identifier
- **Invoice Date**: The date on the invoice
- **Due Date**: Payment due date (if present)
- **Total Amount**: The total amount due (in USD)
- **Line Items**: A list of individual charges with description and amount
- **Category**: Classify as one of: Office Supplies, Software/SaaS, Professional Services, Travel & Expenses, Hardware, Utilities, or Other
- **Payment Terms**: Net 30, Net 60, etc. (if stated)

### 4. Run Anomaly Detection
- Flag the invoice with anomaly indicators if ANY of the following are true:
- 🔴 **High Amount**: Total amount exceeds $5,000
- 🔴 **Missing Critical Fields**: Vendor name, invoice number, or total amount could not be extracted
- 🟡 **Duplicate Suspicion**: Same vendor name + same total amount as another invoice processed today
- 🟡 **Overdue Risk**: Due date is within 3 days or already past
- 🟡 **Unusual Category**: Invoice categorized as "Other" (may need manual review)

### 5. Move Processed Files
- After successful processing, copy each file from `incoming/` to `processed/` prefix (preserving the filename), then delete it from `incoming/`.

### 6. Post Results to Activity Feed
- Post a single consolidated summary with:
- Header: "📄 Invoice Processing Report — [today's date]"
- Count: "Processed X new invoice(s)"
- For each invoice, show:
- Severity icon: 🟢 (no issues), 🟡 (needs review), or 🔴 (anomaly detected)
- Vendor Name | Invoice # | Amount | Category
- Any anomaly flags with explanation
- Footer: Total value of all invoices processed

Format the output as a clean, scannable report. Use tables where helpful.

From Prompt to Plan (But Not Yet a Working Automation)

With your prompt the Automation Assistant does the first piece of heavy lifting: it generates a high-level plan. You'll see it appear in the canvas as a sequence of steps, each one a plain-language description of part of your process: "Read the invoice from the incoming folder," "Extract the text," "Summarize the key fields," "Save the result and move the file to processed."
It's tempting to hit run at this point — but here's the key thing to understand:
The plan is intent, not implementation. Each step describes what should happen, but it isn't wired up to do anything yet.
Think of it as two layers:
  • The plan layer — the steps you see now. These are the Assistant's understanding of your process, written in natural language. No connectors are called, no code runs, no inputs are mapped.
  • The action layer — the concrete, executable building blocks that live inside each step: connector actions (S3, Textract, Bedrock), code blocks, loops, and the variable wiring that passes data from one step to the next.
A freshly generated plan has the first layer but not the second. That's why each step shows up as not yet implemented — it's a placeholder waiting to be turned into real actions.

Two ways to implement each step

For every step, you choose how to fill in that action layer:
  1. Do it manually. Drag the action you want from the Actions library into the step and configure it yourself — pick the connector, set its inputs (which bucket, which object key, which Region, which model), and map where each value comes from (a previous step's output or a variable). This gives you full control and no surprises, which is ideal for the AWS connector steps where the inputs are specific.
  2. Generate with instructions. Let the Assistant implement the step for you from its natural-language description. It chooses the actions, writes any code, and wires the inputs and outputs to match the surrounding steps. This is fast and great for getting unstuck — but treat it as a first draft: it makes reasonable choices, and you'll sometimes need to open the generated action and adjust an input, a variable type, or a line of code.

Implementing the Steps with "Generate with Instructions"

For this scenario, we'll implement each step using "Generate with Instructions" rather than building every action by hand. It's the quicker path: you describe what the step should do, and the Automation Assistant turns that description into the concrete actions and wiring for you. We'll work through the plan step by step, generating one step at a time so we can review each result before moving on.
To keep things focused, we'll walk through the first step in detail as our demonstration — once you've seen how it works, the remaining steps follow the same pattern.
So let's get started. On the first step, "Check S3 for new invoices," click Build with Instructions.
On the next window that opens up, review the instructions and click Generate
Once the Assistant finishes, you'll notice the step is no longer a plain placeholder — it now contains a set of detailed actions. Click on the step's panel to expand it, and the full workflow behind Step 1 opens up.
Take a moment to look at what it generated. Rather than a single call, the step is a small orchestrated sequence — it loops over the files in the bucket, writes log messages so you can trace what happened on each run, saves values into variables that later steps will use, and stitches these actions together in order.

What Step 1 actually does


Walking through the generated logic, here's what's happening under the hood:

  1. Look in the incoming/ folder — the step checks the S3 bucket's incoming/ folder and makes a list of all the PDF files sitting there.
  2. Look in the processed/ folder — it also checks the processed/ folder to see which PDF files have already been handled in previous runs.
  3. Compare the two lists — for each PDF in incoming/, it asks: "Does a file with this same name already exist in processed/?"
    If yes → skip it (already processed).
    If no → it's a new invoice that needs processing.
  4. Create a list of new invoices — every file that exists in incoming/ but not in processed/ gets added to a list called new_invoice_files.
  5. Log the result — finally, it reports either "No new invoices found" or "Found X new invoice files," so you always know what happened on that run.

Repeat for the Remaining Steps

From here, the process is the same for every step in the plan. Work down the canvas and, for each step, click Generate with Instructions, let the Assistant implement it, then expand the step and review what it produced before moving on. Generating and checking one step at a time — rather than all at once — makes it far easier to spot where something needs a tweak.
For our invoice workflow, the remaining steps follow naturally: extract the text from each new invoice with Textract, send that text to Bedrock to pull out the key fields, save the structured result, and finally move the processed file into the processed/ folder so it isn't picked up again.

Gotchas to Watch For

"Generate with Instructions" gets you most of the way, but a few details consistently need a human eye. These are the ones that cost the most time if you don't know them up front:
Keep everything in one Region. Textract reads the document straight from S3 and requires the bucket to be in the same Region as the Textract call. If your bucket is in us-east-2 but an action runs in us-east-1, you'll get confusing errors like "the region … is wrong" or "unable to get object metadata from S3." Make sure your bucket, your action connectors, and your actions all use the same Region.
Bedrock needs an inference profile, not the raw model ID. Newer Claude models can't be called on-demand by their plain model ID — you'll see "Invocation of model ID … with on-demand throughput isn't supported. Retry … with an inference profile." Use the cross-region inference profile ID instead: the model ID with a geo prefix, e.g. us.anthropic.claude-sonnet-4-6 for US Regions (eu. / apac. elsewhere).
Test as You Go
Before moving on to the next step, use the Run (or Debug) button to actually execute what you've built so far and confirm it works. Don't wait until the whole automation is assembled — testing step by step means that when something breaks, you know it's the step you just implemented, not one of a dozen others.
This is also how you'll surface the gotchas above: a real run is what tells you whether the Region is right, whether Bedrock accepts the model ID, or whether a code step is missing a variable. Each run produces a fresh result (and a new Request ID), so if you see the same error with the same Request ID twice, you're looking at a stale result — re-run to get the real outcome.
Fix anything the run surfaces, confirm it passes, then move on to the next step and repeat: generate → review → run → fix → continue.
Also note that when a run surfaces an error, you don't always have to debug it by hand. Quick Automate offers a Fix with Assistant option that reads the error and suggests (or applies) a correction for you — re-mapping an input, adjusting a code block, or reworking the action that failed.

Wrap-Up

Once you've implemented all the steps, run the full workflow end to end and review the results — check that new invoices in incoming/ were picked up, their text extracted, the key fields pulled out, and each file moved into processed/ so it won't be handled again.
Congratulations! 🎉 You've built a complete, agentic invoice-processing workflow on Quick Automate. With a single run, it now scans your S3 bucket for new invoices, uses Textract to read each document, calls Bedrock to intelligently extract and structure the key fields, saves the results, and moves processed files aside — all without manual data entry. Just as importantly, the incoming/ vs processed/ design makes it safe to re-run anytime, so it fits naturally into a scheduled or on-demand process.
From here, you can extend it however your use case needs — write the extracted fields to a database or spreadsheet, trigger a notification when an invoice is processed, add validation rules, or branch on invoice type. The same pattern you used here — generate, review, run, fix — applies to every new step you add.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article