Your first m9m workflow¶
A five-minute walkthrough: fetch JSON from an API, transform the response, return the result. Then add a webhook trigger and basic error handling.
What you'll build¶
┌─────────┐ ┌─────────────┐ ┌──────────────┐
│ Start │───▶│ Fetch Posts │───▶│ Format Output│
└─────────┘ └─────────────┘ └──────────────┘
A workflow that:
- Starts via the
Startnode (or a webhook trigger — added later) - Fetches a JSON post from
jsonplaceholder.typicode.com - Transforms the response into a compact summary
- Returns the output
Prerequisites¶
- m9m installed (installation guide)
curlfor API calls (or the Web UI athttp://localhost:8080)
Step 1 — define the workflow¶
Save as my-first-workflow.json:
{
"name": "My First Workflow",
"nodes": [
{
"id": "start",
"name": "Start",
"type": "n8n-nodes-base.start",
"position": [250, 300],
"parameters": {}
},
{
"id": "http",
"name": "Fetch Posts",
"type": "n8n-nodes-base.httpRequest",
"position": [450, 300],
"parameters": {
"url": "https://jsonplaceholder.typicode.com/posts/1",
"method": "GET"
}
},
{
"id": "set",
"name": "Format Output",
"type": "n8n-nodes-base.set",
"position": [650, 300],
"parameters": {
"assignments": [
{ "name": "title", "value": "={{ $json.title }}" },
{ "name": "summary", "value": "Post ID: {{ $json.id }} by User {{ $json.userId }}" }
]
}
}
],
"connections": {
"Start": {
"main": [[{"node": "Fetch Posts", "type": "main", "index": 0}]]
},
"Fetch Posts": {
"main": [[{"node": "Format Output", "type": "main", "index": 0}]]
}
}
}
This is the same JSON shape n8n uses. Workflows exported from n8n drop in here.
Step 2 — run it¶
CLI¶
Expected output:
{
"status": "success",
"data": [
{
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"summary": "Post ID: 1 by User 1"
}
]
}
REST API¶
# Create
curl -X POST http://localhost:8080/api/v1/workflows \
-H "Content-Type: application/json" \
-d @my-first-workflow.json
# Execute
curl -X POST http://localhost:8080/api/v1/workflows/{workflow-id}/execute
Step 3 — what each node does¶
Start node¶
Entry point. Passes input through, or emits a single empty data item if no input is provided.
HTTP Request node¶
{
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://jsonplaceholder.typicode.com/posts/1",
"method": "GET"
}
}
Makes an HTTP call. The parsed response body is exposed to downstream nodes as $json.
Set node¶
{
"type": "n8n-nodes-base.set",
"parameters": {
"assignments": [
{ "name": "title", "value": "={{ $json.title }}" },
{ "name": "summary", "value": "Post ID: {{ $json.id }} by User {{ $json.userId }}" }
]
}
}
Reshapes data with expressions. Use ={{ ... }} for a value computed entirely by an expression, or interpolate inline with {{ ... }} inside a string.
Connections¶
{
"Start": { "main": [[{ "node": "Fetch Posts", "type": "main", "index": 0 }]] },
"Fetch Posts": { "main": [[{ "node": "Format Output","type": "main", "index": 0 }]] }
}
Defines the directed graph: which node's output feeds which node's input.
Step 4 — trigger via webhook¶
Replace the Start node with a webhook:
{
"id": "webhook",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [250, 300],
"parameters": {
"path": "/my-workflow",
"httpMethod": "POST"
}
}
Then trigger it:
curl -X POST http://localhost:8080/webhook/my-workflow \
-H "Content-Type: application/json" \
-d '{"custom": "data"}'
The posted body is available as $json in downstream nodes.
Step 5 — add error handling¶
Insert a filter to short-circuit on non-2xx responses:
{
"id": "check",
"name": "Check Success",
"type": "n8n-nodes-base.filter",
"position": [550, 300],
"parameters": {
"conditions": [
{
"leftValue": "={{ $json.statusCode }}",
"operator": "equals",
"rightValue": 200
}
]
}
}
For richer error handling, see Workflow patterns › Error handling.
Next steps¶
- Core Concepts — workflows, nodes, data flow, expressions
- Nodes Reference — all 40+ available node types
- Expressions — full expression syntax and built-in functions
- Workflow Examples — common patterns
- Migrate from n8n — import your existing workflows