Solutions engineering Implementations & integrations Open to New Roles

Good things happen
between systems.

Connecting platforms, data, and the people who depend on them. I build integrations and the tools that make a complicated workflow easier to run, and easier to diagnose.

Tools of the trade
  • JavaScript
  • Node.js
  • PHP
  • WordPress
  • SQL
  • Docker
  • REST APIs
01 / Selected work

Built to connect.
Designed to hold up.

What I built, and the decisions behind each one.

01Integration architectureWordPress → CRM

Form to CRM Bridge

Solve the shared integration once. Adapt only the part that changes.

Every new client site repeated the same capture, mapping, and delivery work, and each rebuild was another chance to drop a lead quietly. I separated the shared core from the platform-specific adapters, so adding a CRM is a contained piece of work with an obvious place to plug in.

Role
Sole engineer: architecture through production.
Built
Plugin architecture, form normalization, platform adapters, and delivery handling.
Outcome
Adding a destination is now one adapter rather than another rebuild, and a delivery that fails keeps its payload instead of disappearing.
PHPWordPressREST APIsGravity Forms
bridge-demo.msschermer.usOpen The Form to CRM Bridge live demo. A control selects how the recipient CRM fails, shown here as "CRM is down (500)", beside an intake form and a lead inbox that displays the normalized result. The demo lets you choose the failure, then watch the transport handle it.
Inside the build / Boundaries, delivery, and tradeoffs

One submission. Different destinations.

Mapping example
Form fields01 / Capture
commentsA new inquiry
02 / Normalize once · map per platform
HUBSPOT / CONTACT03 / Adapter
{
  "properties": {
    "email": "[email protected]",
    "message": "A new inquiry"
  }
}

HubSpot nests contact fields under properties. The form capture and normalization stay the same; the adapter owns the destination format.

Illustrative field subset · sample data · no request is sent
A closer look

One core. A clear boundary.

Switch destinations to see how one normalized inquiry becomes three platform-specific payloads.

A boundary that removes repetition

Gravity Forms and Elementor Pro feed a canonical field set. Downstream adapters handle authentication, the destination’s request format, and the meaning of its response.

Delivery needs a recovery path

Network and server failures get bounded retries with backoff. Failed deliveries include the payload in the logs for recovery. A rejected payload needs investigation, not an endless retry loop.

An adapter has a sensible limit

Shared transport keeps the plugin compact. Retries currently happen inside the request. Higher volume would need a background queue that owns delivery separately.

02Observability & automationScheduled checks → useful history

Performance Monitor

Turn isolated speed tests into a history you can act on.

A Google Apps Script workflow was producing numbers nobody could act on: no history, and a failed API call looked identical to a real regression. I rebuilt it as a standalone service that preserves every measurement and keeps those two cases distinguishable.

Role
Sole engineer: migration, service, and deployment.
Built
Service rebuild, scan scheduling, SQLite data model, dashboard, and container deployment.
Outcome
Regressions and collection failures are never confused: a failed check is stored as an error, never as a zero.
Node.jsExpressSQLiteDocker
psi.msschermer.usOpen The Performance Monitor dashboard listing ten monitored sites with mobile and desktop scores, and per-site status of OK, ALERT, or ERROR. Ten sites under scheduled monitoring, with errors separated from low scores.
Inside the build / History, consistency, and constraints

A low score isn’t a failed check.

Explore the difference
58 out of 100Below threshold

A completed check with a lower performance score.

Sample performance historySix illustrative scores: 91, 94, 90, 92, 89, 58. The last score is below the threshold of 70.
Performance scoreThreshold: 70

The score is data. Store it, flag it against the configured threshold, and keep the previous measurements so the change has context.

Read the sample measurements
Illustrative measurements for the selected scenario
CheckScoreResult
191Completed
294Completed
390Completed
492Completed
589Completed
658Completed
Illustrative scenarios · sample scores, not live monitoring data
A closer look

A result needs context.

Compare a healthy run, a genuine score drop, and a failed request. Each should tell a different story.

Keep the measurements

Checks are append-only. The threshold is applied when results are read, so changing what counts as an alert re-reads the history instead of rewriting it.

Show one coherent snapshot

