DEV Community

choong
choong

Posted on

4 APIs to Automate Expense Reporting: Receipt Scan Slack Approval Payout

Every finance manager has a folder they don't talk about.

A quiet accumulation of receipt photos, Slack forwards, and email attachments with subjects like "pls reimburse ty." It grows a little every week, an it never shrinks on its own.

Here's how to stop feeding that folder, with a scan to bank transfer automated workflow.


The workflow

Employee submits a receipt → Veryfi extracts the data → Slack sends the manager an approve/reject message → QuickBooks logs the expense → Wise transfers the reimbursement.


API #1 — Veryfi (receipt OCR)

Veryfi takes a receipt image or PDF and returns structured JSON in seconds: vendor name, date, total amount, line items, currency, and tax.

It's pre-trained on hundreds of millions of receipts and works across 38 languages and 91 currencies.

No model training required on your end.

POST https://api.veryfi.com/api/v8/partner/documents/

Headers:
  CLIENT-ID: your_client_id
  AUTHORIZATION: apikey your_username:your_api_key
  Content-Type: application/json

{
  "file_url": "https://your-storage.com/receipts/receipt-001.jpg",
  "tags": ["expense", "reimbursement"],
  "categories": ["Meals", "Travel", "Office Supplies"]
}
Enter fullscreen mode Exit fullscreen mode

The response gives you total, vendor.name, date, currency_code, and a full line_items array. All of that feeds into the Slack message in the next step.

API #2 — Slack (manager approval)

Rather than routing approvals through email, you send the manager an interactive Slack message using Block Kit - Slack's UI framework for composing messages with buttons and structured data.

The manager sees the receipt details and clicks Approve or Reject directly in Slack.

POST https://slack.com/api/chat.postMessage

Headers:
  Authorization: Bearer xoxb-your-bot-token
  Content-Type: application/json

{
  "channel": "manager-slack-user-id",
  "text": "New expense reimbursement request",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*New expense request*\n*Employee:* John Doe\n*Vendor:* Marriott\n*Amount:* USD 189.00\n*Date:* 2025-03-10"
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": { "type": "plain_text", "text": "Approve" },
          "style": "primary",
          "action_id": "approve_expense",
          "value": "expense_id_123"
        },
        {
          "type": "button",
          "text": { "type": "plain_text", "text": "Reject" },
          "style": "danger",
          "action_id": "reject_expense",
          "value": "expense_id_123"
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

When the manager clicks a button, Slack sends an interaction payload to your app's endpoint with the action_id and value. You use that to trigger the next step.

Tips: You'll need to enable Interactivity in your Slack app settings and provide a Request URL — that's the endpoint your app exposes to receive button click payloads. Test this locally first using a tunnel like ngrok before deploying.

API #3 — QuickBooks (accounting sync)

Once approved, you log the expense in QuickBooks Online via the Purchase endpoint. This creates a record in the books against the right account and employee.

POST https://quickbooks.api.intuit.com/v3/company/{realmId}/purchase

Headers:
  Authorization: Bearer your_oauth2_access_token
  Content-Type: application/json
  Accept: application/json

{
  "PaymentType": "Cash",
  "AccountRef": { "value": "35", "name": "Checking" },
  "EntityRef": { "value": "employee_id", "type": "Employee" },
  "TotalAmt": 189.00,
  "Line": [
    {
      "Amount": 189.00,
      "DetailType": "AccountBasedExpenseLineDetail",
      "AccountBasedExpenseLineDetail": {
        "AccountRef": { "value": "7", "name": "Travel" }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

API #4 — Wise (reimbursement payout)

Wise handles the actual bank transfer to the employee.

It's API-first, uses the mid-market exchange rate with a flat transparent fee, and works for both domestic and international payouts across 40+ currencies.

The payout flow is three calls: create a quote, create the transfer, then fund it.

# Step 1 — Create a quote
POST https://api.wise.com/v3/profiles/{profileId}/quotes

{
  "sourceCurrency": "USD",
  "targetCurrency": "USD",
  "sourceAmount": 189.00,
  "targetAccount": "<recipient_account_id>"
}

# Step 2 — Create the transfer
POST https://api.wise.com/v1/transfers

{
  "targetAccount": "<recipient_account_id>",
  "quoteUuid": "<quote_id_from_step_1>",
  "customerTransactionId": "expense_id_123",
  "details": { "reference": "Expense reimbursement — March 2025" }
}

# Step 3 — Fund the transfer
POST https://api.wise.com/v3/profiles/{profileId}/transfers/{transferId}/payments

{
  "type": "BALANCE"
}
Enter fullscreen mode Exit fullscreen mode

Piecing things together

Your app receives the receipt, calls Veryfi to extract the data, formats it into a Slack Block Kit message, and waits for the manager's response.

On approval, it logs the expense in QuickBooks and triggers the Wise payout.

The whole chain from submission to transfer initiation runs without anyone opening a spreadsheet.


Final note: What this doesn't cover

Receipt policy enforcement: flagging expenses that exceed per-category limits, and multi-level approval chains are not covered here.

Those are logic layers you can add on top of the Slack interaction step, no additional APIs needed.

Top comments (0)