Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.1.0
info:
title: R.E. Merrill Pump Line Finder API
version: 1.0.0
summary: Deterministic pump line selection from R.E. Merrill & Associates, over plain HTTP and over A2A JSON-RPC.
description: >-
R.E. Merrill & Associates, Inc. (Texas industrial pump distributor and manufacturers' representative,
founded 1987) publishes one machine-callable capability: given a pumping duty (fluid, concentration,
temperature, flow, pressure or head, service type) the line finder returns which of the represented
pump lines fit and why, citing the manufacturer catalog page for the smallest covering model, plus a
prefilled request-for-quote link. Verdicts come only from curated tables of manufacturer-published
flow and pressure data (curated today: Grundfos dosing pumps and Continental progressing cavity pumps;
the other eleven represented lines are "consult-REM" by design). Fluid, concentration and temperature
are never resolved by the endpoint: chemical compatibility is a safety determination confirmed by an
R.E. Merrill engineer before quoting. No pricing is returned; quotes come from a human engineer.
The same capability is exposed twice on one host: as POST /api/line-finder (plain JSON) and as an
Agent2Agent (A2A) v1.0 JSON-RPC 2.0 endpoint at POST /api/a2a (method SendMessage), whose agent card is
served at https://www.remerrill.com/.well-known/agent-card.json and signed (JWS ES256, keys at
/.well-known/jwks.json). Both are unauthenticated and stateless.
PROVENANCE. R.E. Merrill publishes no OpenAPI of its own. This document was GENERATED by API Evangelist
on 2026-09-19 from the provider's own published descriptions of the endpoint — the "## API" section of
https://www.remerrill.com/llms.txt (request field list and two curl examples), the agent card's skill
descriptions, and the agent's own reply to a natural-language message, which states the request schema
verbatim ("fluid (string, required), concentrationPct?, temperatureF?, flowGpm? or flowGph? (exactly
one), pressurePsi? or headFt? (exactly one), service? (transfer | feed | sump | slurry | boiler)") —
and from live, unauthenticated calls made the same day with the provider's documented example bodies.
Response schemas describe the observed 200, 400 and 405 bodies. Nothing here was invented; where the
provider states a constraint only in prose it is carried in prose here too.
contact:
name: R.E. Merrill & Associates, Inc.
email: info@remerrill.com
url: https://www.remerrill.com/contact
x-apievangelist-provenance:
method: generated
generated: '2026-09-19'
generated_from:
- https://www.remerrill.com/llms.txt
- https://www.remerrill.com/.well-known/agent-card.json
- 'POST https://www.remerrill.com/api/line-finder (documented example; HTTP 200 observed 2026-09-19)'
- 'POST https://www.remerrill.com/api/line-finder with an invalid body (HTTP 400 observed 2026-09-19)'
- 'GET https://www.remerrill.com/api/line-finder (HTTP 405 observed 2026-09-19); OPTIONS returned Allow: OPTIONS, POST'
- 'POST https://www.remerrill.com/api/a2a SendMessage (documented example; HTTP 200 observed 2026-09-19)'
- 'POST https://www.remerrill.com/api/a2a GetTask with an unknown id (JSON-RPC error -32001 observed 2026-09-19)'
provider_publishes_openapi: false
note: >-
A generated contract, not a harvested one. Probed and absent on 2026-09-19: /openapi.json,
/openapi.yaml, /swagger.json, /api/openapi.json, /api-docs, /docs (all real 404s).
externalDocs:
description: Provider documentation (llms.txt) — the only published reference for this API
url: https://www.remerrill.com/llms.txt
servers:
- url: https://www.remerrill.com
description: Production. The apex remerrill.com 308-redirects every path to www.
tags:
- name: Line Finder
description: Deterministic pump line selection from curated, manufacturer-published performance data.
- name: A2A
description: The same line finder over the Agent2Agent protocol v1.0 (JSON-RPC 2.0).
paths:
/api/line-finder:
post:
tags: [Line Finder]
operationId: findPumpLines
summary: Find which represented pump lines fit a duty
description: >-
Send a duty as JSON. Returns the represented lines whose curated model envelopes cover the stated flow
and pressure, with per-family matching-model counts, a rationale naming the smallest covering model and
its printed catalog source page, the items the endpoint deliberately does not resolve (fluid,
concentration, temperature, service type), the curated-vs-consult-REM coverage of the represented
lines, and an rfqUrl that prefills the contact form with the duty. Catalog performance is stated for
water; R.E. Merrill confirms the selection for the actual fluid before quoting. Unauthenticated;
stateless; GET returns 405 (Allow: OPTIONS, POST).
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Duty'
examples:
hypochlorite-feed:
summary: The provider's documented example
value: {fluid: sodium hypochlorite, concentrationPct: 12.5, flowGph: 2, pressurePsi: 40, service: feed}
sludge-transfer:
summary: Progressing-cavity duty (from the agent card)
value: {fluid: municipal sludge, flowGpm: 100, pressurePsi: 50, service: transfer}
responses:
'200':
description: Line-finder verdict. An empty matches[] is still a 200 — the duty simply falls outside every curated envelope and the response routes to consult-REM.
content:
application/json:
schema:
$ref: '#/components/schemas/LineFinderResult'
examples:
hypochlorite-feed:
summary: Observed response to the documented example (2026-09-19), family lists abbreviated
value:
matches:
- lineSlug: grundfos
lineName: Grundfos
families:
- {id: dda, name: DDA SMART Digital, matchingModels: 3}
- {id: dmh-280, name: DMH 280 series, matchingModels: 22}
rationale: '2 US gph at 40 psi falls inside 100 cataloged model envelopes across 9 families of the Grundfos line. Smallest covering model: DMH 281 6,4-100 (2 US gph at 1450 psi), DMH 280 series family, per Dosing All Product Brochure, printed page 25. Catalog performance is stated for water. REM confirms the selection for the actual fluid before quoting.'
unresolved:
- 'Fluid, concentration, and temperature are never resolved by this endpoint. Chemical compatibility is a safety determination: an REM engineer confirms materials for sodium hypochlorite, 12.5 percent against curated compatibility data and factory guidance. Send the duty through rfqUrl.'
- 'Service type "feed" is not resolved by the curated tables, which cover flow and pressure envelopes only. REM confirms fit for the service at quoting.'
coverage:
curated:
- {lineSlug: grundfos, lineName: Grundfos}
- {lineSlug: continental-ultrapump, lineName: Continental Progressing Cavity UltraPump}
consultRem: [Vanton Pump & Equipment Corporation, DP Pumps USA, Pinnacle-Flo, Inc., Vertiflo, Simtech Process Systems, TF Seals, Garbarino Pumps Asia Pte Ltd, GreenPumps, Pemo Pumps, Victor Pumps, Walrus Pump]
note: 'Curated selector tables, built only from manufacturer-published catalogs with per-row source citations, exist for the lines under curated. The remaining represented lines are consult-REM by design: send the duty through rfqUrl and an engineer responds.'
rfqUrl: '/contact?duty=Fluid%3A%20sodium%20hypochlorite%2C%2012.5%20percent.%20Flow%3A%202%20gph.%20Discharge%20pressure%3A%2040%20psi.%20Service%3A%20feed.'
'400':
description: The body failed validation. The reason string concatenates the failing fields; rfqUrl still points at the human path.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
invalid:
summary: Observed 2026-09-19 for {"flowGph":"x"}
value: {error: Invalid request, reason: 'fluid: Invalid input flowGph: Invalid input', rfqUrl: /contact}
'405':
description: Method not allowed — only POST (and OPTIONS) is served on this path. Observed on GET with an empty body.
/api/a2a:
post:
tags: [A2A]
operationId: sendA2AMessage
summary: A2A v1.0 JSON-RPC endpoint (SendMessage) for the same line finder
description: >-
Agent2Agent protocol v1.0 over JSON-RPC 2.0. Send the duty JSON as a message part (a data part, or the
same JSON as text) with method SendMessage; the provider's example sets an A2A-Version: 1.0 header. The
agent is synchronous and stateless: a resolvable duty returns a completed task whose artifact
(name line-finder-result) carries exactly the POST /api/line-finder response plus a text summary;
a non-JSON natural-language message returns an agent message that restates the expected duty schema
rather than guessing. Tasks are not retained after SendMessage, so GetTask answers JSON-RPC error
-32001 Task not found (google.rpc.ErrorInfo reason TASK_NOT_FOUND) for any id; unknown methods return
-32601 Method not found. No streaming, no push notifications, no authentication. GET returns 405 with a
JSON pointer to the agent card. Discovery: https://www.remerrill.com/.well-known/agent-card.json.
parameters:
- name: A2A-Version
in: header
required: false
schema: {type: string, examples: ['1.0']}
description: Protocol version header the provider's own example sends.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/JsonRpcRequest'
examples:
send-duty:
summary: The provider's documented SendMessage example
value:
jsonrpc: '2.0'
id: 1
method: SendMessage
params:
message:
messageId: m1
role: ROLE_USER
parts:
- data: {fluid: sodium hypochlorite, concentrationPct: 12.5, flowGph: 2, pressurePsi: 40, service: feed}
responses:
'200':
description: A JSON-RPC 2.0 envelope — either a result (task with a line-finder-result artifact, or an agent message) or an error object. JSON-RPC errors are returned with HTTP 200.
content:
application/json:
schema:
$ref: '#/components/schemas/JsonRpcResponse'
examples:
task-not-found:
summary: Observed 2026-09-19 for GetTask with an unknown id
value:
jsonrpc: '2.0'
id: 3
error:
code: -32001
message: Task not found
data:
- '@type': type.googleapis.com/google.rpc.ErrorInfo
reason: TASK_NOT_FOUND
domain: remerrill.com
metadata: {taskId: apievangelist-nonexistent-probe, note: Tasks on this agent complete synchronously inside SendMessage and are not retained afterward.}
method-not-found:
summary: Observed 2026-09-19 for an unknown method
value: {jsonrpc: '2.0', id: 2, error: {code: -32601, message: Method not found}}
'405':
description: GET is not served. Body is a JSON object with error, reason and agentCard (the discovery URL).
content:
application/json:
schema:
type: object
properties:
error: {type: string}
reason: {type: string}
agentCard: {type: string, format: uri}
example: {error: Method not allowed, reason: 'This is the A2A v1.0 JSON-RPC endpoint; POST a JSON-RPC 2.0 request. Agent discovery starts at the agent card.', agentCard: 'https://www.remerrill.com/.well-known/agent-card.json'}
components:
schemas:
Duty:
type: object
description: >-
A pumping duty as the provider defines it. Exactly one of flowGpm / flowGph and exactly one of
pressurePsi / headFt (stated by the provider in prose; not expressible as a plain required list).
Units are US customary. Fluid, concentration and temperature are accepted but never resolved by the
endpoint — they are passed through to the engineer via rfqUrl.
required: [fluid]
properties:
fluid:
type: string
description: The pumped liquid, e.g. "sodium hypochlorite", "municipal sludge". Required; never resolved by the endpoint.
concentrationPct:
type: number
description: Concentration, percent. Passed through, not resolved.
temperatureF:
type: number
description: Temperature, degrees Fahrenheit. Passed through, not resolved.
flowGpm:
type: number
description: Flow in US gallons per minute. Use this OR flowGph, not both.
flowGph:
type: number
description: Flow in US gallons per hour (the native unit of the Grundfos dosing catalog). Use this OR flowGpm.
pressurePsi:
type: number
description: Discharge pressure, psi. Use this OR headFt, not both.
headFt:
type: number
description: Head, feet. Use this OR pressurePsi.
service:
type: string
enum: [transfer, feed, sump, slurry, boiler]
description: Service type. Recorded and passed to the engineer; the curated tables cover flow and pressure envelopes only, so service is reported under unresolved.
additionalProperties: true
PumpFamily:
type: object
properties:
id: {type: string, description: Family slug, e.g. dda, dmh-280, ck2000.}
name: {type: string, description: Family name as printed in the manufacturer catalog.}
matchingModels: {type: integer, description: Number of curated model envelopes in this family that cover the duty.}
LineMatch:
type: object
properties:
lineSlug: {type: string, description: Represented line slug; also the path segment under https://www.remerrill.com/lines/.}
lineName: {type: string}
families:
type: array
items: {$ref: '#/components/schemas/PumpFamily'}
rationale:
type: string
description: Human-readable verdict naming the smallest covering model, its family, the source catalog and printed page, and the water-basis caveat.
LineRef:
type: object
properties:
lineSlug: {type: string}
lineName: {type: string}
Coverage:
type: object
properties:
curated:
type: array
description: Lines for which curated selector tables exist (Grundfos, Continental UltraPump as of 2026-09-19).
items: {$ref: '#/components/schemas/LineRef'}
consultRem:
type: array
description: Represented lines with no curated table; the duty goes to an engineer via rfqUrl.
items: {type: string}
note: {type: string}
LineFinderResult:
type: object
properties:
matches:
type: array
items: {$ref: '#/components/schemas/LineMatch'}
unresolved:
type: array
description: What the endpoint deliberately did not decide (fluid compatibility, concentration, temperature, service) and why.
items: {type: string}
coverage: {$ref: '#/components/schemas/Coverage'}
rfqUrl:
type: string
description: Site-relative URL (resolve against https://www.remerrill.com) that prefills the contact form with the duty for a human quotation.
ErrorResponse:
type: object
description: Plain JSON error object (not RFC 9457). Observed on 400.
properties:
error: {type: string, description: Short failure name, e.g. "Invalid request".}
reason: {type: string, description: Field-level detail concatenated into one string.}
rfqUrl: {type: string, description: Always present; the human fallback.}
JsonRpcRequest:
type: object
required: [jsonrpc, id, method]
properties:
jsonrpc: {type: string, const: '2.0'}
id: {oneOf: [{type: integer}, {type: string}]}
method:
type: string
description: A2A v1.0 method name. SendMessage is the implemented operation; GetTask answers -32001 for every id (tasks are not retained); anything else answers -32601.
examples: [SendMessage, GetTask]
params:
type: object
description: >-
For SendMessage: {message: {messageId, role: ROLE_USER, parts: [{data: <Duty>} | {text: "<Duty as JSON>"}]}}.
JsonRpcResponse:
type: object
required: [jsonrpc, id]
properties:
jsonrpc: {type: string, const: '2.0'}
id: {oneOf: [{type: integer}, {type: string}]}
result:
type: object
description: >-
On a resolvable duty: {task: {id, contextId, status: {state: TASK_STATE_COMPLETED, timestamp},
artifacts: [{artifactId, name: line-finder-result, description, parts: [{data: <LineFinderResult>},
{text: <summary>}]}]}}. On a non-JSON message: {message: {messageId, role: ROLE_AGENT, parts:
[{text, mediaType: text/plain}]}} restating the expected duty schema.
error:
type: object
properties:
code:
type: integer
description: '-32001 Task not found (A2A TaskNotFoundError); -32601 Method not found.'
message: {type: string}
data:
type: array
items:
type: object
description: google.rpc.ErrorInfo-shaped detail (@type, reason, domain, metadata).