How to Auto-Categorize and Reconcile Bank-Style Stripe Payouts with AI
Run a scheduled workflow that pulls your recent Stripe charges and invoices, lets an Agent-mode Connector node assign accounting categories and match each line to expected revenue as structured JSON, then writes the reconciled results into a MySQL ledger table.
What This Integration Does
Finance teams that take payments through Stripe usually still have to read each charge by hand, decide which revenue category it belongs to (subscription, one-off sale, refund offset, fee), and tick it off against what they expected to bill that period. That manual reconciliation is slow and easy to get wrong once volume grows. This Spojit workflow does the reading and the matching for you: every morning it gathers the latest Stripe activity, asks an Agent-mode Connector node to categorize and reconcile each line into a fixed JSON shape, and lands a tidy, queryable ledger in MySQL that your reporting tools can read directly.
The workflow is started by a Schedule trigger on a cron expression, so no one has to press a button. On each run it lists charges and invoices created since the last window, hands that data to a Connector node in Agent mode with a Response Schema so the output is always valid JSON, and inserts the categorized rows into a MySQL table with an idempotency key on the Stripe object id. Because the insert keys on that id, re-running the workflow over an overlapping window updates or skips rows you already have instead of duplicating them, which keeps the ledger clean if a run retries or you widen the date range.
Prerequisites
- A Stripe connection in Spojit (an API key connection with read access to charges and invoices). See the Stripe connector article for setup.
- A MySQL connection pointing at the database that will hold your ledger. See the MySQL connector article.
- A destination table, for example
stripe_ledger, with columns such asstripe_id(unique),object_type,amount,currency,category,expected_match,confidence,charge_created, andreconciled_at. - A short, agreed list of accounting categories (for example
subscription,one_time_sale,fee,refund,uncategorized) so the AI output stays consistent. - Enough AI credits in your workspace for the Agent-mode step. See Managing Credits.
Step 1: Start with a Schedule trigger
Add a Trigger node and set its type to Schedule. Enter a 5-field Unix cron expression and an IANA timezone so the window aligns with your accounting day. For a run every weekday at 6am Sydney time, use:
0 6 * * 1-5
Australia/Sydney
The trigger output is { scheduledAt }, which you will use to compute the lookback window. A single Schedule trigger can hold more than one schedule if you want, for example, a mid-day catch-up run as well. For a full walkthrough of cron and timezone fields, see Setting Up a Schedule Trigger.
Step 2: Compute the lookback window
Add a Connector node in Direct mode on the date connector and pick the subtract tool to derive the start of your window from {{ trigger.scheduledAt }}. Subtract one day (or your run interval) to get a since timestamp. Stripe list endpoints filter on Unix seconds, so add a second date Direct-mode step with the unix tool to convert that since value into an epoch integer. Store it as {{ since_unix }} so the next steps can pass it as a created-after filter.
Step 3: List recent Stripe charges and invoices
Add a Connector node on the Stripe connector in Direct mode and select the list-charges tool. Map a created filter to your {{ since_unix }} value and set a sensible page limit (Stripe caps page size at 100). Save the result as {{ charges }}.
Add a second Stripe Direct-mode Connector node using the list-invoices tool, filtered the same way, and save it as {{ invoices }}. Listing both gives the AI the invoice context (what you expected to bill) alongside the actual charge that settled, which is what makes the reconciliation meaningful. If a window can return more than one page, wrap each list call in a Loop node that follows Stripe's pagination cursor until there are no more results.
Step 4: Categorize and reconcile with Agent mode
Add a Connector node and switch it to Agent mode. This is where the agent reads each charge, decides its accounting category, and tries to match it to a corresponding invoice line so you can see whether actual revenue lines up with expected revenue. Paste both lists into the prompt as variables and constrain the categories explicitly:
You are reconciling Stripe activity into an accounting ledger.
Charges: {{ charges }}
Invoices: {{ invoices }}
For each charge, assign exactly one category from:
subscription, one_time_sale, fee, refund, uncategorized.
Where possible, match the charge to an invoice by amount,
currency, and customer, and report whether the actual
amount matches the expected invoice amount.
Turn on the Response Schema so the node always returns structured JSON instead of prose. Define one object per ledger row, for example:
{
"type": "object",
"properties": {
"rows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"stripe_id": { "type": "string" },
"object_type": { "type": "string" },
"amount": { "type": "number" },
"currency": { "type": "string" },
"category": { "type": "string" },
"expected_match": { "type": "boolean" },
"confidence": { "type": "number" }
},
"required": ["stripe_id", "category", "amount", "currency"]
}
}
},
"required": ["rows"]
}
Save the node output as {{ reconciled }}. Because the schema forces a fixed shape, the next step can rely on {{ reconciled.rows }} without defensive parsing. For more on why a schema makes AI output safe to write straight into a database, see How to Use Structured Output for Reliable AI Data Extraction and Using Connector Nodes in Agent Mode.
Step 5: Write the ledger rows to MySQL
Add a Connector node on the MySQL connector in Direct mode. The cleanest approach is the insert-rows tool: point it at stripe_ledger and map the row fields from {{ reconciled.rows }}. If your reconciled output contains a list, wrap this insert in a Loop node set to ForEach over {{ reconciled.rows }} so each row is inserted in turn, mapping {{ row.stripe_id }}, {{ row.category }}, {{ row.amount }} and so on.
To make re-runs safe, prefer an upsert keyed on stripe_id. Use the execute-query tool with a parameterized statement so an existing Stripe object updates rather than duplicates:
INSERT INTO stripe_ledger
(stripe_id, object_type, amount, currency, category, expected_match, confidence, reconciled_at)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE
category = VALUES(category),
expected_match = VALUES(expected_match),
confidence = VALUES(confidence),
reconciled_at = NOW();
Always pass values as query parameters rather than building the SQL string by hand, so amounts and customer text cannot break the statement.
Step 6: Flag low-confidence matches for review
Add a Condition node after the insert that checks the AI's {{ row.confidence }} or {{ row.category }}. When a row comes back as uncategorized or below your confidence threshold, route the true branch to a Send Email node that emails your finance inbox a short summary of the rows that need a human eye. The Send Email node uses Spojit's built-in mail service, so no extra connection is needed; just make sure your finance address is on the org allowlist under Settings → General → Email recipients. This keeps the ledger trustworthy: clean lines flow straight through, and only the genuinely ambiguous ones land in someone's inbox.
Step 7: Save, enable, and let it run
Save the workflow and enable it so the Schedule trigger becomes active. From here, each scheduled run pulls the latest Stripe activity, categorizes and reconciles it, and grows your MySQL ledger automatically. You can ask Miraxa to extend the canvas at any time, for example: "Add a Condition node that checks if {{ row.expected_match }} is false and connect the true branch to a Send Email node." Miraxa knows the workflow you are editing and will wire the nodes for you.
Tips
- Make the categories an explicit, closed list in your prompt. A short fixed vocabulary keeps the AI output stable and your
GROUP BY categoryreports clean. - Keep your Schedule interval and your
datelookback in sync (a daily cron with a one-day subtract). Add a small overlap, say two days, and rely on theON DUPLICATE KEY UPDATEupsert to absorb the overlap without duplicates. - Set a sensible
limitonlist-chargesandlist-invoicesand paginate with a Loop. Large unpaginated windows make the Agent-mode prompt longer and more expensive. - Filter charges to settled or succeeded status before sending them to the AI so you are not reconciling failed or pending payments.
Common Pitfalls
- Stripe list filters expect Unix seconds, not an ISO date string. If you skip the
dateunixconversion in Step 2, thecreatedfilter will not behave as you expect. - Amounts in Stripe are in the currency's smallest unit (for example cents). Store the raw integer or divide consistently, and never mix the two in the same column.
- Without the
Response Schema, an Agent-mode node can return prose or slightly different field names between runs, which breaks the MySQL mapping. Always enable the schema for anything that writes to a table. - If your
stripe_idcolumn is not actually unique in MySQL, the upsert silently degrades into plain inserts and you will get duplicate ledger rows on overlapping windows. Add the unique key first. - Timezone drift: the cron timezone and your accounting day must match, or a run near midnight can pull the wrong calendar day's charges.
Testing
Before enabling the schedule, point the MySQL connection at a scratch table and run the workflow once over a narrow window (a single past day with a handful of charges) using the Run button. Inspect the execution log to confirm the Agent-mode node returned valid JSON in {{ reconciled.rows }} and that each row landed in MySQL with a sensible category. Run it a second time over an overlapping window to prove the upsert updates rather than duplicates. Once the categories and matches look right, swap the connection to your real ledger table and enable the Schedule trigger.
Learn More
- Stripe connector documentation
- MySQL connector documentation
- Connector node (Direct and Agent mode) documentation
- Trigger node and Schedule documentation
- How to Reconcile Shopify Orders with Stripe Charges
- How to Use SQL Queries to Drive Workflow Decisions
- How to Sync Revenue Data to a Database for Reporting