Examples / Overdue invoice chaser

Overdue invoice chaser

Every Monday morning, read open invoices from Stripe, work out which are overdue and by how much with a JavaScript step, and email finance a short, prioritized list.

StripeJavaScriptYour AI modelsEmail

What you tell the assistant

Every Monday at 8am Toronto time, email finance@northwind.example a list of Stripe invoices that are past due, biggest first, with how many days late each one is.

Why it works

  • The Stripe connection's key never appears in the workflow, and api_get reaches the invoice list with the same filters Stripe's own API takes.
  • The JavaScript step does the date math and the totals, so the model only writes the email.
  • The email connection's allowed recipients mean this workflow can only ever email your own domain.
Build this workflow

What it builds

Trigger
Schedule
0 8 * * 1 · America/Toronto
tool
Open invoices
stripe.api_get
javascript
Find the overdue ones
→ lines, count, total
llm
Write the email
fast · structured
if overdue.output.count gt 0
tool
Email finance
email.send_email
if overdue.output.count gt 0
View the definition
trigger:
  kind: schedule
  cron: "0 8 * * 1"
  timezone: America/Toronto
steps:
  - key: invoices
    name: Open invoices
    kind: tool
    tool: stripe.api_get
    args: { path: /invoices, params: { status: open, limit: 100 } }
  - key: overdue
    name: Find the overdue ones
    kind: javascript
    inputs: { invoices: "{{ steps.invoices.output.data }}", now: "{{ run.started_at }}" }
    outputs: { lines: array, count: integer, total: string }
    code: |
      function main({ invoices, now }) {
        const today = Date.parse(now) / 1000
        const late = invoices
          .filter(invoice => invoice.due_date && invoice.due_date < today)
          .map(invoice => ({ ...invoice, days: Math.floor((today - invoice.due_date) / 86400) }))
          .sort((a, b) => b.amount_due - a.amount_due)
        const cents = late.reduce((sum, invoice) => sum + invoice.amount_due, 0)
        const lines = late.map(i => `${i.number} · ${i.customer_name} · $${(i.amount_due / 100).toFixed(2)} · ${i.days} days late · ${i.hosted_invoice_url}`)
        console.log(`${late.length} of ${invoices.length} open invoices are overdue`)
        return { lines, count: late.length, total: `$${(cents / 100).toFixed(2)}` }
      }
  - key: write
    name: Write the email
    kind: llm
    model: fast
    if: { path: steps.overdue.output.count, gt: 0 }
    prompt: |
      Write a short email to the finance team about overdue Stripe invoices.
      Open with the total ({{ steps.overdue.output.total }} across {{ steps.overdue.output.count }} invoices),
      then list them as given, biggest first, and flag anything more than 30 days late.
      {{ steps.overdue.output.lines }}
    schema:
      type: object
      properties: { subject: { type: string }, text: { type: string } }
      required: [subject, text]
  - key: send
    name: Email finance
    kind: tool
    tool: email.send_email
    if: { path: steps.overdue.output.count, gt: 0 }
    args: { to: [finance@northwind.example], subject: "{{ steps.write.output.data.subject }}", text: "{{ steps.write.output.data.text }}" }

A sample run

What each step produces

Open invoices tool

succeeded
Calling stripe.api_get
{
  "data": [
    {
      "id": "in_1Q8a",
      "number": "NW-0412",
      "customer_name": "Harbor Freight Co.",
      "amount_due": 480000,
      "due_date": 1788264000,
      "hosted_invoice_url": "https://invoice.stripe.com/i/nw-0412"
    },
    {
      "id": "in_1Q8b",
      "number": "NW-0437",
      "customer_name": "Lumen Studio",
      "amount_due": 129000,
      "due_date": 1789473600,
      "hosted_invoice_url": "https://invoice.stripe.com/i/nw-0437"
    },
    {
      "id": "in_1Q8c",
      "number": "NW-0441",
      "customer_name": "Kestrel Health",
      "amount_due": 64000,
      "due_date": 1790683200,
      "hosted_invoice_url": "https://invoice.stripe.com/i/nw-0441"
    }
  ],
  "has_more": false
}

Find the overdue ones javascript

succeeded
2 of 3 open invoices are overdue
{
  "lines": [
    "NW-0412 · Harbor Freight Co. · $4800.00 · 20 days late · https://invoice.stripe.com/i/nw-0412",
    "NW-0437 · Lumen Studio · $1290.00 · 6 days late · https://invoice.stripe.com/i/nw-0437"
  ],
  "count": 2,
  "total": "$6090.00"
}

Write the email llm

succeeded
Using model fast (Claude Haiku 4.5, Murmurator AI)
{
  "text": "",
  "data": {
    "subject": "2 overdue invoices, $6,090 outstanding",
    "text": "Hi team,\n\n$6,090.00 is overdue across 2 invoices this week:\n\n- NW-0412, Harbor Freight Co., $4,800.00, 20 days late\n- NW-0437, Lumen Studio, $1,290.00, 6 days late\n\nHarbor Freight is the one to chase first. Links to both are in Stripe.\n"
  }
}

Email finance tool

succeeded
Calling email.send_email
{
  "id": "4f1c9a0e-5b2d-4c8e-9f3a-1d2e3f4a5b6c",
  "to": "finance@northwind.example"
}

Trigger payload

{
  "scheduled_at": "2026-09-21T12:00:00Z"
}

Your next automation is one sentence away.

14-day free trial with $5 of built-in AI included. Cancel anytime.