How do I set up interactive Slack approval buttons for invoice approval?
Post the invoice as a message with an interactive button block, then respond to the click by opening a modal with the trigger_id Slack sends in the interaction payload — within three seconds, before any lookup or validation. Loopfour's Slack integration supports this through sendMessage (with blocks) and openModal, but the modal call has to happen first, not last.
Part of the finance integrations guide.
| What posts the button | sendMessage (chat.postMessage) with a blocks array containing a button element |
|---|---|
| What Slack sends back on click | An interaction payload including a trigger_id, valid for three seconds and single-use |
| What opens the approval form | openModal (views.open), called with that trigger_id |
| What populates it afterward | updateModal (views.update) — not trigger_id-gated, safe to call after slower checks |
| Common mistake | Running a permissions or invoice-status check before calling openModal, which burns the three-second window |
Posting the approval button
Loopfour's Slack integration posts the invoice message via sendMessage, which maps to Slack's chat.postMessage. To make the message interactive, the request includes a blocks array — Slack's own documentation describes blocks as "a JSON-based array of structured blocks, presented as a URL-encoded string," and a button is one of the interactive elements a block can contain. The message's plain text field still matters even with blocks present: Slack treats it as the fallback shown in notifications and previews.
What happens the moment someone clicks it
Clicking the button doesn't call anything in your workflow directly — Slack sends an interaction payload to your app. Slack's own docs describe this as the mechanism by which "certain events and interactions occur between users and your app, you'll receive a trigger_id as part of the interaction payload." That trigger_id is what openModal needs to actually show a form. It's short-lived by design: three seconds, one use (the sibling diagnostic on this cluster covers what happens when that window is missed).
The order that actually works
Call openModal immediately, with a minimal or loading-state view, before doing anything else. Once the modal is open, use updateModal to fill it with the real approval form — the invoice amount, vendor, and an approve/reject choice — after any permissions check or data lookup finishes. updateModal identifies the view by viewId or externalId rather than trigger_id, so it isn't subject to the three-second limit. This is the only reliable order: open first, populate second.
Handling the approve or reject submission
When the approver submits the modal, Slack sends another interaction payload — this one with the form's values and a fresh trigger_id if you need to open yet another modal (a confirmation, for instance). The original invoice message can then be updated via updateMessage to show the outcome, so the approval trail stays visible in the channel rather than only inside a modal no one else saw.
Next step
Map the finance workflow with the most exposure and prove the automation path.
Bring the invoice, contract, payment reconciliation, or customer finance workflow you have to defend at audit. Loopfour can map the trigger, controls, integrations, and approval loop.
Checklist
Invoice approval button: setup order, not just setup steps
- Post the invoice with sendMessage, including a blocks array with an approve/reject button
- On the interaction payload, call openModal with the trigger_id as the very first action — no lookups first
- Run the permissions check and invoice-status lookup after the modal is already open
- Call updateModal with the real form (or an access-denied view) using viewId, not trigger_id
- On submission, call updateMessage on the original post so the outcome is visible in-channel
Frequently Asked Questions
Sources
Related
Topic
AP & Invoice Processing
Accounts payable and invoice processing is the set of steps a vendor bill goes through between arriving at a company and turning into a payment: capturing what the vendor sent, checking it against wha…
Read moreDiagnostic
What causes a three-way match failure between the PO, receipt, and invoice?
A three-way match compares the PO, the receipt (what was actually delivered), and the invoice on quantity, price, and charges. A failure means one of those three disagrees beyond tolerance — usually because the invoice bills a quantity that isn't fully receipted yet, the unit price differs from the PO, or the invoice adds a charge, like freight, the PO never included.
Read moreTopic
Integrations
Every finance automation vendor publishes an integrations page: a grid of logos, a claim of "seamless" connectivity, and not much else. That page answers a marketing question — does this vendor touch…
Read moreDiagnostic
What breaks when a Slack approval modal's trigger_id expires before approval?
Slack's trigger_id is valid for only three seconds after the interaction that generated it, and can be used once. If an AP approval workflow does anything asynchronous — a permissions check, a lookup — between the button click and calling views.open, the trigger_id can expire, returning an expired_trigger_id error instead of opening the modal. The click appears to do nothing.
Read more