Sync Adapter
Pattern for syncing structured .md data to Google Sheets. Uses a service account, JWT auth, and content-hash dedup to avoid duplicates.
Authentication
- Service account:
/home/hobopi/.openclaw/secrets/google-service-account.json - JWT flow: sign with RSA256, exchange for access token
- Sheet API:
https://sheets.googleapis.com/v4/spreadsheets/{id} - Spreadsheet ID:
1Ikbydh-Xzc6F3pk1Q5lbCTbdEERSb4Hq8obzbABVZbU
Dedup strategy
Key generation: (normalized_date|normalized_amount|notes)
normalize_amount(str)— strips trailing zeros from decimal:$18.00→$18,$18.50→$18.50- This prevents float vs string mismatches between ledger (
$18.00as float18.0) and sheet (18as string)
Flow:
- Read all ledger
*.mdfiles → parse entries - Read all existing sheet rows → build set of dedup keys
- Filter: only entries whose key not already in set
- Append new entries only
Parsing ledger format
m = re.match(r'^- ([^$]+) \$([0-9]+\.?[0-9]*) \| (.+)', line)
Parses: - Category $Amount | Notes
Investment format:
m = re.match(r'^- ([^$]+) \$([0-9.]+)(?: \| (.+))?', line)
Sync implementations
Expense sync (daily 9:30am)
- Script:
scripts/sync-ledger-to-sheet.py - Tab:
Sheet1 - Columns: Date, Category, Amount (SGD), Notes
- Pinned to
deepseek/deepseek-v4-flash, timeout 60s
Investment sync (Saturday 11am)
- Script:
scripts/sync-investments-to-sheet.py(or agent reads ledger + writes to sheet directly) - Tab:
Investments - Columns: Date, Type (DEPOSIT/WITHDRAW), Amount (SGD), Notes
- Type field: positive → DEPOSIT, negative → WITHDRAW
- Pinned to
deepseek/deepseek-v4-flash, timeout 600s
API notes
- Use
valueInputOption: 'USER_ENTERED'for date/currency formatting - Date format:
YYYY-MM-DD(string) — prevents auto-conversion to different format - Amount: send as number (float), not string — prevents
'prefix - Find last row: read column A, count non-empty cells
- Append: start after last populated row
- Never modify existing rows — append only
