executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
supermarkt-prijzenAlbert Heijn bonuses, product search, multi-store price comparison (12 supermarkets), recipe search by ingredients, and fridge scanner with vision AI.
Independent GitHub snapshot · Automated classification · Original documentation governs
Complete AH bonussen + producten + recepten via GraphQL (web) en OAuth (mobile).
✅ Bonussen ophalen (GraphQL, 200+ items, geen login)
✅ Producten zoeken (REST API, 20k+ items, geen login)
✅ Recepten zoeken (GraphQL, geen login)
✅ Multi-supermarkt prijsvergelijking (Checkjebon.nl - 12 supermarkten, 107k producten)
✅ OAuth token flow (mobile API access - alleen voor persoonlijke data)
✅ Fridge scanner (vision AI → recepten → shopping list)
Bonussen ophalen (200+ items):
./ah-api.py bonuses --filter WEB_BONUS_PAGE --pretty
Producten zoeken (20.000+ items):
./ah-api.py search --query "melk" --limit 10 --pretty
Recepten zoeken:
./ah-recipes.py search --query "pasta carbonara" --pretty
Recept ophalen via URL:
./ah-recipes.py url --url "https://www.ah.nl/allerhande/recept/R-R1187649/zoete-tortillachips" --pretty
✨ Alles werkt zonder cookies! Gebruikt curl-cffi met Chrome fingerprint.
Get initial token:
curl -X POST 'https://api.ah.nl/mobile-auth/v1/auth/token' \
-H 'Content-Type: application/json' \
-H 'User-Agent: Appie/8.22.3' \
-d '{"clientId":"appie","code":"PASTE_CODE_HERE"}'
Response:
{
"access_token": "USERID_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"expires_in": 604798
}
Save to ~/.ah_tokens.json:
echo '{"access_token":"...","refresh_token":"...","expires_in":604798}' > ~/.ah_tokens.json
Refresh token (na 7 dagen):
./refresh-token.py
Search across 12 supermarkets:
./checkjebon-search.py --compare "melk" --top 10
Stores: AH, Jumbo, Lidl, Plus, Dekamarkt, Spar, Dirk, Hoogvliet, Poiesz, Aldi, Vomar, Ekoplaza
| Tool | Purpose |
|---|---|
ah-api.py | Cookie-based bonussen + producten (GraphQL + REST) |
ah-recipes.py | NEW! Recipe search by text or ingredients |
fridge-scan.sh | NEW! Scan fridge with camera/peekaboo |
smart-cook.sh | NEW! Complete workflow: scan → recipes → shopping |
get-bonuses.py | Legacy bonus tool (GraphQL only) |
checkjebon-search.py | Multi-store prijsvergelijking |
refresh-token.py | OAuth token vernieuwen |
setup-cookies.sh | Cookie setup helper |
Previous: Required session cookies from browser
Now: Uses curl-cffi with impersonate='chrome120'
How it works:
Only needed for:
Endpoint: https://www.ah.nl/gql
Query:
query FetchBonusPromotions($periodStart: String, $periodEnd: String) {
bonusPromotions(
filterSet: WEB_BONUS_PAGE
input: {
periodStart: "2026-02-01"
periodEnd: "2026-02-08"
filterUnavailableProducts: false
forcePromotionVisibility: true
}
) {
id title promotionType
price { now { amount } }
product { title category }
}
}
Available filters:
WEB_BONUS_PAGE - Alle bonussen (326 items!)APP_PERSONAL - Persoonlijke aanbiedingenAPP_BONUS_BOX - Bonus boxCOUPON - KortingsbonnenFREE_DELIVERY - Gratis bezorgingSPOTLIGHT - Spotlight aanbiedingenEndpoint: https://www.ah.nl/zoeken/api/products/search
Example:
curl 'https://www.ah.nl/zoeken/api/products/search?query=melk' \
-H 'Cookie: SSOC=...; jsessionid_myah=...' \
--user-agent 'Mozilla/5.0 (compatible; AH-Bot/1.0)'
Response:
{
"cards": [
{
"products": [
{
"id": 441199,
"title": "Campina Halfvolle melk",
"price": { "now": 1.99, "unitSize": "1,5 l" }
}
]
}
]
}
Authorization: https://login.ah.nl/secure/oauth/authorize
Token exchange: https://api.ah.nl/mobile-auth/v1/auth/token
Token refresh: https://api.ah.nl/mobile-auth/v1/auth/token/refresh
Token lifetime: 7 days (604798 seconds)
Known endpoints (from gist):
/mobile-services/v1/receipts - All receipts/mobile-services/v2/receipts/{id} - Specific receipt/mobile-services/product/search/v2 - Product searchNote: Some mobile endpoints currently return 500 errors (infrastructure issues).
AH uses Cloudflare + Akamai bot detection. Normal requests → 403 Access Denied.
curl-cffi uses real Chrome TLS fingerprints:
from curl_cffi import requests
response = requests.get(url, impersonate="chrome120") # ← Magic!
Source: https://raw.githubusercontent.com/supermarkt/checkjebon/main/data/supermarkets.json
Stats:
Usage:
# Find cheapest
./checkjebon-search.py --compare "bier" --top 5
# Specific store
./checkjebon-search.py --query "campina" --store jumbo
# Show stats
./checkjebon-search.py --stats
1. Scan your fridge:
./fridge-scan.sh
# Opens camera, captures fridge contents
# Output: /tmp/fridge-scan.jpg
2. Extract ingredients (via OpenClaw image tool):
# Ask assistant:
# "Analyze /tmp/fridge-scan.jpg and list all food items as comma-separated"
# → melk, eieren, tomaten, kaas, broccoli
3. Find recipes:
./ah-recipes.py ingredients --ingredients "melk,eieren,kaas,broccoli" --pretty
4. Get recipe details (by ID):
./ah-recipes.py details --recipe-id 1187649 --pretty
Or get recipe from URL directly:
./ah-recipes.py url --url "https://www.ah.nl/allerhande/recept/R-R1187649/zoete-tortillachips" --pretty
5. Complete workflow:
./smart-cook.sh
# Interactive: scan → analyze → find recipes → shopping list
How to get recipe IDs:
search action returns titles only. To get full details, you need the recipe ID.R-R{ID}:
https://www.ah.nl/allerhande/recept/R-R1187649/zoete-tortillachipsR-R1187649 → ID = 1187649url action to automatically extract ID and fetch detailsWorkflow:
# Step 1: Search for recipes (returns titles only)
./ah-recipes.py search --query "pasta carbonara" --pretty
# Step 2: If you have the recipe URL (e.g., from browser or website), extract ID
./ah-recipes.py url --url "https://www.ah.nl/allerhande/recept/R-R{ID}/{slug}" --pretty
# Note: Search results don't include recipe IDs (client-side rendered)
# To get full details, you need either:
# - The direct recipe URL (contains R-R{ID})
# - The recipe ID number
Search by text (returns IDs + titles):
./ah-recipes.py search --query "pasta carbonara" --size 10 --pretty
# Output: {"recipes": [{"id": 1200422, "title": "Klassieke spaghetti carbonara"}, ...], "total": 49, "hasMore": true}
Search with detailed info (cook time, ratings, images, servings):
./ah-recipes.py search --query "pasta carbonara" --size 5 --detailed --pretty
# Output: Full recipe summaries with time, ratings, images, servings
Search by ingredients:
./ah-recipes.py ingredients --ingredients "tomaat,ui,knoflook" --size 5 --pretty
Get recipe from URL:
./ah-recipes.py url --url "https://www.ah.nl/allerhande/recept/R-R1187649/zoete-tortillachips" --pretty
# Extracts recipe ID from URL (R-R1187649 → 1187649) and fetches full details
Cheapest melk across all stores:
./checkjebon-search.py --compare "melk" --top 5
AH bonussen vandaag:
./ah-api.py bonuses --filter WEB_BONUS_PAGE --pretty | \
jq '.bonuses[] | select(.title | contains("Campina"))'
Search AH products:
./ah-api.py search --query "bier" --limit 20 --pretty
"Access Denied" errors:
./setup-cookies.sh)OAuth code expired:
GraphQL errors:
ah-bonuses/
├── SKILL.md # This file
├── README.md # Quick start
├── ah-api.py # Main CLI tool (bonuses + search)
├── get-bonuses.py # Legacy bonus tool
├── checkjebon-search.py # Multi-store search
├── refresh-token.py # OAuth token refresh
├── setup-cookies.sh # Cookie extractor
└── ~/.ah_cookies.json # Session cookies (gitignored)
└── ~/.ah_tokens.json # OAuth tokens (gitignored)
✅ Bonussen API (GraphQL) - 100% working WITHOUT login! (200+ bonussen)
✅ Product search (REST) - 100% working WITHOUT login! (20k+ producten)
✅ Recipe search (GraphQL) - 100% working WITHOUT login!
✅ Multi-store comparison (Checkjebon) - 100% working (107k products, 12 stores)
✅ OAuth token flow - Working (7-day tokens, voor mobile API)
⚠️ Mobile API endpoints - Partial (some 500 errors)
2026-02-02 - MAJOR UPDATE:
curl-cffi with impersonate='chrome120' to bypass bot detectionsetup-cookies.sh (no longer needed)2026-02-01:
Last updated: 2026-02-02