How to Run an On-Demand Endpoint Diagnostic from a Synchronous Webhook

Build a Spojit workflow that accepts a target URL over a signed webhook, fans out three concurrent probes (a full GET, a fast HEAD latency check, and a DNS-style REST lookup), assembles one report with custom code, and returns it inline to the caller.

What This Integration Does

When something looks wrong with an external endpoint, you often want a single command that says "check this URL right now and tell me everything." This workflow gives your team an HTTP endpoint they can POST a target URL to and get back a complete diagnostic in the same request: the status code and body, the response-time headers, and a public DNS resolution of the host. Because it runs on demand and answers synchronously, you can wire it into a runbook, a Slack slash command relay, an internal status page, or a quick curl from a terminal, without polling or waiting for an email.

The workflow starts on a Webhook trigger verified with a Custom HMAC signing connection, so only signers who hold the shared secret can run it. The parsed JSON body (the target URL you sent) flows into a Parallel node that launches three Connector probes at once on the http connector. A Transform node running execute-javascript merges those three results into one structured report, and a Response node returns that report as the HTTP response to the original caller. The workflow stores nothing and holds no state between runs: each call is independent, idempotent against the target, and safe to re-run as often as you like.

Prerequisites

  • A Spojit workspace where you can create workflows in the Workflow Designer.
  • A Webhook signing connection using the Custom HMAC scheme. Create it under Connections -> Add connection and note the shared secret, header name, and signature format so your caller can sign requests.
  • The http connector (built in, no authentication needed) and the code connector (built in) available in your workspace.
  • A DNS-over-HTTPS resolver URL for the lookup probe, for example https://dns.google/resolve, which returns JSON for a given hostname.
  • The host name or full URL you want a caller to be able to diagnose, sent in the request body at run time.

Step 1: Add the Webhook trigger with Custom HMAC verification

Create a new workflow and add a Trigger node, then set Trigger Type to Webhook. In the trigger configuration, select your Custom HMAC signing connection so every inbound request is verified before the workflow runs. Copy the generated webhook URL: this is what your callers will POST to. The trigger parses the request body as JSON and exposes it as {{ input }}. Design your callers to send a small payload like this:

{
  "target": "https://api.example.com/health"
}

At run time the target is available as {{ input.target }}. A verified call returns 202 with an executionId immediately, but because this workflow ends in a Response node, the caller will instead receive your diagnostic report inline (covered in Step 6).

Step 2: Normalize the target URL with a Transform node

Add a Transform node right after the trigger so every probe works from clean values. Use execute-javascript on the code connector to derive both the full URL and the bare hostname from {{ input.target }}. This guards against callers who send a host without a scheme and gives the DNS probe a clean hostname:

const raw = input.target.trim();
const url = raw.startsWith("http") ? raw : "https://" + raw;
const host = new URL(url).hostname;
return { url, host };

Name the output variable parsed so downstream nodes can reference {{ parsed.url }} and {{ parsed.host }}.

Step 3: Fan out three probes with a Parallel node

Add a Parallel node after the Transform. The Parallel node runs its branches concurrently, so all three checks finish in roughly the time of the slowest one rather than the sum of all three. Create three branches and place one Connector node (Direct mode) in each:

  • Branch A - full GET: use http-get on the http connector with url set to {{ parsed.url }}. Set a timeout (for example 10000 ms) so a hung endpoint cannot stall the whole run. This returns { data, status, statusText, headers }; name the output getResult.
  • Branch B - latency probe: use http-head on the http connector with url set to {{ parsed.url }} and a short timeout (for example 5000 ms). HEAD fetches headers without a body, so it is the cheapest way to confirm the endpoint is up and read server-reported timing or cache headers. This returns { status, statusText, headers }; name the output headResult.
  • Branch C - DNS-style lookup: use http-get on the http connector against your DNS-over-HTTPS resolver, with url set to https://dns.google/resolve?name={{ parsed.host }}&type=A and the headers field carrying accept: application/dns-json. Name the output dnsResult. This resolves the host to its A records through a public REST API, reached via the http connector exactly as described in the guide on calling any REST endpoint.

