The problem with the timesheet app

If you bill by the hour to a handful of clients, you have probably paid for Harvest, Toggl, Bonsai, Clockify or something like them. They are good products. They are also solving a problem that is smaller than their price suggests. You need to know when you started, when you stopped, what you were doing, and you need the total to become an invoice the client can pay. That is a spreadsheet with a payment button.

The part they get wrong is more interesting than the price. The time log lives in the app. The invoice arrives as a PDF with a single line: "Consulting, 38.5 hours, $3,272.50." The client who wants to know what the 38.5 hours were has to ask, and now you are in a thread defending your time instead of getting paid for it.

The method

Three pieces. A Google Sheet is the time log. Stripe Invoicing is the payment rail. A short script joins them and adds a review step.

The log. One row per block of work: date, start, end, break minutes, hours (a formula), category, what got done, and a column for approved overage. Start and Stop are menu items that stamp the current time, so you never reconstruct a day from memory. Hours round to the quarter hour if your contract says so, and the raw times stay in the row so nothing is lost.

The invoice. On invoice day the script sums the hours in the period, multiplies by the rate from a settings tab, and creates a Stripe invoice: customer, due date, line item, and the entire time log printed in the invoice footer, grouped by category. Stripe finalizes it and emails the client a Pay button. Card or ACH, your choice. The client sees exactly what they are paying for on the same page as the button.

The review. This is the piece that matters. Before anything is created, one screen shows the hours, the amount, the due date, every log entry, and a list of what would embarrass you: a block you forgot to stop that would bill zero, two rows with the same date and times that would bill twice, a row with no category, hours over the cap in your contract with no approval recorded. You look, you fix, then you click. Then you click again to confirm, because a send is a send.

Invoice amount = hours logged in the period × rate. Never typed. Refused if: zero hours · a second invoice for the same period · over cap without approved overage · Stripe total ≠ log total.

The rules, and why each one exists

Every rule below was learned on a real engagement before it was written into the script. That is the only reason to trust them.

The amount is never typed. The moment a number is typed, it can be wrong, and a wrong number on an invoice costs more trust than a late one.

Over-cap hours need a written yes before they are worked, not after. If your contract has a 40-hour period cap, the script refuses to invoice 43 hours unless a row in that period is marked Approved overage. That forces the conversation to happen when it is cheap.

The Stripe key is restricted. It can create invoices and read customers, and nothing else. It cannot move money, refund, or change payout settings. If it leaks, you roll it in Stripe and nothing bad happened. Never put a full secret key in a spreadsheet, and never put any key in a cell; the script stores it in its own properties.

The daily check only reads. Every morning a timer refreshes paid status from Stripe and emails you if the client paid, if an invoice is due to go out today, or if one is past due. It never creates or sends. Automation that can send money requests while you sleep is a liability, not a feature.

If Stripe's total does not equal hours times rate, void it and stop. This should never fire. It exists because "should never" is not a control.

Build it or buy it

You can build this yourself in an afternoon if you are comfortable in Apps Script. The Stripe API calls are four: create invoice, create invoice item, finalize, send. The hard parts are the review checks and the time zone handling, which is where a sheet stores dates in one zone and your script runs in another and an invoice quietly moves a day.

Or you can buy the version Quorra uses, with the client names stripped out and a Setup tab in their place. It is the Hourly Invoice Sheet, $29 once, unlimited clients, with a written guide and a refund if it does not work for you. The setup guide is public, so you can read exactly what it does before deciding.

What it does not do

Fixed fees, tax lines, multi-person time on one invoice, and expenses. For fixed fees, create the invoice directly in Stripe. For tax, turn on Stripe Tax on the customer. For a team, you have outgrown a spreadsheet and should pay for the app.

One more thing about the footer

Putting the time log on the invoice changed how clients read invoices. Nobody asked "what was this for" again. Several started skimming the log and replying with a thank-you for a specific item. That is not a billing feature. That is the client seeing the work, and it is the reason to do it this way even if you never touch the script.