A utility app to create accounting in Fortnox based on e-mails in GMail
https://github.com/danniefraim/gmail-to-fortnox.git
A utility script that automates the process of finding specific emails in Gmail and creating accounting verifications in Fortnox. This can be used for instance to automatically add recurring company card charged to the accounting, or to take certain expenses and book them as a liability to yourself. I created this because Apple doesn't allow me to separate business expenses and private expenses when buying things in App Store and so on. The script was created almost entirely using Cursor's AI agent mode, so use it with care. If you make any improvements, feel free to submit them as a PR. If you like the script and find it useful, drop me a line.
git clone https://github.com/danniefraim/gmail-to-fortnox.git
cd gmail-to-fortnox
# Using uv (recommended)
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -r requirements.txt
# Or using pip
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
app/config/credentials.jsonhttp://localhost:8000/callbackvoucher and archivecp config.example.json app/config/config.jsonapp/config/config.json with your Fortnox client ID and client secretpython main.py --create-rule to interactively create a new config ruleRun the script:
python main.py
The script will:
app/config/token.json for future use.
app/config/fortnox_token.json for future useThe app/config/config.json file allows you to configure:
{
"gmail": {
"credentials_file": "credentials.json",
"token_file": "token.json",
"scopes": ["https://www.googleapis.com/auth/gmail.readonly"]
},
"fortnox": {
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uri": "http://localhost:8000/callback",
"base_url": "https://api.fortnox.se/3"
},
"email_rules": [
{
"sender": "no_reply@email.apple.com",
"subject": "",
"body_contains": "iCloud+",
"data_extraction": {
"total_amount": {
"pattern": "(?:betalat|avgift|SEK|kr)[\\s:]*([0-9]+[,.]?[0-9]*)",
"default": 399.00
}
},
"accounting": {
"description": "Apple iCloud",
"series": "F",
"entries": [
{"account": "6540", "debit": "total_amount * 0.8", "credit": 0},
{"account": "2641", "debit": "total_amount * 0.2", "credit": 0},
{"account": "2820", "debit": 0, "credit": "total_amount"}
]
}
}
]
}
In the config.json file, you can define rules to match emails:
"email_rules": [
{
"sender": "no_reply@email.apple.com",
"subject": "",
"body_contains": "iCloud+",
"data_extraction": {
"total_amount": {
"pattern": "(?:betalat|avgift|SEK|kr)[\\s:]*([0-9]+[,.]?[0-9]*)",
"default": 399.00
}
},
"accounting": {
"description": "Apple iCloud",
"series": "F",
"entries": [
{"account": "6540", "debit": "total_amount * 0.8", "credit": 0},
{"account": "2641", "debit": "total_amount * 0.2", "credit": 0},
{"account": "2820", "debit": 0, "credit": "total_amount"}
]
}
}
]
"body_contains": "Payment received""body_contains": ["Payment", "Invoice #123"]You can create rules interactively by using the rule creation mode:
python main.py --create-rule
This mode allows you to:
--email-id=ID: Use a specific Gmail message ID--rule-file=FILE: Load/save rule from/to a specific file--debug: Enable debug outputIn accounting entries, you can use formulas that reference extracted variables:
"debit": "total_amount""debit": "amount * 0.8""debit": "base_amount * (tax_percent/100)""credit": "price + shipping"MIT
Copyright 2025 Danni Efraim
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the βSoftwareβ), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED βAS ISβ, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.