categorization
npx machina-cli add skill peerjakobsen/smartspender/categorization --openclawCategorization
Purpose
Provides Claude with Danish merchant knowledge and categorization rules for classifying bank transactions into spending categories.
Categories
15 Danish spending categories with subcategories:
| Category | Subcategories | Description |
|---|---|---|
| Bolig | Husleje, El, Vand, Varme, Forsikring | Housing costs |
| Dagligvarer | Supermarked, Specialbutik | Groceries |
| Transport | Offentlig, Bil, Taxi, Cykel | Transportation |
| Abonnementer | Streaming, Fitness, Software, Telefon | Subscriptions |
| Restauranter | Restaurant, Café, Takeaway | Dining out |
| Shopping | Tøj, Elektronik, Bolig, Andet | Shopping |
| Sundhed | Apotek, Læge, Tandlæge | Health |
| Underholdning | Biograf, Koncert, Spil | Entertainment |
| Rejser | Fly, Hotel, Ferie | Travel |
| Børn | Daginstitution, Tøj, Legetøj | Children |
| Personlig pleje | Frisør, Kosmetik | Personal care |
| Uddannelse | Kurser, Bøger, Materialer | Education |
| Opsparing | Overførsler til opsparing | Savings transfers |
| Indkomst | Løn, Refusion | Income |
| Andet | Ukategoriseret | Other / fallback |
Merchant Pattern Database
Map raw transaction text patterns to normalized merchant names and categories. Patterns use * as wildcard.
Dagligvarer (Groceries)
| Pattern | Merchant | Subcategory |
|---|---|---|
*NETTO* | Netto | Supermarked |
*FØTEX* or *FOETEX* | Føtex | Supermarked |
*REMA*1000* or *REMA1000* | Rema 1000 | Supermarked |
*IRMA* | Irma | Supermarked |
*LIDL* | Lidl | Supermarked |
*ALDI* | Aldi | Supermarked |
*BILKA* | Bilka | Supermarked |
*MENY* | Meny | Supermarked |
*SPAR* | Spar | Supermarked |
*FAKTA* | Fakta | Supermarked |
*COOP* | Coop | Supermarked |
*DAGLIG*BRUGSEN* or *DAGLI*BRUGSEN* | Dagli'Brugsen | Supermarked |
*SUPER*BRUGSEN* | SuperBrugsen | Supermarked |
*KVICKLY* | Kvickly | Supermarked |
Transport
| Pattern | Merchant | Subcategory |
|---|---|---|
*DSB* | DSB | Offentlig |
*REJSEKORT* | Rejsekort | Offentlig |
*MOVIA* | Movia | Offentlig |
*Q8* | Q8 | Bil |
*CIRCLE*K* | Circle K | Bil |
*SHELL* | Shell | Bil |
*OK BENZIN* or *OK PLUS* | OK | Bil |
*UBER* | Uber | Taxi |
*TAXA* or *DANTAXI* | Taxa | Taxi |
*DONKEY*REPUBLIC* | Donkey Republic | Cykel |
Abonnementer (Subscriptions)
| Pattern | Merchant | Subcategory |
|---|---|---|
*NETFLIX* | Netflix | Streaming |
*SPOTIFY* | Spotify | Streaming |
*DISNEY*PLUS* or *DISNEYPLUS* | Disney+ | Streaming |
*HBO* or *MAX*STREAMING* | HBO Max | Streaming |
*VIAPLAY* | Viaplay | Streaming |
*TV2*PLAY* or *TV 2 PLAY* | TV2 Play | Streaming |
*YOUTUBE*PREMIUM* or *GOOGLE*YOUTUBE* | YouTube Premium | Streaming |
*APPLE*MUSIC* | Apple Music | Streaming |
*FITNESS*WORLD* | Fitness World | Fitness |
*FITNESS*DK* | Fitness DK | Fitness |
*SATS* | SATS | Fitness |
*TDC* or *YOUSEE* | TDC | Telefon |
*TELENOR* | Telenor | Telefon |
*TELIA* | Telia | Telefon |
*ADOBE* | Adobe CC | Software |
*MICROSOFT*365* or *MICROSOFT*OFFICE* | Microsoft 365 | Software |
*ICLOUD* or *APPLE.COM/BILL* | iCloud | Software |
*DROPBOX* | Dropbox | Software |
Restauranter (Dining)
| Pattern | Merchant | Subcategory |
|---|---|---|
*WOLT* | Wolt | Takeaway |
*JUST*EAT* | Just Eat | Takeaway |
*TOO GOOD TO GO* or *TOOGOODTOGO* | Too Good To Go | Takeaway |
*STARBUCKS* | Starbucks | Café |
*JOE*THE*JUICE* or *JOE & THE JUICE* | Joe & The Juice | Café |
*LAGKAGEHUSET* | Lagkagehuset | Café |
*MCDONALDS* or *MC DONALDS* | McDonald's | Restaurant |
*BURGER*KING* | Burger King | Restaurant |
Shopping
| Pattern | Merchant | Subcategory |
|---|---|---|
*H&M* or *H M * or *HM * | H&M | Tøj |
*ZALANDO* | Zalando | Tøj |
*IKEA* | IKEA | Bolig |
*ELGIGANTEN* | Elgiganten | Elektronik |
*POWER* | Power | Elektronik |
*NORMAL* | Normal | Andet |
*FLYING*TIGER* | Flying Tiger | Andet |
*SØSTRENE*GRENE* or *SOSTRENE*GRENE* | Søstrene Grene | Andet |
*AMAZON* | Amazon | Andet |
*JYSK* | Jysk | Bolig |
Bolig (Housing)
| Pattern | Merchant | Subcategory |
|---|---|---|
*ØRSTED* or *OERSTED* | Ørsted | El |
*HOFOR* | HOFOR | Vand |
*TRYG* | Tryg | Forsikring |
*TOPDANMARK* | Topdanmark | Forsikring |
*ALKA* or *CODAN* | Codan | Forsikring |
*HUSLEJE* or *FAST OVERFØRSEL*HUSLEJE* | Husleje | Husleje |
*NORLYS* | Norlys | El |
*EWII* | Ewii | El |
Sundhed (Health)
| Pattern | Merchant | Subcategory |
|---|---|---|
*APOTEK* | Apoteket | Apotek |
*MATAS* | Matas | Apotek |
*TANDLÆGE* or *TANDLAEGE* | Tandlæge | Tandlæge |
*LÆGE* or *LAEGE* | Læge | Læge |
Underholdning (Entertainment)
| Pattern | Merchant | Subcategory |
|---|---|---|
*NORDISK*FILM* | Nordisk Film | Biograf |
*CINEMAXX* | CinemaxX | Biograf |
*TICKETMASTER* | Ticketmaster | Koncert |
*BILLETLUGEN* | Billetlugen | Koncert |
Rejser (Travel)
| Pattern | Merchant | Subcategory |
|---|---|---|
*SAS* or *SCANDINAVIAN*AIRLINES* | SAS | Fly |
*NORWEGIAN* | Norwegian | Fly |
*RYANAIR* | Ryanair | Fly |
*AIRBNB* | Airbnb | Hotel |
*HOTELS.COM* or *BOOKING.COM* | Booking.com | Hotel |
Personlig pleje
| Pattern | Merchant | Subcategory |
|---|---|---|
*SEPHORA* | Sephora | Kosmetik |
*FRISØR* or *FRISOER* or *CUTTERS* | Frisør | Frisør |
Uddannelse (Education)
| Pattern | Merchant | Subcategory |
|---|---|---|
*SAXO* | Saxo | Bøger |
*AMAZON*KINDLE* | Amazon Kindle | Bøger |
Normalization Rules
Before matching patterns, normalize the raw transaction text:
- Convert to uppercase for matching
- Replace Danish characters:
Ø→OE,Æ→AE,Å→AA(for pattern matching only — preserve originals in raw_text) - Collapse multiple spaces into one
- Trim leading/trailing whitespace
Transaction Type Detection
Use the transaction description prefix to identify the payment method:
| Prefix Pattern | Type | Notes |
|---|---|---|
Dankort-køb | Card payment (physical) | Danish debit card |
Visa-køb | Card payment (physical) | Visa card |
Overførsel | Bank transfer | Internal or external |
Fast overførsel | Standing order | Recurring transfer |
PBS or Betalingsservice | Direct debit | Subscriptions and bills |
MobilePay | Mobile payment | Person-to-person or merchant |
Løn fra | Salary | Income — categorize as Indkomst |
Hævning | ATM withdrawal | Categorize as Andet |
Matching Order
When categorizing a transaction, check these sources in order — stop at the first match:
- learnings/categorization.md — Check for user corrections learned from previous sessions (confidence 1.0). These take precedence over all other rules.
- learnings/merchants.md — Check for merchant name aliases to normalize the raw text before pattern matching.
- merchant-overrides.csv — Check for a matching
raw_pattern(confidence 1.0). These are learned from previous user corrections within the same session. - Static pattern database — Check the merchant pattern tables above (confidence 1.0 exact / 0.8 partial).
- Intelligent classification — No pattern match, but Claude can infer category from transaction context (confidence 0.5–0.7).
- Fallback — Cannot determine category — assign to "Andet" (confidence 0.0).
Confidence Scoring
Assign a confidence score to each categorization:
| Match Type | Confidence | Description |
|---|---|---|
| Merchant override match | 1.0 | Transaction text matches a learned override in merchant-overrides.csv |
| Exact pattern match | 1.0 | Transaction text matches a known merchant pattern |
| Partial pattern match | 0.8 | Part of the text matches a known pattern |
| Intelligent classification | 0.5–0.7 | No pattern match, but Claude can infer from context |
| Unknown | 0.0 | Cannot determine category — assign to Andet |
When manual_override is TRUE for a transaction, always use the user's category regardless of what patterns suggest.
Learning from Corrections
When a user manually corrects a transaction's category, the correction should be saved to learnings/categorization.md so future sessions automatically apply the learned rule.
How to record a categorization learning:
- Open
learnings/categorization.md - Append a new row to the Learnings table with:
- Date: Today's date (YYYY-MM-DD)
- Pattern: The normalized raw text pattern to match (uppercase, wildcards allowed)
- Merchant: The normalized merchant name
- Category: The user-corrected category
- Subcategory: The user-corrected subcategory
- Context: Brief note about why this correction was made
Example learning entry:
| 2026-01-15 | *NETFLIX* | Netflix | Underholdning | Streaming | User: "Netflix er underholdning, ikke abonnement" |
Also update merchant-overrides.csv for the current session:
- Take the corrected transaction's
raw_textfrom transactions.csv - Normalize it: uppercase, trim whitespace, collapse multiple spaces
- Use this as the
raw_pattern - Use the user-corrected
merchant,category, andsubcategoryfrom categorized.csv - Set
created_atto the current timestamp - Only append if no row with the same
raw_patternalready exists
Ambiguous Merchants
- Netto: Always
Dagligvarer, even though they sell non-grocery items - 7-Eleven:
Dagligvarer(most purchases are food/drink) - Amazon: Default to
Shoppingunless description contains "Prime" or "Kindle" (thenAbonnementerorUddannelse) - Normal:
Shopping→Andet(sells mixed categories) - Matas:
Sundhed→Apotek(primarily health/beauty) - Wolt:
Restauranter→Takeawayunless the Wolt+ subscription charge (thenAbonnementer)
Examples
Example 1: Clear Match
raw_text: "NETTO FO 1234 KØBENHAVN"
normalized: "NETTO FO 1234 KOEBENHAVN"
pattern match: *NETTO* → Netto
result: category=Dagligvarer, subcategory=Supermarked, merchant=Netto, confidence=1.0
Example 2: PBS Subscription
raw_text: "PBS FITNESS WORLD"
normalized: "PBS FITNESS WORLD"
pattern match: *FITNESS*WORLD* → Fitness World
result: category=Abonnementer, subcategory=Fitness, merchant=Fitness World, confidence=1.0, is_recurring=TRUE
Example 3: Intelligent Classification
raw_text: "RESTAURANT COFOCO KBH"
normalized: "RESTAURANT COFOCO KBH"
no pattern match — but "RESTAURANT" prefix suggests dining
result: category=Restauranter, subcategory=Restaurant, merchant=Cofoco, confidence=0.6
Example 4: Income
raw_text: "Løn fra Arbejdsgiver ApS"
prefix match: "Løn fra" → salary
result: category=Indkomst, subcategory=Løn, merchant=Arbejdsgiver ApS, confidence=1.0
Source
git clone https://github.com/peerjakobsen/smartspender/blob/main/skills/categorization/SKILL.mdView on GitHub Overview
Provides Claude with Danish merchant knowledge and categorization rules to classify bank transactions into spending categories. It maps raw transaction text to normalized merchant names and 15 main categories with subcategories, enabling consistent budgeting and reporting in Danish contexts.
How This Skill Works
The skill uses a Merchant Pattern Database to map raw transaction text to normalized merchant names and a Danish subcategory. Patterns use wildcards (*) to match merchant names (e.g., *NETTO*, *DSB*, *LAGKAGEHUSET*) and assign the appropriate 15-category + subcategory pairing such as Dagligvarer: Supermarked or Transport: Offentlig.
When to Use It
- When organizing Danish bank transactions into spending categories for budgeting
- When you need consistent merchant name normalization from raw text
- When analyzing recurring payments in Abonnementer or attempting to classify subscriptions
- When handling new or ambiguous merchants that should map to Andet
- When auditing or exporting categorized data for Danish financial reports
Quick Start
- Step 1: Input the raw bank transaction text
- Step 2: Run it through the Merchant Pattern Database to find a match
- Step 3: Assign the corresponding normalized merchant and the Danish category/subcategory
Best Practices
- Keep the 15 category definitions aligned with your chart of accounts
- Regularly update merchant patterns to reflect new names and brands
- Test wildcard patterns against common feeds to minimize misclassification
- Prioritize exact matches before wildcard fallbacks
- Maintain a fallback to Andet for unrecognized patterns
Example Use Cases
- Pattern *NETTO* → Dagligvarer > Supermarked (Netto)
- Pattern *DSB* → Transport > Offentlig
- Pattern *NETFLIX* → Abonnementer > Streaming
- Pattern *LAGKAGEHUSET* → Restauranter > Café
- Pattern *WOLT* → Restauranter > Takeaway