Skip to main content
    All case studies
    Case study · Browser automation
    Meridian Operations · upstream system without an API

    Five deterministic steps. 99.2% success. No API.

    The upstream system offers no API, no webhook, and no bulk-import mechanism. Playwright drives the browser through a stable five-step UI flow; the LLM is reserved for diagnosing the 5% of failures from captured artifacts.

    0
    Deterministic Playwright steps per registration
    0.0%
    Success rate after one retry
    0
    Third-party error codes recovered automatically
    Built with
    PlaywrightPlaywrightPostgreSQLPostgresDrizzle ORMDrizzleORMMicrosoft AzureAzure
    The problem

    No API. No webhook. No bulk import. A 15-minute browser session per account.

    The upstream third-party system supports exactly one interface: its web UI behind a login form. The team was stepping through a five-step flow per account, dozens of times per month. Each session required staying focused on the browser for 15 to 20 minutes — and missing a branch condition on step 2 meant starting over from the beginning.

    • No public API, full stop
      The upstream third-party system offers no API, no webhook, and no bulk-import mechanism. The only supported interface is its web UI. Looking for an undocumented endpoint returned nothing usable. The manual browser session was the only path.
    • Shared transmissions page with no per-user filter
      After submitting a unique account identifier, the operator polled a shared page that showed submissions from all team members interleaved. Finding their own row meant scanning by username and submission time — a task that got harder the busier the team was.
    • Two error codes with known recoveries, zero documentation
      Two upstream codes — one meaning the identifier already exists, one meaning the account number is on file — have clear, recoverable responses. But the system displayed them as opaque codes with no guidance. Operators learned the responses by trial and error.
    • Conditional branching on account status
      Step 2 varies: if the account is on file, the operator goes to an update path; if not, a creation path with a tri-state agency-assign field and an identity-document checkbox. Missing the branch condition meant starting the session over.
    Manual session · per account
    15-20 min · attention required
    1Sign in0:48
    2POST identifier · poll shared page4:22
    3Branch (on-file? new path?)1:15
    4Toggle checkbox + tri-state field0:34
    5Capture bond + commit3:18
    Branch missed → start over+15:00
    Five steps · one branch · zero APIRedo on miss
    The pipeline

    From inbox to verified record in one pass

    Six steps end-to-end. Five are deterministic Playwright; the sixth is the recovery path with artifact capture for the 5% the LLM diagnoses afterward.

    01Trigger
    Workflow row queued
    A new account record lands in the workflow_items queue with credentials reference and status pending. Azure Functions picks it up.
    Azure Functions
    02Authenticate
    Headless browser sign-in
    Playwright launches a headless browser session and signs in with the stored credentials. The web UI is the only entry point — no API, no shortcut.
    Playwright
    03Submit
    Identifier POSTed; transmissions page polled
    The unique account identifier is POSTed via the form. The shared transmissions page is polled with backoff, the row matched by username and timestamp until status reads 'Accepted'.
    Playwright
    04Branch
    Creation or update path chosen
    Query whether the account is on file. Yes → update path. No → creation path with the identity-document checkbox toggled and the tri-state agency-assign set. Submit; wait for confirmation.
    Playwright
    05Bond
    Bond identifier captured; details committed
    Navigate to the account edit form. Capture the edit URL. Query the bond lookup page to extract the bond identifier. Update the company details, submit, and mark the workflow item complete.
    Playwright
    06Recover
    Known errors auto-recovered; unknowns captured
    Known error codes are recovered automatically and the flow continues. Unrecognized failures capture a screenshot, page HTML, and the transmission URL into JSONB — clean state on the next retry.
    PostgreSQL + Azure Blob
    Success path
    New account registered in 90 seconds end-to-end
    Workflow item marked complete. The new account is registered in the upstream system within 90 seconds end-to-end. The operations team gets a confirmation notification.
    Failure path with full context
    Artifacts captured; next retry runs against fresh state
    Error artifacts captured (screenshot, HTML, transmission URL). Workflow item marked failed-with-context. The next retry runs against fresh state — artifacts are cleared at lock-time so a previous failure's stale page state doesn't contaminate the new session. Known error codes are recovered automatically without human involvement.
    Why deterministic, not LLM-driven

    The upstream UI is stable. Every form field has a known selector, every page transition has a predictable trigger, and the branch condition is boolean. A scripted Playwright flow runs in 90 seconds and follows the same path every time. An LLM-driven browser agent would be slower, less predictable, and more expensive for a flow where every step is already known. The LLM is reserved for the 5% of failures that need diagnosis from captured screenshots and HTML.

    Validation review

    Per-step success confidence

    Step 5 (account number extraction) is the lowest-confidence step — it depends on a shared transmissions page that interleaves submissions across all users.

    Field-level confidence
    Pass 2 — Claude self-review
    Identifier POSTForm submission accepted by upstream
    97%High
    On-file checkBoolean read from labeled status field
    99%High
    Registration formBranch logic + form submission
    94%High
    Bond queryBond identifier extracted from lookup page
    91%High
    Account number extractionRead from the shared transmissions page
    65%Low
    Routed to human review. When multiple users' submissions interleave on the same page, the row that belongs to this submission has to be disambiguated by username and timestamp. On a busy afternoon, the page may not have fully settled when the script reads. Rare misreads go to the retry queue.
    4 of 5 fields cleared the 0.85 threshold
    model: Per-step confidence
    The stack

    Boring tech, glued together well

    Each vendor handles what it's best at. Aisyst owns the orchestration layer in between.

    PlaywrightPlaywright
    Playwright
    Deterministic browser automation across all five UI steps; selectors are stable, transitions are predictable
    PostgreSQLPostgres
    PostgreSQL
    Workflow item queue, status tracking, error artifacts JSONB column
    Drizzle ORMDrizzleORM
    Drizzle ORM
    Type-safe queries against the queue and audit tables
    Microsoft AzureAzure
    Azure Functions + Blob
    Queue worker pulls pending items; Blob stores screenshot and HTML on failure for diagnosis

    Third-party logos are trademarks of their respective owners and appear here only to indicate integration.

    Outcomes

    What changed when 'no API' stopped being a blocker

    The scripted path is fast, predictable, and cheap. The LLM reads captured screenshots and HTML to tell you what went wrong — not to drive the browser on every request.

    0
    Deterministic steps per registration
    0.0%
    Success rate after one retry
    0
    Error codes recovered without human
    0 sec
    End-to-end time per registration
    Watch retry rate broken out by step, not in aggregate

    If step 3 retries climb from 3% to 15%, the upstream system added a new validation. If step 5 retries climb, the transmissions page layout changed. The aggregate retry rate won't tell you which step broke; the per-step breakdown will. The fix is upstream: update the step's selector or polling logic — not the retry logic.

    If you log into a third-party portal manually because it has no API, this pattern fits

    Drive it deterministically with Playwright and reserve the LLM for diagnosing the 5% of failures. Fast, predictable, and cheap. We'll scope it on a 30-minute call.