
๐ค Ghostwritten by GPT 5.4 ยท Fact-checked & edited by Claude Opus 4.6
Sometimes "there's an API" is not true in the way engineers need it to be true. Extending Ratchet to handle Google Voice administration looked straightforward โ check license status, assign a number, confirm the result โ but the actual platform boundary was sharper than expected.
The Google Voice API covers essentially one side of the problem: license operations. It does not provide a practical API path to assign a phone number to a user. That left a split design. Ratchet could use the API where the API was real, then fall back to guarded admin console automation where it was not.
That design worked, but only after a specific failure mode surfaced: the offered number rotated between headless page loads. The number first seen in the console could differ from the number actually available when the assignment action ran. The only safe pattern was to perform the action, read the console back, and assert the assigned result before declaring success. That sounds obvious in hindsight. It was not obvious before the integration started failing in exactly that way.
TL;DR: A vendor can technically have an API and still force part of the workflow through an admin console, which changes the engineering, reliability, and security model of the integration.
The original assumption was common enough to seem harmless: Google Voice is a Google admin product, so there should be an API surface for the core administrative actions. That assumption held for licensing but broke for number assignment.
This distinction matters because engineers often compress several questions into one:
For Google Voice admin, the answer to the first question was yes, but the answer to the more important workflow question was no. In practice, that meant Ratchet needed a two-mode integration instead of a clean single-surface implementation.
The usable API surface was centered on license-related operations โ checking whether a target user had the right Voice entitlement and whether the system should proceed.
The critical missing piece was number assignment. There was no practical management API path for "assign this user a phone number" in the way an operator would do it through the admin interface. Once that gap was clear, the architecture had to change.
A useful rule emerged: treat "API available" as a claim that must be tested against the exact administrative action, not against product marketing or a partial reference surface. Integration planning goes wrong when teams validate the existence of an API but not the completeness of the workflow.
The implication is broader than Google Voice. Plenty of enterprise systems expose APIs for reporting, inventory, or licensing while reserving the most operationally important workflows for a browser-only admin surface. When that happens, browser automation stops being a temporary hack and becomes part of the production design.
TL;DR: Ratchet uses the API for license-aware checks and a guarded headless browser flow for number assignment, with console readback as the source of truth.
Once the product boundary was understood, the implementation became a composition problem. Ratchet needed to decide which actions belonged to the API path and which belonged to the console path.
| Capability | Integration mode | Why | Reliability note |
|---|---|---|---|
| License status check | API | License operations exposed cleanly | More stable than UI automation |
| Admin status readback | Console read | Console reflects operational truth | Subject to UI load timing |
| Number assignment | Guarded headless console flow | No practical API path exists | Fragile without verification |
| Success confirmation | Console readback + assertion | Offered value not stable across loads | Mandatory for correctness |
This split was less elegant than a pure API integration, but more honest. It acknowledged that the platform itself had two different control planes: one programmable and one effectively browser-bound.
The first mental model was simple: load the admin page, inspect the offered number, trigger assignment, report success. That model assumed the page state was stable enough that "offered number" and "assigned number" would match.
That assumption failed.
The console could present one offered number on an initial headless load, then rotate to a different offered number on a subsequent load or action path. The UI was not a static rendering of a deterministic backend response โ it behaved more like a live allocation surface.
That changed the trust model completely. The first observed value was no longer something the system could safely carry forward as intent fulfillment. It was merely a candidate value seen at one moment in one page state.
After that discovery, the source of truth became post-action console state, not pre-action offer state:
TL;DR: The number shown as available in a headless session can change between page loads, so any automation that trusts the first-seen value is vulnerable to silent mismatch.
This was the sharpest lesson in the build.
The admin console presented an offered number during the assignment flow. In a normal human workflow, that seems straightforward: the admin sees a number, clicks through, and expects that number to be assigned. But under headless automation, repeated loads and timing differences exposed more dynamic behavior. The offered number could rotate.
That meant the system could not safely say, "I saw number X, therefore number X is what was assigned." The exact failure mode was worse than a hard error โ the workflow could appear to succeed while assigning a different number than the one first observed.
For admin automation, that is the dangerous class of failure: not a crash, but a plausible success report attached to the wrong result.
This was not a brittle selector problem or an occasional timeout. It was a semantic instability problem.
Typical browser automation failures look like this:
Those are annoying but usually obvious. The rotating offered number is different because the automation can continue running and still return the wrong business outcome. That is why the mitigation had to be stronger than retries โ retrying a flow that reads unstable offered state can simply produce a different wrong assumption.
The stable pattern became:
A sanitized illustration:
result = assign_number_via_console(
user_email="user@example.com",
confirm=True,
)
assigned = read_voice_assignment_from_console(
user_email="user@example.com"
)
if not assigned:
raise RuntimeError("No number assigned after console action")
if result.expected_mode == "assign_any_available_number":
assert assigned.status == "assigned"
else:
assert assigned.number == result.intended_number
return {
"status": "confirmed",
"assigned_number": assigned.number,
"source": "admin_console_readback",
}The important point is not the syntax. The important point is that the mutating action and the success condition are separated by an explicit verification step.
TL;DR: Driving an admin console gives an agent real tenant-admin power, so every mutating action needs explicit confirmation, constrained scope, and post-action verification.
Browser automation against an admin surface changes the risk profile. An API usually provides structure by default โ named operations, typed fields, explicit responses, and often narrower credentials. An admin console does not provide those same safety rails. It exposes broad authority through a user session, and the automation layer has to recreate discipline that the UI itself does not enforce for machines.
Every mutating action should be treated as high consequence.
The guarded pattern for Ratchet's Google Voice path included these principles:
These are not abstract best practices. They are responses to concrete failure modes.
Headless admin automation is sensitive to session handling, page timing, and console behavior under load. Even when selectors are stable, the rendered state may not be. That makes "successful click execution" a poor proxy for "correct administrative outcome."
A useful mental model: the browser is part transport, part runtime, and part observation layer. Each of those can drift.
Console readback turns a fragile sequence of UI interactions into a guarded state transition with a verifiable end condition.
Any implementation in this category also needs disciplined sanitization. Admin automation code and logs should avoid exposing tenant-identifying domains, assigned phone numbers, client identifiers, user GUIDs, or secret references. Placeholder values such as user@example.com, your-project-id, and +1-555-0100 are sufficient to explain the pattern without exposing operational details.
Because the practical API surface covers license-related operations but not the number assignment workflow needed for administration. Once that gap became clear, the only workable path for assignment was through the admin console itself.
It means the integration uses the API where it is real and complete, then falls back to another controlled mechanism โ admin console automation โ when the API does not expose the needed action.
Because the offered state shown before a mutating action may not be stable or authoritative. The Google Voice assignment flow can rotate the offered number between headless loads, so only post-action readback can confirm what was actually assigned.
It can be, but only with strong guardrails. Explicit confirmation, narrow action scope, careful session handling, and post-action verification are what make it operationally defensible. Without those, it is too easy to produce false success.
Do not validate integrations at the level of "there is an API." Validate them at the level of the exact administrative outcome the system must produce. If the workflow crosses into a console, design the verification loop before trusting the automation.
The Google Voice integration clarified a durable rule: production automation should be designed around the real control surface, not the hoped-for one. When a platform splits responsibility between an API and an admin console, the engineering challenge is no longer just connectivity. It becomes a correctness problem, a verification problem, and a security problem at the same time. The readback-and-assert pattern โ perform the action, then confirm the result before declaring success โ is the minimum viable discipline for any agent wielding admin-console authority.
Discover more content: