How to bill a client for agent hours, with proof
We bill clients for agents by the hour. Most of our clients run outbound campaigns, they pay for hours on the dialer, and once a month they get an invoice that says how many.
The hard part of that invoice is never the rate. It's the question that can come back a day later: "Were these hours real?"
It's a fair question. An hourly invoice is a claim, and the client has no way to check it unless you give them one. This is how we set ours up so the answer is already in their hands.
We agreed what an hour is before the first shift
"Hours worked" sounds obvious until two people define it. On a dialer an agent spends more of the day waiting for the next call than talking. Is that billable? What about the ten minutes when the client's own list ran out?
We now write it down per campaign, in the agreement, in plain words. Time on a call, time ready and waiting for one, and wrap-up after a call are billable. Breaks and lunch are not. Training is billable when the client asked for it. Downtime on their side is billable; downtime on ours is not.
Nobody enjoys this conversation at the start. Everybody is glad it happened by month two.
The hours come off the dialer, not off a form
We don't use timesheets. A timesheet filled in on Friday is a memory, and the client knows it.
The agent's status comes from the dialer itself: ready at 9:00, talking at 9:02, wrap-up at 9:04, break at 10:30. Those status changes are the record, and the hours are added up from them. When a client asks about a Tuesday, we open the Tuesday.
This only works if you're honest about the days the record is wrong, which brings us to the part most people skip.
The record breaks sometimes. Say so.
We found a nasty one in our own setup. When the link between the dialer and our system drops, the system keeps showing the last status it heard. An agent can be "ready" on paper for an hour they spent on calls, or on a break. Nobody is lying. The link has stopped reporting, and nothing says so.
You can't stop links from dropping. What we changed is this: when the dialer stops reporting, that stretch is now marked. The manager sees it live, and the timesheet says so afterwards in plain words, something like "40 minutes of degraded sync, status may be off". A human then decides what that stretch was.
A marked gap is uncomfortable to show a client. It is far less uncomfortable than a clean-looking month they later find a hole in.
One invoice line is a question. A breakdown is an answer.
"320 hours" invites "which 320?" So the invoice is per campaign, and under each campaign it lists each agent and their hours for the period. If one agent covered two of the client's campaigns, the day is split between them, not counted in both.
Three numbers never share a screen: what the client pays per hour, what the agent earns, and any bonus. The client's rate is not on a payslip, and our margin is not something a team leader can read off a report by accident. It also means a raise for an agent doesn't quietly turn into a price change for a client.
One more thing: billing periods typed by hand overlap. One ends on the 15th, the next starts on the 15th, and the 15th gets billed twice. Each period now starts where the last one stopped, and an hour that's been billed can't be billed again.
Nothing on the invoice should be news
Clients get a summary every Monday: who worked, how many hours against the plan, what happened. The idea is simple. By the time the invoice lands, they've already seen every number on it.
If you set up one thing from this page, set up that.
What we use
All of this runs on our own platform, because we built it for exactly this job. Status comes from the dialer, each campaign says which statuses are paid time (time tracking), and the invoice is built from those hours with the breakdown per agent (payslips and invoices). The agent's payslip comes from the same record, so the two can't disagree about how long someone worked.
And if you'd rather not run a floor at all, we run them for clients too.
Run your whole team in one place.