A family firend run food business was drowning in handwritten orders. Here's how I built an n8n pipeline that uses vision API to read handwritten order slips and log them automatically to Google Sheets without the need for manual data entry.
In this article I'll walk you through exactly how I built it, the specific problems I hit along the way, and how you can replicate it in an afternoon.
Prerequisites:
Before you start you'll need:
A running n8n instance (cloud or self-hosted)
An Anthropic API key
A Google account with Drive and Sheets access
Basic familiarity with n8n nodes
Step 1: Setting Up the Trigger
I used a Google Drive trigger node set to watch a specific folder called Orders.
Whenever a new image file lands in that folder — whether dropped there manually or sent from a phone — the workflow fires.
To configure it:
- Add a Google Drive Trigger node in n8n
- Connect your Google account via OAuth (more on the gotcha here later)
- Set the event to "File Created"
- Point it at your incoming orders folder
If you'd rather use Telegram, you can swap this node for a Telegram Trigger instead. I tested both. The Drive approach works better for a shared team workflow. Telegram is better if you're the only one sending orders in.
Step 2: Sending the Image to Claude
Once the trigger fires and the file is downloaded, we send it to Claude's vision API using an HTTP Request node. The API endpoint is:
POST https://api.anthropic.com/v1/messages
The headers you need:
{
"x-api-key": "YOUR_ANTHROPIC_API_KEY",
"anthropic-version": "2023-06-01",
"content-type": "application/json"
}
The body is where the prompt lives. Here's the exact prompt I used:
{
"model": "claude-opus-4-6",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "{{ $binary.data.toString('base64') }}"
}
},
{
"type": "text",
"text": "This is a handwritten food order slip. Extract all order items, quantities, and any special instructions. Return the result as a JSON object with the following structure: { \"items\": [ { \"name\": \"\", \"quantity\": 0, \"notes\": \"\" } ], \"table\": \"\", \"timestamp\": \"\" }. Return only the JSON, no explanation."
}
]
}
]
}
Step 3: Parsing Claude's Response
Claude returns a response object. The actual content you want is nested inside it at:
{{ $json.content[0].text }}
That string is your JSON order data, but it's still a string at this point. Add a Code node in n8n with this snippet to parse it cleanly:
const raw = $input.first().json.content[0].text;
const parsed = JSON.parse(raw);
return [{ json: parsed }];
After this node your workflow has a clean JavaScript object with the order items, table number, and any special instructions.
Step 4: Writing to Google Sheets
Add a Google Sheets node set to Append Row. Connect your Google account, point it at your orders spreadsheet, and map the fields from the parsed output.
Keep it simple and build on it such as:
- Timestamp
- Table
- Item
- Quantity
- Notes
Because the AI returns an array of items, you may have multiple rows per order slip. Handle this with a Split In Batches node before the Sheets node it loops through each item in the array and writes one row per item.
What Actually Tripped Me Up and lessons to learn
Google OAuth verification
When you first connect Google Drive and Google Sheets in n8n, Google flags the OAuth app as unverified if you're running a self-hosted instance. You'll see a warning screen. The fix is to click "Advanced" and then "Go to app" during development this is fine for a personal workflow.
Handwriting quality
AI vision handles surprisingly messy handwriting well, but very faint pencil or heavily smudged ink causes extraction errors. I added a simple validation steps such as if the returned items array is empty, the workflow sends a Telegram alert flagging the image for manual review.
That's it!
If you build this or adapt it for something else, drop a comment below.
Top comments (0)