AI Categorisation
How It Works
TaxMTD sends uncategorised transactions to Google Gemini AI with the transaction description, amount, date, and existing merchant memory patterns. The AI returns a category with written reasoning.
Categories are universal - the same "Vehicle expenses" category is picked whether you're a sole trader, a limited company, VAT-registered, or claiming Universal Credit. Form-specific box mapping happens at filing time:
| Filing | Where the mapping lives |
|---|---|
| SA103F / SA103S (sole trader) | /api/hmrc/sa103 - maps category slug → SA103 box |
| CT600 (limited company) | /api/hmrc/ct600 - maps category slug → CT600 P&L field |
| VAT return | /api/vat/return - splits by VAT treatment, not category |
| Universal Credit assessment | UC engine - uses system: 'uc' or 'both' categories |
That means you categorise once, and every return for every entity type reads from the same ledger. If you later switch from sole trader to limited company, your historical categorisation doesn't have to be redone.
Category Taxonomy
TaxMTD seeds a full taxonomy of expense, income, and transfer categories on first sign-up. Sub-categories are listed under each parent for reporting granularity - the AI is free to pick either a parent or a specific sub-category.
The SA103 box column below is shown as the canonical reference. For limited companies the equivalent CT600 field is in parentheses.
Expense categories
| Category | SA103 Box (CT600 field) | Sub-categories | Examples |
|---|---|---|---|
| Goods and materials | Box 10 (Cost of sales) | - | Stock, raw materials, packaging |
| CIS subcontractor payments | Box 18 (Cost of sales) | - | Subcontractor labour, CIS deductions |
| Staff expenses | Box 19 (Staff costs) | - | Wages, employer NI, pension contributions |
| Vehicle expenses | Box 20 (Travel costs) | Fuel & charging · Vehicle insurance · Repairs & servicing · Parking & tolls · Vehicle lease/hire | Fuel, MOT, car insurance |
| Transport and travel | Box 21 (Travel costs) | Public transport · Taxis & ride-hailing · Flights · Accommodation · Meals & subsistence | Train tickets, taxis, flights |
| Premises costs | Box 22 (Premises costs) | Rent · Business rates · Utilities · Premises insurance · Home office costs | Rent, rates, electricity |
| Repairs & maintenance | Box 23 (Other expenses) | - | Office repairs, equipment maintenance |
| Legal and financial costs | Box 25 (Legal & professional) | Accountancy fees · Legal fees · Bank charges · Business insurance | Accountant, bank charges |
| Marketing & advertising | Box 25 (Other expenses) | - | Google Ads, website hosting, flyers |
| Office & equipment | Box 25 (Admin expenses) | Software & SaaS · Hardware & equipment · Office supplies · Phone & internet | Stationery, computer, software |
| Professional subscriptions | Box 25 (Admin expenses) | - | ACCA, RICS, trade journals |
| Clothing | Box 25 (Other expenses) | - | Uniforms, safety boots (not everyday clothing) |
| Other expenses | Box 25 (Other expenses) | - | Anything not covered above |
| Irrecoverable debts | Box 26 (Other expenses) | - | Written-off invoices |
| Capital allowances | Box 28 (Capital allowances) | - | AIA, WDA - CT600 computes separately via CT600 tax computation |
| Depreciation & asset disposals | Box 48 (CT600 depreciation) | - | Accounting depreciation. Not tax-deductible on SA103 (disallowable adjustment) but required on CT600 as a P&L line then added back in the tax computation. Logged here so management accounts balance. |
| National Insurance | Not deductible | - | Personal Class 2 & Class 4 NI is a tax, not a business expense - it's calculated on your profit, it doesn't reduce it. Employer NI for staff goes in SA103 Box 19 / CT600 Staff costs. Directors' NI goes in Staff costs for CT600 too. |
| Tax | Not deductible | - | Income tax (sole trader) and Corporation Tax (limited company) aren't deductible against themselves. Tracked for UC and cashflow only. |
| Pensions | SA100 TR4 / CT600 Staff costs | - | Sole trader: personal pension contributions get relief on SA100, not SA103. Limited company: employer pension contributions are deductible on CT600 as part of Staff costs. The category stays the same; the filing pipeline routes it correctly per entity type. |
Income categories
| Category | SA103 Box | Sub-categories | Notes |
|---|---|---|---|
| Sales & Trading Income | Box 9 (Turnover) | Services · Products · Recurring / subscriptions | Main business turnover - feeds SA103 Box 9 for sole traders and CT600 Turnover for limited companies. |
| Other Business Income | Box 17 (Other operating income) | - | Interest received, grants, commissions. SA103 Box 17 / CT600 Other operating income. |
| Refunds & Returns | Offset - no box | - | Treated as a negative expense on the original category, so turnover isn't inflated by refunds from suppliers. Same treatment across SA103 and CT600. |
| Benefits & statutory payments | SA100 / SA102 | - | Universal Credit, Maternity Allowance etc. are not self-employment or trading income. Never hits SA103 or CT600. |
| Personal Income (non-business) | SA100 / SA102 | Employment (PAYE) · Dividends · Family gifts · Loans from family · Inheritance · Pension income · Other | PAYE salary → SA102, dividends → SA100 page TR3, gifts/loans/inheritance → not taxable. Never hits SA103 or CT600. |
Transfer categories
| Category | Notes |
|---|---|
| Bank transfers | No box - a transfer between your own accounts isn't income or expense, it's the same money in a different place. Excluded from P&L, SA103 and UC totals. |
system field on each category (hmrc, uc, or both) controls which reporting surfaces it appears in. Categories marked uc still appear in your UC monthly income figure and in the transactions ledger - they just don't get auto-mapped to an SA103 or CT600 box. That keeps your tax filings clean while your Universal Credit calculation stays accurate.Merchant Memory
When you correct a categorisation, TaxMTD remembers the mapping:
- You re-categorise "SPOTIFY" → Office & Equipment
- Pattern stored in merchant memory
- Next "SPOTIFY" transaction → instantly categorised
Pre-Categorisation Rules
Before AI runs, TaxMTD applies known patterns:
- Bank charges (type
CHG) → Legal & financial - Known software (AWS, GitHub) → Office & equipment
- Personal markers (Tesco, Netflix) → Marked personal
- Income markers (PAYMENT, salary) → Marked as income
- Transfer markers (PAYPAL PAYMENT) → Excluded transfer
API
// Run AI categorisation for a period
const result = await $fetch('https://taxmtd.uk/api/categorise', {
method: 'POST',
body: { periodId: 1 }
})
console.log(`Categorised: ${result.categorised}, Skipped: ${result.skipped}`)
// Manage merchant memory
const rules = await $fetch('https://taxmtd.uk/api/merchant-memory')
await $fetch('https://taxmtd.uk/api/merchant-memory', {
method: 'PUT',
body: { id: 1, category: 'office-equipment', categoryLabel: 'Office & Equipment' }
})
# Run AI categorisation
res = requests.post(
"https://taxmtd.uk/api/categorise",
json={"periodId": 1},
cookies=session_cookies,
)
result = res.json()["data"]
print(f"Categorised: {result['categorised']}, Skipped: {result['skipped']}")
# Get merchant memory rules
rules = requests.get(
"https://taxmtd.uk/api/merchant-memory",
cookies=session_cookies,
).json()["data"]
$result = Http::post('https://taxmtd.uk/api/categorise', ['periodId' => 1])->json();
echo "Categorised: {$result['categorised']}";
// Get merchant memory rules
$rules = Http::get('https://taxmtd.uk/api/merchant-memory')->json();
let client = reqwest::Client::new();
let res = client.post("https://taxmtd.uk/api/categorise")
.json(&serde_json::json!({ "periodId": 1 }))
.send().await?;
println!("Result: {}", res.text().await?);
# Run AI categorisation
curl -X POST https://taxmtd.uk/api/categorise \
-H "Content-Type: application/json" \
-d '{"periodId": 1}'
# List merchant memory
curl https://taxmtd.uk/api/merchant-memory