bps-kit 1.2.2 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.bps-kit.json +4 -4
  2. package/README.md +3 -0
  3. package/implementation_plan.md.resolved +37 -0
  4. package/package.json +2 -2
  5. package/templates/agents-template/ARCHITECTURE.md +21 -9
  6. package/templates/agents-template/agents/automation-specialist.md +157 -0
  7. package/templates/agents-template/rules/GEMINI.md +2 -10
  8. package/templates/agents-template/workflows/automate.md +153 -0
  9. package/templates/skills_normal/n8n-code-javascript/BUILTIN_FUNCTIONS.md +764 -0
  10. package/templates/skills_normal/n8n-code-javascript/COMMON_PATTERNS.md +1110 -0
  11. package/templates/skills_normal/n8n-code-javascript/DATA_ACCESS.md +782 -0
  12. package/templates/skills_normal/n8n-code-javascript/ERROR_PATTERNS.md +763 -0
  13. package/templates/skills_normal/n8n-code-javascript/README.md +350 -0
  14. package/templates/skills_normal/n8n-code-javascript/SKILL.md +699 -0
  15. package/templates/skills_normal/n8n-code-python/COMMON_PATTERNS.md +794 -0
  16. package/templates/skills_normal/n8n-code-python/DATA_ACCESS.md +702 -0
  17. package/templates/skills_normal/n8n-code-python/ERROR_PATTERNS.md +601 -0
  18. package/templates/skills_normal/n8n-code-python/README.md +386 -0
  19. package/templates/skills_normal/n8n-code-python/SKILL.md +748 -0
  20. package/templates/skills_normal/n8n-code-python/STANDARD_LIBRARY.md +974 -0
  21. package/templates/skills_normal/n8n-expression-syntax/COMMON_MISTAKES.md +393 -0
  22. package/templates/skills_normal/n8n-expression-syntax/EXAMPLES.md +483 -0
  23. package/templates/skills_normal/n8n-expression-syntax/README.md +93 -0
  24. package/templates/skills_normal/n8n-expression-syntax/SKILL.md +516 -0
  25. package/templates/skills_normal/n8n-mcp-tools-expert/README.md +99 -0
  26. package/templates/skills_normal/n8n-mcp-tools-expert/SEARCH_GUIDE.md +374 -0
  27. package/templates/skills_normal/n8n-mcp-tools-expert/SKILL.md +642 -0
  28. package/templates/skills_normal/n8n-mcp-tools-expert/VALIDATION_GUIDE.md +442 -0
  29. package/templates/skills_normal/n8n-mcp-tools-expert/WORKFLOW_GUIDE.md +618 -0
  30. package/templates/skills_normal/n8n-node-configuration/DEPENDENCIES.md +789 -0
  31. package/templates/skills_normal/n8n-node-configuration/OPERATION_PATTERNS.md +913 -0
  32. package/templates/skills_normal/n8n-node-configuration/README.md +364 -0
  33. package/templates/skills_normal/n8n-node-configuration/SKILL.md +785 -0
  34. package/templates/skills_normal/n8n-validation-expert/ERROR_CATALOG.md +943 -0
  35. package/templates/skills_normal/n8n-validation-expert/FALSE_POSITIVES.md +720 -0
  36. package/templates/skills_normal/n8n-validation-expert/README.md +290 -0
  37. package/templates/skills_normal/n8n-validation-expert/SKILL.md +689 -0
  38. package/templates/skills_normal/n8n-workflow-patterns/README.md +251 -0
  39. package/templates/skills_normal/n8n-workflow-patterns/SKILL.md +411 -0
  40. package/templates/skills_normal/n8n-workflow-patterns/ai_agent_workflow.md +784 -0
  41. package/templates/skills_normal/n8n-workflow-patterns/database_operations.md +785 -0
  42. package/templates/skills_normal/n8n-workflow-patterns/http_api_integration.md +734 -0
  43. package/templates/skills_normal/n8n-workflow-patterns/scheduled_tasks.md +773 -0
  44. package/templates/skills_normal/n8n-workflow-patterns/webhook_processing.md +545 -0
  45. package/templates/vault/n8n-code-javascript/SKILL.md +10 -10
  46. package/templates/vault/n8n-code-python/SKILL.md +11 -11
  47. package/templates/vault/n8n-expression-syntax/SKILL.md +4 -4
  48. package/templates/vault/n8n-mcp-tools-expert/SKILL.md +9 -9
  49. package/templates/vault/n8n-node-configuration/SKILL.md +2 -2
  50. package/templates/vault/n8n-validation-expert/SKILL.md +3 -3
  51. package/templates/vault/n8n-workflow-patterns/SKILL.md +11 -11