Because each branch uses Direct mode, this fan-out is deterministic and costs no AI credits.

Step 4: Capture per-probe timing

The http tools return status and headers but not a measured round-trip time, so add a lightweight measurement of your own where it matters. The simplest accurate approach is to wrap the latency-sensitive probe in a small timing pair: add a Transform node using execute-javascript at the start of Branch B that records a start timestamp, and read the elapsed time after the probe. For example, capture the start with the date connector's now tool (output variable startedAt) before http-head, then compute the delta in Step 5. If server-side timing headers such as server-timing or x-response-time are present, read them straight out of {{ headResult.headers }} instead. Keep this branch focused on HEAD so the timing reflects connection and first-byte latency, not full body download.

Step 5: Assemble the report with a Transform node

After the Parallel node closes, add a Transform node that uses execute-javascript on the code connector to merge the three branch outputs into one clean diagnostic object. Reference each probe by its output variable. A robust assembly looks like this:

const dns = dnsResult.data || {};
const records = (dns.Answer || [])
  .filter(a => a.type === 1)
  .map(a => a.data);

return {
  target: parsed.url,
  host: parsed.host,
  checkedAt: new Date().toISOString(),
  http: {
    status: getResult.status,
    statusText: getResult.statusText,
    reachable: getResult.status >= 200 && getResult.status < 400,
    contentType: getResult.headers["content-type"] || null
  },
  latency: {
    status: headResult.status,
    cacheControl: headResult.headers["cache-control"] || null,
    serverTiming: headResult.headers["server-timing"] || null
  },
  dns: {
    resolved: records.length > 0,
    addresses: records
  }
};

Name the output variable report. Keep the merge logic defensive: a target that times out may leave a probe with an error status, so guard with fallbacks like || null and || [] so the report never throws.

Step 6: Return the report inline with a Response node

Add a Response node as the final step and set its body to {{ report }}. The Response node returns this value to the synchronous webhook caller, so instead of the default 202 acknowledgement, the original POST receives your full diagnostic JSON in the same HTTP response. A caller can now run one command and read back the status, latency signals, and resolved addresses for the target. If you want different HTTP status codes for healthy versus unhealthy targets, branch with a Condition node on {{ report.http.reachable }} before the Response node and return a distinct body on each path.

Tips

  • Set a sensible timeout on every http probe. A synchronous workflow can only respond as fast as its slowest branch, and the Parallel node waits for all branches, so a missing timeout on one probe stalls the whole response.
  • Ask Miraxa, the intelligent layer across your automation, to scaffold the fan-out for you: "Add a Parallel node with three branches, each a Connector node on the http connector, then a Transform node that merges their outputs into one report." Then fine-tune fields in the properties panel.
  • Read response headers case-insensitively where you can. Many servers lowercase header names, so prefer keys like content-type and cache-control in your merge code.
  • Keep the returned report small and structured. A compact JSON body is easier for runbooks and dashboards to consume than a dump of full response bodies.

Common Pitfalls

  • Forgetting the Response node. Without it the caller only gets the 202 acknowledgement and never sees the diagnostic. The Response node must be the terminating step on every path you want answered.
  • Sending a bare host with no scheme. The DNS lookup needs a hostname and the GET needs a full URL, which is exactly why Step 2 normalizes both up front. Skipping it makes new URL() throw.
  • Mismatched HMAC signing. If the caller's signature header, secret, or format do not match your Custom signing connection, the request is rejected before the workflow runs. Verify the scheme details when you create the connection.
  • Treating a non-2xx GET as a hard failure. A 404 or 503 is still useful diagnostic data, so capture status in the report rather than letting the probe halt the run.

Testing

Validate on a known-good endpoint first. Point the workflow at a stable public URL and POST a signed request with { "target": "https://example.com" } using curl or any HTTP client that can produce the HMAC signature your Custom connection expects. Confirm the response body contains a populated http, latency, and dns section, then open the execution in your run history to see each Parallel branch output. Next, test a deliberately broken target (a non-existent host or a URL that returns 500) and confirm the report still returns cleanly with reachable: false and an empty addresses array, proving your defensive merge code holds. Only after both cases pass should you share the webhook URL with your team.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.