Reseller Licensing Ops
- Zoho: the system that holds what was sold, the plan, the price, the billing schedule.
- Google’s reseller console: the system that holds what is running on the client’s domain today.
- Where they drift: a subscription on one side and not the other, seat counts that do not match, a new Google product with no plan in Zoho, customer details recorded differently in each.
- This platform: one record holding both, and an alert the day they stop matching.
- Role
- Lead product designer & researcher
- Team
- Two designers, then three from halfway through
- Timeline
- May 2025 – mid 2026
- Status
- Live
The brief was a dashboard over the Google reseller console. The console was not short of information. The problem was that it and Zoho held different records of the same client, and nothing compared them.
Zoho had what the client agreed to pay for. The console had what the client was using. When the two did not match, somebody was using seats that nobody was charging for, and it stayed that way until a person went looking. So the platform does not just show both numbers. It compares them, and every difference it finds becomes a job with a status, a note of how long it has been open, and a way to close it.
I mapped the work as it was being done, step by step, and marked where it broke. I planned how the findings would be put together on a shared online board the whole team worked on. What the operators said sits beside the step that produced it. The opportunities come after. With the operations team I put those in order and decided what to build first. I then drew the account and subscription model, the discrepancy and request flows, and the client-facing request portal. Each one went from wireframe to final design. I ran the testing sessions with operators and reworked the designs from what they showed. I also built the component library and tokens this platform shares with the other internal tools.
Two designers, and three from halfway through. Both were early in their careers. Alongside the design work I was running reviews and pairing on components. I also wrote down rules we had been improvising, so that the three of us stayed consistent across the platform.
Everything on this page is a recreation. The client is not named; their branding, real identifiers, customer data and internal documents are replaced with fictional equivalents built for this portfolio. Figures appear only where they describe my own process, and anything commercially sensitive is left out.
The reseller is not named, and neither are its clients: every account, domain, e-mail address, telephone number, order id and seat count in these screens is invented. Zoho and the Google reseller console are named because they are off-the-shelf platforms that thousands of resellers run on; naming them says nothing about how this one works.
Two consoles, one client
Operators worked across two monitors and moved information between them by hand. Zoho held the commercial commitment: start dates, negotiated pricing, billing schedule. The console held what was running: seats assigned, products provisioned, domain state.
Neither was wrong. They answered different questions and updated at different times, and nothing reconciled the two.
Four teams inside the reseller work from these two systems every day. None of them are the clients. They are the people who bill the accounts, renew them, look after them, and answer for the revenue.
Eleven flows, and where they break
Before anything was designed, the operation was mapped: eleven end-to-end flows across licensing, sales and client success. Requests for extra seats, subscription lifecycle, suspension, payment tracking, cancellation, releasing a customer, plan changes, seat reductions, creating a plan for a new product, the monthly tax add-on, and the notification checks across them. Each flow has a lane per role, names the system behind every step, and marks where it breaks.
The problems came out of that mapping. Each flow is a run of steps. A step is one action by one person in one system. Suspend the subscription in the reseller console. Void the last invoice in Zoho. We drew the flows on that same shared board, and for every step the operators said what went wrong at that point in the job. It was written against that step, not collected into one list at the end. The four below are theirs, as they were written on the board. Keeping each problem next to the step that caused it is what showed the product had to compare the two systems rather than report on them.
New products and pricing updates require manual syncing, leading to inconsistency or delays.
Duplicate invoices can occur, and there’s no clear way to track or validate them. This leads to confusion and extra back-and-forth between teams.
When a subscription is in the dunning state, there is no next renewal date generated. This prevents the creation of a request, causing delays and confusion — especially when urgent actions are needed.
Both manual suspension and cancel-at-renewal display as ‘Suspended’ in the console. The only way to distinguish them is by checking the subscription history.
Two details from that mapping changed the product. The first was the way clients are reminded to pay. That already ran to a fixed schedule: reminders at forty-five, thirty, fourteen and seven days before the renewal date, then thirty days’ grace before the subscription is suspended. Those dates came from the business, not from the design, so the platform automates the schedule it found instead of proposing a new one. The second was a single word. In the upstream console, ‘Suspended’ covered two things. A manual suspension, and a cancellation set for the renewal date. The only way to tell them apart was to open the subscription history. That is why every status in this product says which system it came from.
Finding out, without looking
Before this platform, a mismatch was found because somebody went looking for it. Two routines did that work: a licensing dashboard read every day, and a weekly email of error checks. Anything that happened between those two routines waited.
Both sat on top of the notification channel that comes with the Google reseller console. Its messages were inconsistent enough that one operator kept a separate sheet to track what it had and had not sent. Some events produced no message at all: when Google suspends an account for passing its storage limit, the reseller is not told. Duplicated seat-addition requests, where the same extra seats are added a second time, were caught only by a comparison that ran monthly, so one could sit open for weeks.
The platform compares the two systems on a schedule and raises what it finds on the record itself. Four kinds were specified for the build: a subscription in the console with none in Zoho, seat counts that do not match, a new Google product not yet mapped to a Zoho plan, and customer details recorded differently in each system. Nobody has to remember to check.
A discrepancy is an object
The mismatch is not reported somewhere else. It appears on the subscription it concerns: a yellowish band above the record, naming the two counts that do not match, with the action that resolves it inside the band.
The disputed field is tinted yellow in the record below, so the operator can see which number is in question. The fix is applied from here, so nobody opens the other console by hand.
One client, eight subscriptions
Enterprise accounts run eight or more concurrent products, each with its own plan, seat count, billing schedule, and state of sync with the other system. A flat table of that is unreadable: horizontal scroll, truncated names, and the important number off the right edge of the screen.
Key decisions
The first two make the consequence visible before it is committed. The third I got wrong, and testing showed it.
Timing is a required field
Every plan change, seat adjustment and cancellation asks when it takes effect before it can be submitted. It also states what each option costs. Immediate, immediate with a charge for the days left in the month, or queued for the renewal date. Most disputes here were about when a change took effect, not about the change itself.
Readable plan names instead of raw ids
Operators were reading subscription IDs against documentation to work out which product they were looking at. The interface now shows readable plan codes with product badges. The raw identifiers are still there, with a copy button, for anyone debugging.
Account notes behind a floating button
Notes and history sat behind a floating action button in the corner. Testers read it as a support chat widget and never opened it. That included accounts whose first note said not to change seat counts without approval. Replaced with a counter in the header and a pinned banner for anything critical.
What testing changed
The prototype was a Figma file that did not work. One person from the client clicked through it on a call while three others watched and interrupted. That still shows whether someone can find what they need, and whether the words on screen mean what we think they mean.
What was cut, and why
The platform was designed in full and then reduced for cost and time. Four modules were removed, each costed at two weeks of build. There are two design files for this project, one before the reduction and one after.
The customer profile lost most of its second layer with them. Notes at profile and subscription level. Tags by team. Profile states such as an upgrade in progress. The per-subscription cards for billing and seat-request history. The notes work described in the decisions above was designed, tested, and then cut.
What survived is the reconciliation and the alerts. What went is the record of what had happened: the logs, the history, the notes. That is the material a team reads after something goes wrong, and it was cut first because the platform still runs without it.
Preventing the wrong action
A large part of this platform’s job is stopping people doing things that cause a problem later. If a client has an unpaid invoice, nobody can add seats to their account. The billing system calls that state dunning. The buttons stay on the screen, switched off, and hovering over one says why and what to do about it. An operator can tell being blocked from something being broken.
Clients ask for extra seats on a public form. There is no sign-in, so the form works out who they are from what they type: domain name, contact name, telephone number and e-mail. It checks the domain first, and answers in one of four ways.
- No domain was given.
- The domain is not one this reseller holds.
- The domain is known, but the other details do not match the record.
- This client is handled offline. The request is taken, and the seats are added by hand.
The form has a cart, so one client can ask for several subscriptions at once, each with its own number of seats. They pay with the card already on file. The request is approved as soon as the invoice is paid, and accounts on the trusted list are approved straight away.
Error messages work the same way. Both systems answer with a code. The platform turns each one into a sentence that says what went wrong, and gives the operator something to do.
One vocabulary, several tools
The platform is one of several internal tools that share a component library and a token set. A change to a control reaches all of them at once. Four button levels, used strictly: one solid primary per view, outline for secondary work, ghost for utilities, and a danger state reserved for things that cannot be undone.
A rule decides when a banner appears. An alert cannot be dismissed, sits above the thing it concerns, and has to carry a resolution. An announcement can be dismissed, sits at the top of the page, and carries a link at most. Without that rule every message becomes an alert and operators stop reading all of them. The same library holds the confirmations for each completed action, so a message about a mismatch never looks like a message about a saved change.
The internal design systemWhat I’d do differently
Both of these cost the project time later on.