Every 15 minutes
Approve supplier invoices in Slack before they become bills
Supplier invoices from your inbox are read and posted to Slack with Approve and Reject buttons, and approved ones become QuickBooks bills.
- Runs
- Every 15 minutes
- Apps
- QuickBooks, Slack
- AI
- Yes, uses the built-in AI allowance
- Access
- Slack chat:write; QBO accounting
Send this to your assistant
Create a ClickRuntime automation: every 15 minutes, read new invoice emails with AI and post each to #finance with Approve and Reject buttons; when someone approves, create the bill in QuickBooks.
First time? Adding ClickRuntime to your assistant takes about two minutes.
Why use it
Why teams use this automation
In a company with a few budget holders, supplier invoices need a yes from the person who ordered the work before they're paid. That approval usually happens over email forwards and 'did you approve this?' messages, and bills sit unentered while people chase each other.
Posting each invoice to Slack with an Approve button puts the decision where the approver already is. One click creates the bill, and the channel keeps a record of who approved what.
What you get out of it
Approvals in one click
Budget holders approve from Slack on their phone instead of digging through forwarded emails.
A record of every decision
Each invoice message shows who approved or rejected it and when.
Only approved spend enters the books
Bills are created after approval, so disputed invoices never become payables.
No retyping
Vendor, number and amount are read from the invoice and used for the bill.
How it works
What happens, step by step
Every 15 minutes, ClickRuntime checks your inbox for new invoice emails.
AI reads each one and picks out the vendor, invoice number and amount.
It posts the invoice to #finance in Slack with Approve and Reject buttons.
When someone clicks Approve, the bill is created in QuickBooks and the message is updated with who approved it.
Each invoice is remembered, so it's never posted or billed twice.
Before it runs live
- Connect QuickBooks
- Connect Slack, then invite the app to the channel (type /invite @ClickRuntime there)
What you get
Build your own
Need it to work differently?
Your own rules
Conditions, thresholds, filters and exceptions, written the way you'd explain them to a colleague.
Any number of apps
One automation can read from one app and update several others. Over 248 apps are ready, and anything with an API works too.
AI where it helps
Summaries, classification, extraction and drafts use ClickRuntime's built-in AI, with no extra key.
On a schedule or on each event
Runs as often as every 5 minutes, or the moment an app sends an event, and keeps track of what it already handled.
Changes you can ask for
- Act only on the items that match your own rules, and skip the rest
- Change what the AI looks for, or how it writes the result
- Add another app, for example also send it to Google Sheets
- Combine it with other QuickBooks and Slack steps into one automation
Under the hood
The code that runs this automation
import json
from urllib.parse import parse_qs
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from slack_sdk import WebClient
from clickruntime import on_trigger, on_event, credentials, http, state, ai, report_fetched
API = "https://quickbooks.api.intuit.com/v3/company/1234567890" # your QuickBooks company id
def qb(method, path, **kw):
r = http.request(method, API + path, params={"minorversion": 73, **kw.pop("params", {})}, **kw, headers={"Accept": "application/json", "Authorization": "Bearer " + credentials.connection("quickbooks", "company")})
return r.raise_for_status() or r.json()
@on_trigger
def run(event):
gmail = build("gmail", "v1", credentials=Credentials(credentials.connection("google", "work")), cache_discovery=False)
seen = set(state.get("seen") or [])
msgs = [m for m in gmail.users().messages().list(userId="me", q="label:Invoices newer_than:3d").execute().get("messages", []) if m["id"] not in seen]
report_fetched(len(msgs))
if not msgs:
return {"summary": "Nothing new"}
slack = WebClient(token=credentials.connection("slack", "work"))
for m in msgs:
snippet = gmail.users().messages().get(userId="me", id=m["id"]).execute()["snippet"]
f = ai.generate(f"Return JSON vendor, number, amount from:\n{snippet}", max_output_tokens=80, json_schema={"type": "object",
"properties": {"vendor": {"type": "string"}, "number": {"type": "string"}, "amount": {"type": "number"}}}).json
value = json.dumps(f)
slack.chat_postMessage(channel="#finance", text=f"{f['vendor']} #{f['number']}: ${f['amount']:,.2f}", blocks=[
{"type": "section", "text": {"type": "mrkdwn", "text": f"*{f['vendor']}* invoice {f['number']}: ${f['amount']:,.2f}"}},
{"type": "actions", "elements": [{"type": "button", "text": {"type": "plain_text", "text": t}, "action_id": t.lower(), "value": value}
for t in ("Approve", "Reject")]}])
seen.add(m["id"])
state.set("seen", list(seen)[-500:])
return {"posted": len(msgs)}
@on_event
def clicked(event):
p = json.loads(parse_qs(event.body)["payload"][0])
action, f = p["actions"][0]["action_id"], json.loads(p["actions"][0]["value"])
if action == "approve":
v = qb("GET", "/query", params={"query": f"select * from Vendor where DisplayName = '{f['vendor']}'"})["QueryResponse"]["Vendor"][0]
qb("POST", "/bill", json={"VendorRef": {"value": v["Id"]}, "DocNumber": f["number"], "Line": [{"Amount": f["amount"],
"DetailType": "AccountBasedExpenseLineDetail", "AccountBasedExpenseLineDetail": {"AccountRef": {"value": "7"}}}]})
WebClient(token=credentials.connection("slack", "work")).chat_update(channel=p["channel"]["id"], ts=p["message"]["ts"], blocks=[],
text=f"{f['vendor']} #{f['number']} ${f['amount']:,.2f}: {action}d by <@{p['user']['id']}>")
return {"action": action}FAQ
Common questions
Who can approve?
What happens when someone rejects?
What does the Slack app need?
What permissions does it need?
Does it need AI?
How many runs does it use?
Can I change how it works?
Related
You might also like
Post a weekday overdue invoice report to Slack
Every weekday morning, Slack shows your overdue QuickBooks invoices and total receivables by how late they are.
See how it worksPost every new QuickBooks invoice to Slack
Whenever an invoice is created in QuickBooks, your finance channel sees the customer, amount and due date within seconds.
See how it worksLabel new emails automatically
New mail is sorted into labels like Inquiry, Invoice and Newsletter within minutes of arriving.
See how it worksKeep exploring
Set up “Approve supplier invoices in Slack before they become bills” from your assistant
Copy the prompt above, or connect your assistant first. Free plan, no credit card.