@@ -0,0 +1,702 @@
1
+ # Data Access Patterns - Python Code Node
2
+
3
+ Complete guide to accessing data in n8n Code nodes using Python.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ In n8n Python Code nodes, you access data using **underscore-prefixed** variables: `_input`, `_json`, `_node`.
10
+
11
+ **Data Access Priority** (by common usage):
12
+ 1. **`_input.all()`** - Most common - Batch operations, aggregations
13
+ 2. **`_input.first()`** - Very common - Single item operations
14
+ 3. **`_input.item`** - Common - Each Item mode only
15
+ 4. **`_node["NodeName"]["json"]`** - Specific node references
16
+ 5. **`_json`** - Direct current item (use `_input` instead)
17
+
18
+ **Python vs JavaScript**:
19
+ | JavaScript | Python (Beta) | Python (Native) |
20
+ |------------|---------------|-----------------|
21
+ | `$input.all()` | `_input.all()` | `_items` |
22
+ | `$input.first()` | `_input.first()` | `_items[0]` |
23
+ | `$input.item` | `_input.item` | `_item` |
24
+ | `$json` | `_json` | `_item["json"]` |
25
+ | `$node["Name"]` | `_node["Name"]` | Not available |
26
+
27
+ ---
28
+
29
+ ## Pattern 1: _input.all() - Process All Items
30
+
31
+ **Usage**: Most common pattern for batch processing
32
+
33
+ **When to use:**
34
+ - Processing multiple records
35
+ - Aggregating data (sum, count, average)
36
+ - Filtering lists
37
+ - Transforming datasets
38
+
39
+ ### Basic Usage
40
+
41
+ ```python
42
+ # Get all items from previous node
43
+ all_items = _input.all()
44
+
45
+ # all_items is a list of dictionaries like:
46
+ # [
47
+ # {"json": {"id": 1, "name": "Alice"}},
48
+ # {"json": {"id": 2, "name": "Bob"}}
49
+ # ]
50
+
51
+ print(f"Received {len(all_items)} items")
52
+
53
+ return all_items
54
+ ```
55
+
56
+ ### Example 1: Filter Active Items
57
+
58
+ ```python
59
+ all_items = _input.all()
60
+
61
+ # Filter only active items
62
+ active_items = [
63
+ item for item in all_items
64
+ if item["json"].get("status") == "active"
65
+ ]
66
+
67
+ return active_items
68
+ ```
69
+
70
+ ### Example 2: Transform All Items
71
+
72
+ ```python
73
+ all_items = _input.all()
74
+
75
+ # Transform to new structure
76
+ transformed = []
77
+ for item in all_items:
78
+ transformed.append({
79
+ "json": {
80
+ "id": item["json"].get("id"),
81
+ "full_name": f"{item['json'].get('first_name', '')} {item['json'].get('last_name', '')}",
82
+ "email": item["json"].get("email"),
83
+ "processed_at": datetime.now().isoformat()
84
+ }
85
+ })
86
+
87
+ return transformed
88
+ ```
89
+
90
+ ### Example 3: Aggregate Data
91
+
92
+ ```python
93
+ all_items = _input.all()
94
+
95
+ # Calculate total
96
+ total = sum(item["json"].get("amount", 0) for item in all_items)
97
+
98
+ return [{
99
+ "json": {
100
+ "total": total,
101
+ "count": len(all_items),
102
+ "average": total / len(all_items) if all_items else 0
103
+ }
104
+ }]
105
+ ```
106
+
107
+ ### Example 4: Sort and Limit
108
+
109
+ ```python
110
+ all_items = _input.all()
111
+
112
+ # Get top 5 by score
113
+ sorted_items = sorted(
114
+ all_items,
115
+ key=lambda item: item["json"].get("score", 0),
116
+ reverse=True
117
+ )
118
+ top_five = sorted_items[:5]
119
+
120
+ return [{"json": item["json"]} for item in top_five]
121
+ ```
122
+
123
+ ### Example 5: Group By Category
124
+
125
+ ```python
126
+ all_items = _input.all()
127
+
128
+ # Group items by category
129
+ grouped = {}
130
+ for item in all_items:
131
+ category = item["json"].get("category", "Uncategorized")
132
+
133
+ if category not in grouped:
134
+ grouped[category] = []
135
+
136
+ grouped[category].append(item["json"])
137
+
138
+ # Convert to list format
139
+ return [
140
+ {
141
+ "json": {
142
+ "category": category,
143
+ "items": items,
144
+ "count": len(items)
145
+ }
146
+ }
147
+ for category, items in grouped.items()
148
+ ]
149
+ ```
150
+
151
+ ### Example 6: Deduplicate by ID
152
+
153
+ ```python
154
+ all_items = _input.all()
155
+
156
+ # Remove duplicates by ID
157
+ seen = set()
158
+ unique = []
159
+
160
+ for item in all_items:
161
+ item_id = item["json"].get("id")
162
+
163
+ if item_id and item_id not in seen:
164
+ seen.add(item_id)
165
+ unique.append(item)
166
+
167
+ return unique
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Pattern 2: _input.first() - Get First Item
173
+
174
+ **Usage**: Very common for single-item operations
175
+
176
+ **When to use:**
177
+ - Previous node returns single object
178
+ - Working with API responses
179
+ - Getting initial/first data point
180
+
181
+ ### Basic Usage
182
+
183
+ ```python
184
+ # Get first item from previous node
185
+ first_item = _input.first()
186
+
187
+ # Access the JSON data
188
+ data = first_item["json"]
189
+
190
+ print(f"First item: {data}")
191
+
192
+ return [{"json": data}]
193
+ ```
194
+
195
+ ### Example 1: Process Single API Response
196
+
197
+ ```python
198
+ # Get API response (typically single object)
199
+ response = _input.first()["json"]
200
+
201
+ # Extract what you need
202
+ return [{
203
+ "json": {
204
+ "user_id": response.get("data", {}).get("user", {}).get("id"),
205
+ "user_name": response.get("data", {}).get("user", {}).get("name"),
206
+ "status": response.get("status"),
207
+ "fetched_at": datetime.now().isoformat()
208
+ }
209
+ }]
210
+ ```
211
+
212
+ ### Example 2: Transform Single Object
213
+
214
+ ```python
215
+ data = _input.first()["json"]
216
+
217
+ # Transform structure
218
+ return [{
219
+ "json": {
220
+ "id": data.get("id"),
221
+ "contact": {
222
+ "email": data.get("email"),
223
+ "phone": data.get("phone")
224
+ },
225
+ "address": {
226
+ "street": data.get("street"),
227
+ "city": data.get("city"),
228
+ "zip": data.get("zip")
229
+ }
230
+ }
231
+ }]
232
+ ```
233
+
234
+ ### Example 3: Validate Single Item
235
+
236
+ ```python
237
+ item = _input.first()["json"]
238
+
239
+ # Validation logic
240
+ is_valid = bool(item.get("email") and "@" in item.get("email", ""))
241
+
242
+ return [{
243
+ "json": {
244
+ **item,
245
+ "valid": is_valid,
246
+ "validated_at": datetime.now().isoformat()
247
+ }
248
+ }]
249
+ ```
250
+
251
+ ### Example 4: Extract Nested Data
252
+
253
+ ```python
254
+ response = _input.first()["json"]
255
+
256
+ # Navigate nested structure
257
+ users = response.get("data", {}).get("users", [])
258
+
259
+ return [
260
+ {
261
+ "json": {
262
+ "id": user.get("id"),
263
+ "name": user.get("profile", {}).get("name", "Unknown"),
264
+ "email": user.get("contact", {}).get("email", "no-email")
265
+ }
266
+ }
267
+ for user in users
268
+ ]
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Pattern 3: _input.item - Current Item (Each Item Mode)
274
+
275
+ **Usage**: Common in "Run Once for Each Item" mode
276
+
277
+ **When to use:**
278
+ - Mode is set to "Run Once for Each Item"
279
+ - Need to process items independently
280
+ - Per-item API calls or validations
281
+
282
+ **IMPORTANT**: Only use in "Each Item" mode. Will be undefined in "All Items" mode.
283
+
284
+ ### Basic Usage
285
+
286
+ ```python
287
+ # In "Run Once for Each Item" mode
288
+ current_item = _input.item
289
+ data = current_item["json"]
290
+
291
+ print(f"Processing item: {data.get('id')}")
292
+
293
+ return [{
294
+ "json": {
295
+ **data,
296
+ "processed": True
297
+ }
298
+ }]
299
+ ```
300
+
301
+ ### Example 1: Add Processing Metadata
302
+
303
+ ```python
304
+ item = _input.item
305
+
306
+ return [{
307
+ "json": {
308
+ **item["json"],
309
+ "processed": True,
310
+ "processed_at": datetime.now().isoformat(),
311
+ "processing_duration": random.random() * 1000 # Simulated
312
+ }
313
+ }]
314
+ ```
315
+
316
+ ### Example 2: Per-Item Validation
317
+
318
+ ```python
319
+ item = _input.item
320
+ data = item["json"]
321
+
322
+ # Validate this specific item
323
+ errors = []
324
+
325
+ if not data.get("email"):
326
+ errors.append("Email required")
327
+ if not data.get("name"):
328
+ errors.append("Name required")
329
+ if data.get("age") and data["age"] < 18:
330
+ errors.append("Must be 18+")
331
+
332
+ return [{
333
+ "json": {
334
+ **data,
335
+ "valid": len(errors) == 0,
336
+ "errors": errors if errors else None
337
+ }
338
+ }]
339
+ ```
340
+
341
+ ### Example 3: Conditional Processing
342
+
343
+ ```python
344
+ item = _input.item
345
+ data = item["json"]
346
+
347
+ # Process based on item type
348
+ if data.get("type") == "premium":
349
+ return [{
350
+ "json": {
351
+ **data,
352
+ "discount": 0.20,
353
+ "tier": "premium"
354
+ }
355
+ }]
356
+ else:
357
+ return [{
358
+ "json": {
359
+ **data,
360
+ "discount": 0.05,
361
+ "tier": "standard"
362
+ }
363
+ }]
364
+ ```
365
+
366
+ ---
367
+
368
+ ## Pattern 4: _node - Reference Other Nodes
369
+
370
+ **Usage**: Less common, but powerful for specific scenarios
371
+
372
+ **When to use:**
373
+ - Need data from specific named node
374
+ - Combining data from multiple nodes
375
+
376
+ ### Basic Usage
377
+
378
+ ```python
379
+ # Get output from specific node
380
+ webhook_data = _node["Webhook"]["json"]
381
+ api_data = _node["HTTP Request"]["json"]
382
+
383
+ return [{
384
+ "json": {
385
+ "from_webhook": webhook_data,
386
+ "from_api": api_data
387
+ }
388
+ }]
389
+ ```
390
+
391
+ ### Example 1: Combine Multiple Sources
392
+
393
+ ```python
394
+ # Reference multiple nodes
395
+ webhook = _node["Webhook"]["json"]
396
+ database = _node["Postgres"]["json"]
397
+ api = _node["HTTP Request"]["json"]
398
+
399
+ return [{
400
+ "json": {
401
+ "combined": {
402
+ "webhook": webhook.get("body", {}),
403
+ "db_records": len(database) if isinstance(database, list) else 1,
404
+ "api_response": api.get("status")
405
+ },
406
+ "processed_at": datetime.now().isoformat()
407
+ }
408
+ }]
409
+ ```
410
+
411
+ ### Example 2: Compare Across Nodes
412
+
413
+ ```python
414
+ old_data = _node["Get Old Data"]["json"]
415
+ new_data = _node["Get New Data"]["json"]
416
+
417
+ # Simple comparison
418
+ changes = {
419
+ "added": [n for n in new_data if n.get("id") not in [o.get("id") for o in old_data]],
420
+ "removed": [o for o in old_data if o.get("id") not in [n.get("id") for n in new_data]]
421
+ }
422
+
423
+ return [{
424
+ "json": {
425
+ "changes": changes,
426
+ "summary": {
427
+ "added": len(changes["added"]),
428
+ "removed": len(changes["removed"])
429
+ }
430
+ }
431
+ }]
432
+ ```
433
+
434
+ ---
435
+
436
+ ## Critical: Webhook Data Structure
437
+
438
+ **MOST COMMON MISTAKE**: Forgetting webhook data is nested under `["body"]`
439
+
440
+ ### The Problem
441
+
442
+ Webhook node wraps all incoming data under a `"body"` property.
443
+
444
+ ### Structure
445
+
446
+ ```python
447
+ # Webhook node output structure:
448
+ {
449
+ "headers": {
450
+ "content-type": "application/json",
451
+ "user-agent": "..."
452
+ },
453
+ "params": {},
454
+ "query": {},
455
+ "body": {
456
+ # ← YOUR DATA IS HERE
457
+ "name": "Alice",
458
+ "email": "alice@example.com",
459
+ "message": "Hello!"
460
+ }
461
+ }
462
+ ```
463
+
464
+ ### Wrong vs Right
465
+
466
+ ```python
467
+ # ❌ WRONG: Trying to access directly
468
+ name = _json["name"] # KeyError!
469
+ email = _json["email"] # KeyError!
470
+
471
+ # ✅ CORRECT: Access via ["body"]
472
+ name = _json["body"]["name"] # "Alice"
473
+ email = _json["body"]["email"] # "alice@example.com"
474
+
475
+ # ✅ SAFER: Use .get() for safe access
476
+ webhook_data = _json.get("body", {})
477
+ name = webhook_data.get("name") # None if missing
478
+ email = webhook_data.get("email", "no-email") # Default value
479
+ ```
480
+
481
+ ### Example: Full Webhook Processing
482
+
483
+ ```python
484
+ # Get webhook data from previous node
485
+ webhook_output = _input.first()["json"]
486
+
487
+ # Access the actual payload
488
+ payload = webhook_output.get("body", {})
489
+
490
+ # Access headers if needed
491
+ content_type = webhook_output.get("headers", {}).get("content-type")
492
+
493
+ # Access query parameters if needed
494
+ api_key = webhook_output.get("query", {}).get("api_key")
495
+
496
+ # Process the actual data
497
+ return [{
498
+ "json": {
499
+ # Data from webhook body
500
+ "user_name": payload.get("name"),
501
+ "user_email": payload.get("email"),
502
+ "message": payload.get("message"),
503
+
504
+ # Metadata
505
+ "received_at": datetime.now().isoformat(),
506
+ "content_type": content_type,
507
+ "authenticated": bool(api_key)
508
+ }
509
+ }]
510
+ ```
511
+
512
+ ### POST Data, Query Params, and Headers
513
+
514
+ ```python
515
+ webhook = _input.first()["json"]
516
+
517
+ return [{
518
+ "json": {
519
+ # POST body data
520
+ "form_data": webhook.get("body", {}),
521
+
522
+ # Query parameters (?key=value)
523
+ "query_params": webhook.get("query", {}),
524
+
525
+ # HTTP headers
526
+ "user_agent": webhook.get("headers", {}).get("user-agent"),
527
+ "content_type": webhook.get("headers", {}).get("content-type"),
528
+
529
+ # Request metadata
530
+ "method": webhook.get("method"), # POST, GET, etc.
531
+ "url": webhook.get("url")
532
+ }
533
+ }]
534
+ ```
535
+
536
+ ---
537
+
538
+ ## Choosing the Right Pattern
539
+
540
+ ### Decision Tree
541
+
542
+ ```
543
+ Do you need ALL items from previous node?
544
+ ├─ YES → Use _input.all()
545
+
546
+ └─ NO → Do you need just the FIRST item?
547
+ ├─ YES → Use _input.first()
548
+
549
+ └─ NO → Are you in "Each Item" mode?
550
+ ├─ YES → Use _input.item
551
+
552
+ └─ NO → Do you need specific node data?
553
+ ├─ YES → Use _node["NodeName"]
554
+ └─ NO → Use _input.first() (default)
555
+ ```
556
+
557
+ ### Quick Reference Table
558
+
559
+ | Scenario | Use This | Example |
560
+ |----------|----------|---------|
561
+ | Sum all amounts | `_input.all()` | `sum(i["json"].get("amount", 0) for i in items)` |
562
+ | Get API response | `_input.first()` | `_input.first()["json"].get("data")` |
563
+ | Process each independently | `_input.item` | `_input.item["json"]` (Each Item mode) |
564
+ | Combine two nodes | `_node["Name"]` | `_node["API"]["json"]` |
565
+ | Filter list | `_input.all()` | `[i for i in items if i["json"].get("active")]` |
566
+ | Transform single object | `_input.first()` | `{**_input.first()["json"], "new": True}` |
567
+ | Webhook data | `_input.first()` | `_input.first()["json"]["body"]` |
568
+
569
+ ---
570
+
571
+ ## Common Mistakes
572
+
573
+ ### Mistake 1: Using _json Without Context
574
+
575
+ ```python
576
+ # ❌ RISKY: _json is ambiguous
577
+ value = _json["field"]
578
+
579
+ # ✅ CLEAR: Be explicit
580
+ value = _input.first()["json"]["field"]
581
+ ```
582
+
583
+ ### Mistake 2: Forgetting ["json"] Property
584
+
585
+ ```python
586
+ # ❌ WRONG: Trying to access fields on item dictionary
587
+ items = _input.all()
588
+ names = [item["name"] for item in items] # KeyError!
589
+
590
+ # ✅ CORRECT: Access via ["json"]
591
+ names = [item["json"]["name"] for item in items]
592
+ ```
593
+
594
+ ### Mistake 3: Using _input.item in All Items Mode
595
+
596
+ ```python
597
+ # ❌ WRONG: _input.item is None in "All Items" mode
598
+ data = _input.item["json"] # AttributeError!
599
+
600
+ # ✅ CORRECT: Use appropriate method
601
+ data = _input.first()["json"] # Or _input.all()
602
+ ```
603
+
604
+ ### Mistake 4: Not Handling Empty Lists
605
+
606
+ ```python
607
+ # ❌ WRONG: Crashes if no items
608
+ first = _input.all()[0]["json"] # IndexError!
609
+
610
+ # ✅ CORRECT: Check length first
611
+ items = _input.all()
612
+ if items:
613
+ first = items[0]["json"]
614
+ else:
615
+ return []
616
+
617
+ # ✅ ALSO CORRECT: Use _input.first()
618
+ first = _input.first()["json"] # Built-in safety
619
+ ```
620
+
621
+ ### Mistake 5: Direct Dictionary Access (KeyError)
622
+
623
+ ```python
624
+ # ❌ RISKY: Crashes if key missing
625
+ value = item["json"]["field"] # KeyError!
626
+
627
+ # ✅ SAFE: Use .get()
628
+ value = item["json"].get("field", "default")
629
+ ```
630
+
631
+ ---
632
+
633
+ ## Advanced Patterns
634
+
635
+ ### Pattern: Safe Nested Access
636
+
637
+ ```python
638
+ # Deep nested access with .get()
639
+ value = (
640
+ _input.first()["json"]
641
+ .get("level1", {})
642
+ .get("level2", {})
643
+ .get("level3", "default")
644
+ )
645
+ ```
646
+
647
+ ### Pattern: List Comprehension with Filtering
648
+
649
+ ```python
650
+ items = _input.all()
651
+
652
+ # Filter and transform in one step
653
+ result = [
654
+ {
655
+ "json": {
656
+ "id": item["json"]["id"],
657
+ "name": item["json"]["name"].upper()
658
+ }
659
+ }
660
+ for item in items
661
+ if item["json"].get("active") and item["json"].get("verified")
662
+ ]
663
+
664
+ return result
665
+ ```
666
+
667
+ ### Pattern: Dictionary Comprehension
668
+
669
+ ```python
670
+ items = _input.all()
671
+
672
+ # Create lookup dictionary
673
+ lookup = {
674
+ item["json"]["id"]: item["json"]
675
+ for item in items
676
+ if "id" in item["json"]
677
+ }
678
+
679
+ return [{"json": lookup}]
680
+ ```
681
+
682
+ ---
683
+
684
+ ## Summary
685
+
686
+ **Most Common Patterns**:
687
+ 1. `_input.all()` - Process multiple items, batch operations
688
+ 2. `_input.first()` - Single item, API responses
689
+ 3. `_input.item` - Each Item mode processing
690
+
691
+ **Critical Rule**:
692
+ - Webhook data is under `["body"]` property
693
+
694
+ **Best Practice**:
695
+ - Use `.get()` for dictionary access to avoid KeyError
696
+ - Always check for empty lists
697
+ - Be explicit: Use `_input.first()["json"]["field"]` instead of `_json["field"]`
698
+
699
+ **See Also**:
700
+ - [SKILL.md](SKILL.md) - Overview and quick start
701
+ - [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Python-specific patterns
702
+ - [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Avoid common mistakes