This workflow corresponds to n8n.io template #6527 — we link there as the canonical source.
This workflow follows the Chainllm → Form Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "9378321b-87f0-479f-8846-9e40a41c91ca",
"name": "On form submission",
"type": "n8n-nodes-base.formTrigger",
"position": [
-1240,
-140
],
"parameters": {
"options": {},
"formTitle": "Upload Training Plan",
"formFields": {
"values": [
{
"fieldType": "file",
"fieldLabel": "data",
"multipleFiles": false,
"requiredField": true,
"acceptFileTypes": ".jpg, .png, .pdf, .jpeg"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "634600e6-0b0b-4051-ade7-e0b9f1e353ac",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
228,
80
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"routine\": {\n \"type\": \"object\",\n \"properties\": {\n \"title\": {\n \"type\": \"string\",\n \"description\": \"The title of the routine\"\n },\n \"folder_id\": {\n \"type\": \"number\",\n \"nullable\": true,\n \"default\": 1352963,\n \"description\": \"The folder id the routine should be added to. Pass null to insert the routine into default 'My Routines' folder\"\n },\n \"notes\": {\n \"type\": \"string\",\n \"description\": \"Additional notes for the routine\"\n },\n \"exercises\": {\n \"type\": \"array\",\n \"description\": \"List of exercises in the routine\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"exercise_template_id\": {\n \"type\": \"string\",\n \"description\": \"The ID of the exercise template\"\n },\n \"superset_id\": {\n \"type\": \"integer\",\n \"nullable\": true,\n \"description\": \"The ID of the superset\"\n },\n \"rest_seconds\": {\n \"type\": \"integer\",\n \"nullable\": true,\n \"description\": \"The rest time in seconds (Pause). Never negative.\"\n },\n \"notes\": {\n \"type\": \"string\",\n \"nullable\": true,\n \"description\": \"Additional notes for the exercise\"\n },\n \"sets\": {\n \"type\": \"array\",\n \"description\": \"Sets for the exercise\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"type\": {\n \"type\": \"string\",\n \"enum\": [\"warmup\", \"normal\", \"failure\", \"dropset\"],\n \"description\": \"The type of the set\"\n },\n \"weight_kg\": {\n \"type\": \"number\",\n \"nullable\": true,\n \"description\": \"The weight in kilograms\"\n },\n \"reps\": {\n \"type\": \"integer\",\n \"nullable\": true,\n \"description\": \"The number of repetitions (wdhl.). Never negative.\"\n },\n \"distance_meters\": {\n \"type\": \"integer\",\n \"nullable\": true,\n \"description\": \"The distance in meters\"\n },\n \"duration_seconds\": {\n \"type\": \"integer\",\n \"nullable\": true,\n \"description\": \"The duration in seconds\"\n },\n \"custom_metric\": {\n \"type\": \"number\",\n \"nullable\": true,\n \"description\": \"A custom metric for the set. Currently used for steps and floors\"\n }\n }\n }\n }\n },\n \"required\": [\"exercise_template_id\", \"sets\"]\n }\n }\n },\n \"required\": [\"title\", \"exercises\"]\n }\n },\n \"required\": [\"routine\"]\n}"
},
"typeVersion": 1.2
},
{
"id": "9cf95512-03fc-44ce-8a4c-bdf828927ac2",
"name": "google/gemini-2.5-flash",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
60,
80
],
"parameters": {
"model": "google/gemini-2.5-flash",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "d13c6c4d-c938-4617-a679-5e4cd356168e",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1740,
-200
],
"parameters": {
"color": 7,
"width": 420,
"height": 280,
"content": "## Scan Any Workout Plan into the Hevy App with AI\n\nThis workflow extracts a workout plan from a PDF, uses AI to match the exercises to those available in the Hevy app, and automatically creates the routine for you.\nSetup:\n\nConfigure the trigger.\nAdd your OpenRouter & Hevy credentials.\nActivate and test!"
},
"typeVersion": 1
},
{
"id": "d7a3ba9a-d86d-42bf-a6f9-5f30e699398a",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1100,
-360
],
"parameters": {
"width": 260,
"height": 180,
"content": "This node converts the incoming PDF file into a Base64 text string. This format is required to send the file content to the AI model in the next step."
},
"typeVersion": 1
},
{
"id": "5b42918d-1446-4b91-b867-b9b0f771bfb7",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-800,
-360
],
"parameters": {
"width": 300,
"height": 180,
"content": "This node sends the PDF content to an AI model to perform OCR and extract all the text from the document.\nRequires: OpenRouter.ai API credentials."
},
"typeVersion": 1
},
{
"id": "a137506d-75c3-45e6-8c78-8f6e0104914f",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-440,
-360
],
"parameters": {
"width": 300,
"height": 180,
"content": "These nodes fetch all available exercises from your Hevy account and combine them into a single list. This list provides the context for the AI to know which exercises are valid.\nRequires: https://api.hevyapp.com/docs/ credentials."
},
"typeVersion": 1
},
{
"id": "c8ae0644-c339-4929-990c-ca99ab0247b1",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
40,
-420
],
"parameters": {
"width": 340,
"height": 240,
"content": "This is the core of the workflow.\nIt uses Google's Gemini model to:\n\nRead the messy text from the PDF.\nCompare the exercises mentioned against the official Hevy exercise list.\nCreate a clean, structured JSON output ready for the Hevy API.\nThe prompt and the Schema in the Structured Output Parser are critical here."
},
"typeVersion": 1
},
{
"id": "88dbdb21-34e3-4faf-a226-2b3633b1dc4c",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
440,
-340
],
"parameters": {
"content": "This final node takes the structured data from the AI and makes a POST request to the Hevy API, creating the complete workout routine in your account."
},
"typeVersion": 1
},
{
"id": "0002347a-a9e3-43a3-be10-1773e3efd6cf",
"name": "Convert PDF to Base64",
"type": "n8n-nodes-base.extractFromFile",
"position": [
-1020,
-140
],
"parameters": {
"options": {},
"operation": "binaryToPropery"
},
"typeVersion": 1
},
{
"id": "fcce2688-1ecc-4337-9737-3b92764ce718",
"name": "Extract Text from PDF via AI",
"type": "n8n-nodes-base.httpRequest",
"position": [
-800,
-140
],
"parameters": {
"url": "https://openrouter.ai/api/v1/chat/completions",
"method": "POST",
"options": {},
"jsonBody": "={\n \"model\": \"google/gemini-2.5-flash\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"Please perform OCR on this document and provide the full extracted content in your response\"\n },\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": \"data:image/jpeg;base64,{{ $json.data\u00a0}}\"\n }\n }\n ]\n }\n ]\n}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "51ad34e9-0aaa-4ebe-adfa-6fd263cba56c",
"name": "Get Exercise List from Hevy",
"type": "n8n-nodes-base.httpRequest",
"position": [
-580,
-140
],
"parameters": {
"url": "https://api.hevyapp.com/v1/exercise_templates",
"options": {
"pagination": {
"pagination": {
"parameters": {
"parameters": [
{
"name": "page",
"value": "={{ $pageCount + 1 }}"
}
]
},
"maxRequests": 10,
"requestInterval": 100,
"limitPagesFetched": true,
"paginationCompleteWhen": "receiveSpecificStatusCodes",
"statusCodesWhenComplete": "404"
}
}
},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "pageSize",
"value": "100"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "20ccb4bb-b0bd-4567-bb26-1015b3fa0105",
"name": "Format Exercise Names",
"type": "n8n-nodes-base.set",
"position": [
-360,
-140
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "85dd9d76-d527-43ce-bd36-955bb2a3b3f6",
"name": "exercise_templates",
"type": "array",
"value": "={{ $json.exercise_templates }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "16434525-113f-4f93-b6df-05a096658087",
"name": "Combine Exercise List",
"type": "n8n-nodes-base.aggregate",
"position": [
-140,
-140
],
"parameters": {
"options": {
"mergeLists": true
},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "exercise_templates"
}
]
}
},
"typeVersion": 1
},
{
"id": "291e75d6-a372-4a15-ad66-92043cfc107b",
"name": "Match Exercises & Structure Routine",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
80,
-140
],
"parameters": {
"text": "=# GOAL\nYour task is to analyze the provided OCR data of a workout routine, interpret its contents, and convert it into a structured JSON object. You must strictly adhere to the provided JSON schema.\n\n# CONTEXT\n- The OCR data is imperfect and may contain typos or formatting quirks.\n- You will be given a list of available exercises from an application database (`Base Exercise Data`).\n\n# INPUT DATA\n1. **OCR Data:**\n ```\n {{ $('Extract Text from PDF via AI').item.json.choices[0].message.content }}\n ```\n2. **Base Exercise Data:**\n ```json\n {{ $json.exercise_templates.toJsonString() }}\n ```\n\n# EXTRACTION RULES & SPECIFICS\nThis section contains specific instructions to guide the extraction process.\n\n- **Exercise Matching:** For each exercise name found in the OCR data, you MUST find the most semantically similar exercise in the `Base Exercise Data`. Use its `id` for the `exercise_template_id` in the final JSON. Do not invent new IDs.\n- **Default Folder:** If no folder is specified, use the default `folder_id` of `1352963`.\n- **Interpreting Sets:** A line like \"3x10 @ 50kg\" means 3 sets of 10 repetitions with 50 kilograms.\n- **Rest Times:** Look for terms like \"Pause,\" \"Rest,\" or time values (e.g., \"90s,\" \"1 min\") between exercises to determine the `rest_seconds`.\n- **Supersets:** If exercises are grouped with labels like \"A1, A2\" or listed together without a rest period, they are a superset. Assign them the same, incrementing `superset_id` (e.g., the first superset pair gets `superset_id: 1`, the next pair gets `superset_id: 2`).\n- **Tempo Information (optional):** Look for a 3 or 4-digit number (e.g., `4010`, `31X0`, `311`), often labeled \"Tempo\". If found for an exercise, capture this and include it in that exercise's `notes` field (e.g., `notes: \"Tempo: 4010\"`).\n\n\n\n# OUTPUT INSTRUCTIONS\n- Produce ONLY the final JSON object.\n- Do not include any explanatory text, markdown formatting, or apologies in your response.\n- The output must be a single, valid JSON object conforming to the schema.\n",
"batching": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.7
},
{
"id": "7d710a1d-7e86-4da1-bda1-741d8008cb02",
"name": "Create Hevy Routine",
"type": "n8n-nodes-base.httpRequest",
"position": [
456,
-140
],
"parameters": {
"url": "https://api.hevyapp.com/v1/routines",
"method": "POST",
"options": {},
"jsonBody": "={{ $json.output.toJsonString() }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
}
],
"connections": {
"On form submission": {
"main": [
[
{
"node": "Convert PDF to Base64",
"type": "main",
"index": 0
}
]
]
},
"Combine Exercise List": {
"main": [
[
{
"node": "Match Exercises & Structure Routine",
"type": "main",
"index": 0
}
]
]
},
"Convert PDF to Base64": {
"main": [
[
{
"node": "Extract Text from PDF via AI",
"type": "main",
"index": 0
}
]
]
},
"Format Exercise Names": {
"main": [
[
{
"node": "Combine Exercise List",
"type": "main",
"index": 0
}
]
]
},
"google/gemini-2.5-flash": {
"ai_languageModel": [
[
{
"node": "Match Exercises & Structure Routine",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "Match Exercises & Structure Routine",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Get Exercise List from Hevy": {
"main": [
[
{
"node": "Format Exercise Names",
"type": "main",
"index": 0
}
]
]
},
"Extract Text from PDF via AI": {
"main": [
[
{
"node": "Get Exercise List from Hevy",
"type": "main",
"index": 0
}
]
]
},
"Match Exercises & Structure Routine": {
"main": [
[
{
"node": "Create Hevy Routine",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuthopenRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates the creation of workout routines in the Hevy app by extracting exercise information from an uploaded PDF or Image using AI. Tired of manually typing workout plans into the Hevy app? Whether your coach sends them as Google Docs, PDFs, or you have a…
Source: https://n8n.io/workflows/6527/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Deep Research new (fr). Uses outputParserStructured, formTrigger, chainLlm, form. Event-driven trigger; 82 nodes.
My workflow 53. Uses formTrigger, httpRequest, lmChatOpenAi, form. Event-driven trigger; 74 nodes.
Episode 23: UGC with nanobanana. Uses lmChatOpenAi, lmChatOllama, lmChatDeepSeek, lmChatOpenRouter. Event-driven trigger; 74 nodes.
Automate your lead intake, scoring, and outreach pipeline. This workflow collects leads from forms, enriches and scores them using Relevance AI, routes them by quality, and triggers the right follow-u
Mistral OCR is a cutting-edge document understanding API that improves how businesses extract and process information from complex documents. With top scores in benchmarks for accuracy and comprehensi