The dashboard uses the latest completed scan. A scan still running never shows fresh mobile results beside yesterday’s desktop ones. Failed checks keep their error.

Fit the infrastructure to the job

SQLite suits modest, periodic writes without a separate database service. Scans are paced to respect the upstream API; the configurable schedule defaults to every six hours.

03Inspection & diagnosisPublished signals → visible conflicts

Meta State Validator

Make a site’s published signals inspectable.

A page, its structured data, and its crawler files routinely tell different stories, and the disagreement is invisible until something downstream breaks. I built an inspector that gathers those sources together and surfaces the contradictions with the evidence attached.

Role
Sole engineer: pipeline, detection, and safeguards.
Built
Inspection pipeline, signal extraction, conflict detection, and safeguards for user-supplied URLs.
Outcome
Conflicts arrive with their sources shown, so the finding is something to investigate rather than a score to argue with.
Node.jsExpresscheerioSSRF hardening
meta-state.msschermer.usOpen A completed Meta State Validator inspection showing HTTP 200, zero redirects, five schema types, zero conflicts, and the extracted page and request signals. A completed inspection, with every observed signal and its source.
Inside the build / Evidence, scope, and safe fetching

The page says two different things.

Example finding
Inspecting example.com/services
HTML / canonicalhttps://example.com/services
Open Graph / og:urlhttps://example.com/
JSON-LD / WebPage.urlhttps://example.com/services

Page identity conflictThe canonical and structured data identify the service page. The social URL identifies the homepage. Inspect the disagreement before deciding what to change.

Illustrative report excerpt · sample domain
A closer look

Evidence before conclusions.

A useful finding shows which sources disagree, so you can trace the problem to its origin.

Show the observed state

The inspector covers page metadata, social tags, structured data, robots directives, sitemap declarations, and llms.txt. Conflicting signals remain visible with their sources.

Define what a result means

This is an on-demand inspection of a URL and its supporting resources. It does not crawl the whole site or turn the result into an SEO score or a promise of search visibility.

Bound the work

The fetch pipeline validates destinations and redirect hops, rejects private targets, pins validated DNS addresses, and caps time and response size. Unavailable resources can be reported independently.

Also in production

Smaller tools, same standards
04 / Launch readiness
preflight.msschermer.usOpen A completed Preflight report for wikipedia.org: seventeen checks passed, seven flagged for review, zero problems, finished in 445 milliseconds.

Preflight

A pre-launch check for certificates, redirects, crawlability, and indexing settings: the details that make a finished page hard to reach or find.

The workflowPublic URL → targeted checks → findings
02 / From build to operation

The infrastructure
is part of the work.

Shipping the application is half of it. I maintain the deployment setup alongside the code: each tool builds its own image, and the infrastructure repository defines how the services run together.

Explore the infrastructure
  1. Version the applicationGitHub
  2. Build & publish the imageActions / GHCR
  3. Run the servicesDocker Compose
  4. Route HTTPS trafficCaddy

Application code and deployment configuration stay separate. Persistent data lives on volumes that survive container replacement.

03 / How I work

Understand the problem.
Own the connection.

Role
Solutions engineer: post-sales, integrations, and implementation
Focus
Platform integrations, internal tooling, technical onboarding
Stack
Node.js, PHP, Express, SQLite, Docker, Caddy, REST APIs
Based in
United States
Status
Open to New Roles

I’m a solutions engineer focused on integrations, implementations, and the internal tooling that keeps them supportable. The interesting part of the job isn’t the connector. It’s understanding the workflow well enough to know what the connector has to guarantee.

That means asking what happens on the bad day before writing the happy path, making failures explainable to whoever is on the other end of the ticket, and leaving behind a system the next engineer can follow without me.

  • Find the boundary that matters.Find what every integration shares, and keep the platform quirks at the edges. That turns the next one into an adapter.
  • Preserve the evidence.A failed request, a changed score, or a conflicting signal should each leave a reason behind. Nobody should have to infer it from a missing number.
  • Build for the next person.The source, the decisions, and a deployment that runs the same way twice all ship with the work.
More about me on LinkedIn
An integration to solve. A team to join.

Your next challenge.
Let’s talk.

Building a team or untangling a complicated integration? I’d love to hear what you’re working on.