Automatically create transactions in Actual Budget when you use Tap-to-Pay on a mobile device
Version 1.0.9
Actual Tap bridges the gap between mobile payments and your Actual Budget, making expense tracking seamless and automatic. When you tap to pay with your mobile device, Actual Tap receives the transaction details and automatically creates the corresponding entry in your Actual Budget.
- π Fast and lightweight Fastify API
- π³ Automatic transaction creation from Tap-to-Pay
- π± Mobile automation support (iOS Shortcuts & Android Tasker)
- π Secure API key authentication
- π³ Easy deployment with Docker
- π Real-time transaction syncing with Actual Budget
Actual Tap is a Fastify API that utilizes the Actual Budget API Client to create transactions. Here's the ideal flow:
- Mobile device is tapped to make a purchase
- Automation on mobile device is triggered
- POST request containing transaction information is sent to Actual Tap
- Actual Tap creates the transaction in Actual Budget
X-API-KEY: your-api-key
Content-Type: application/json
{
"account": "Checking", // Required: Name of the account in Actual Budget
"amount": 10.50, // Optional: Transaction amount (defaults to 0)
"payee": "Starbucks", // Optional: Name of the payee (defaults to "Unknown")
"type": "payment" // Optional: "payment" or "deposit" (defaults to "payment")
}# Regular transaction (expense)
curl -X POST https://actualtap.yourdomain.com/transaction \
-H "X-API-KEY: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"account": "Checking",
"amount": 10.50,
"payee": "Starbucks"
}'
# Deposit transaction
curl -X POST https://actualtap.yourdomain.com/transaction \
-H "X-API-KEY: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"account": "Checking",
"amount": 100.00,
"payee": "Refund",
"type": "deposit"
}'docker run -p 3001:3001 \
-e TZ=your_timezone \
-e API_KEY=your_api_key \
-e ACTUAL_URL=your_actual_url \
-e ACTUAL_PASSWORD=your_password \
-e ACTUAL_SYNC_ID=your_budget_id \
mattyfaz/actualtapservices:
actualtap:
container_name: actualtap
image: mattyfaz/actualtap:latest
restart: always
ports:
- 3001:3001
volumes:
- /your/path/here:/app/data
environment:
- TZ=
- API_KEY=
- ACTUAL_URL=
- ACTUAL_PASSWORD=
- ACTUAL_SYNC_ID=| Variable | Example | Description |
|---|---|---|
TZ |
Australia/Melbourne | Your timezone, ideally you should match the TZ set in Actual |
API_KEY |
527D6AAA-B22A-4D48-9DC8-C203139E5531 | Unique API key for authentication (generate with uuidgenerator.net) |
ACTUAL_URL |
https://actual.yourdomain.com | URL to Actual Budget Server |
ACTUAL_PASSWORD |
superSecretPassword | Password for your Actual Budget Server |
ACTUAL_SYNC_ID |
8B51B58D-3A0D-4B5B-A41F-DE574306A4F2 | The Unique ID of your Budget |
-
Clone the repository:
git clone https://github.com/MattFaz/actualtap.git cd actualtap -
Install dependencies:
npm install
-
Set up your environment variables in your terminal:
export API_KEY="your-api-key" export ACTUAL_URL="your-actual-url" export ACTUAL_PASSWORD="your-password" export ACTUAL_SYNC_ID="your-budget-id"
-
Start the development server:
npm run dev
The app will run on port 3001 by default.
Note: Mobile setup requires ActualTap container running and publicly accessible via URL.
Setup for iOS has 2 parts, one is a Shortcut, and the second is an Automation to trigger the Shortcut upon tapping your iOS device to pay.
Click the following link to download and add the Shortcut: https://www.icloud.com/shortcuts/a07b4aca380f422ba30e1ccf1ca95aa9
You do not nee to make any edits to the Shortcut. Once added, follow the below steps to create the Automation, end result will look like the screenshot below:
-
Open Shortcuts app, select 'Automations', then '+' to create a new Automation
-
Tap 'Transaction' and Enable relevant Card, all Categories, then select 'Run Immediately'
- Do not enable 'Notify When Run'
-
Select 'New Blank Automation', then Search & add 'Dictionary'
-
Add the values below to Dictionary:
Item Type Value URL Text https://actualtap.yourdomain.com API_KEY Text api_key used when setting up ActualTap Account Text exact name of Account in Actual Budget Merchant Text Tap 'Select Variable' then tap 'Shortcut Input'. Then Tap 'Shortcut Input' in the Value and change it to Merchant Name Text Tap 'Select Variable' then tap 'Shortcut Input'. Then Tap 'Shortcut Input' in the Value and change it to Name Amount Text Tap 'Select Variable' then tap 'Shortcut Input'. Then Tap 'Shortcut Input' in the Value and change it to Amount
-
-
Search & tap on 'Run Shortcut'
-
Tap 'Shortcut' and select 'ActualTap'
-
Tap the '>' to expand the action, and change 'Input' value to 'Dictionary'
Tip: Rename the card in your Google Wallet to match the account name in Actual Budget. This will allow you to use the %account variables and use multiple cards with Google Wallet and Actual Budget.
This method requires the Notification addon for Tasker.
- Add "+" a task within Tasker, and proceed to Taskernet.
- Search for "Wallet to ActualBudget" and import the task.
- Import by long pressing on "PROFILES"
- Search for "Wallet to ActualBudget" and import the task.
- Navigate to the "TASKS" tab and edit "Send To ActualTap"
- Edit the HTTP Request step
- Replace URL with http://{your-actual-tap-address.com}/transaction
- Add your API key to HEADERS
- Body:
- Remove the [ ] brackets.
The free version of Automate allows 30 blocks to be run at once with full capability. This flo uses 11 of the 30.
- Download the flo for Automate. https://llamalab.com/automate/community/flows/50847
- This can be searched for within the Automate app on your mobile device.
- Edit the "HTTP request" block
- Update the Request URL to your actualtap address
- Update your API key for your actualtap deployment
- Save your changes and start flo.
Summary of flo
- The flo will pause until a new notification appears.
- If the notification is Google Wallet, proceed.
- Set two variables. One for payee and one that contains account and amount information.
- Get current date
- Use a REGEX pattern to extract the amount information.
- Pass the amount, payee, and date information to actualtap using the HTTP request block.
- If the httprequest was successful, returns 200, remove the notification.
- If the httprequest failed, leave the notification and return to wait for a new notification.
If a request failed, you can change the Notification block to activate "Immediately" to process it. Then change it back to "When transition"
Navigate to Settings -> Companion App -> Manage Sensors Select Last Notification
- Enable the sensor
- Select Allow list. Select any apps that will be used for posting transactions. Google Wallet is typically selected.
Add a section in configuration.yaml and update your actualtap url.
rest_command:
actualbudget:
url: "https://actualtap.example.com/transaction"
method: post
content_type: 'application/json'
headers:
X-API-KEY: !secret actualtap_api
payload: '{"account": "{{accountVar}}", "amount": "{{amountVar}}", "date": "{{dateVar}}", "payee": "{{payeeVar}}", "notes": "{{notesVar}}"}'
Add your api key.
actualtap_api: "YOUR API KEY"
Navigate to Settings -> Automations and Scenes within Home Assistant.
Create a new Automation
Use the three dot menu at the top to "Edit in YAML".
Paste the following code. Replace "your_device" with your devices name.
alias: Google Wallet Transaction Automation
description: ""
triggers:
- entity_id: sensor.your_device_last_notification
trigger: state
actions:
- data:
accountVar: >
{% set text = state_attr('sensor.your_device_last_notification',
'android.text') %} {% if text %}
{{ text.split(' with ')[1] if ' with ' in text else 'Unknown Account' }}
{% else %}
'Unknown Account'
{% endif %}
amountVar: >
{% set text = state_attr('sensor.your_device_last_notification',
'android.text') %} {% if text %}
{% set match = text | regex_findall('\$([0-9]+\.[0-9]{2})') %}
{{ match[0] if match else '0.00' }}
{% else %}
'0.00'
{% endif %}
dateVar: "{{ now().date() }}"
payeeVar: >-
{{ state_attr('sensor.your_device_last_notification', 'android.title')
}}
notesVar: Added with Home Assistant
response_variable: httpresponse
action: rest_command.actualbudget
- data:
level: info
message: "REST Response: {{ httpresponse }}"
action: system_log.write
Actual Tap was developed with Mobile Tap-to-Pay as the main use case. In order for that to function Actual Tap needs to be exposed to the internet. Below is a standard Caddyfile configuration:
actualtap.yourdomain.com {
@auth header X-API-KEY your-api-key
handle @auth {
reverse_proxy 0.0.0.0:3001
}
respond 401
}
Note: This project is in active development. Issues, pull requests, and feature requests are welcome.



