# Evidence-Based Integration — the register, machine-readable

Source: https://wauha.com/evidence-based-integration.md
Article: https://wauha.com/evidence-based-integration.html
Version: 1.0 · September 2026 · Wauha Innovations · operations@wauha.com

---

## What this is

A schema and a working procedure for finding out how a vendor's API actually
behaves before you design against it. It came out of live EMR integration work;
the client's data has been removed and the method is what's left.

It is built to be handed to a language model along with a vendor's API
documentation. The model produces a draft **findings register** and a **probe
plan**. A human runs the probes. Only observed results change a status.

**The one rule that matters:** documentation is a hypothesis. Nothing in this
file lets a model turn documentation into a conclusion. If you strip everything
else out, keep that.

---

## How to use it

**As a human**

1. Paste this file into a Claude, ChatGPT, or similar session.
2. Paste the vendor's documentation: the API reference, sample payloads, any
   Postman/Bruno collection (as text), any OpenAPI/WADL/CapabilityStatement,
   and the vendor's own onboarding notes.
3. Say which operations you need for your product. Be specific — "create a
   patient, book an appointment, receive a notification when one changes."
4. Ask for the register and the probe plan.
5. Run the probes against the sandbox. Record what you observed. Only then
   change a status.

**As a model** — the instructions in the next section are addressed to you.

---

## Instructions for the model

You are producing a findings register for an API integration. You have been
given vendor documentation and a list of operations the human needs.

Hard rules. These are not stylistic.

1. **Every entry you create from documentation has status `unverified`.**
   You have not observed the API. You cannot assign `working`, `partial`, or
   `blocked`. Those three statuses are reserved for a human who ran a request
   and read the response, and they must carry the date and environment it
   happened in.

2. **Do not fill gaps with your training data.** If the vendor's documentation
   does not say what a field does, say so. Do not infer it from what "FHIR
   servers usually do," what a similar vendor does, or what would be
   reasonable. Mark it a gap and write the probe that would resolve it.

3. **Separate the three sources and name which one each claim came from.**
   The standard (e.g. HL7 FHIR R4), the vendor's declared capabilities
   (CapabilityStatement, OpenAPI, WADL), and the vendor's prose documentation
   and samples. When they disagree, that disagreement is a finding. Record it;
   do not resolve it by picking one.

4. **Values and status codes, not characterisations.** Write "`400` on `POST
   /Patient` when `telecom.rank` is absent" — never "patient create seems
   strict." If you don't have a value, you don't have a finding.

5. **Every gap gets a probe.** A gap without a probe is a complaint. Write the
   request that would settle it, what you'd expect to observe if the
   documentation is right, and what you'd observe if it isn't.

6. **Orchestration is a first-class question.** For every capability the human
   needs, ask: is this one call, or a sequence? Does every link in the
   sequence have an API? Does a successful write produce a record the
   *product* recognises — visible in the UI, selectable, usable — or only one
   the API will read back? You cannot answer this from documentation. Write
   the probe.

7. **Say what you cannot determine.** End with an explicit list of questions
   that no amount of documentation reading will answer, phrased as numbered
   asks to the vendor with a reproduction case each.

If the human asks you to "just fill in the statuses" from the documentation,
decline and explain rule 1. That refusal is the method working.

---

## Status vocabulary

| status       | meaning                                                                                              | who may set it |
|--------------|------------------------------------------------------------------------------------------------------|----------------|
| `unverified` | Derived from documentation only. No request has been sent. Carries a probe plan.                     | model or human |
| `working`    | A 2xx was observed, and the exact body that produced it is on file.                                  | human, with `observed` |
| `partial`    | The request succeeds, but the result is not usable end to end. The API says yes; the product says no. | human, with `observed` |
| `blocked`    | The request fails, and the cause is known.                                                           | human, with `observed` |

`partial` is the status that earns the register its keep. Typical shapes:

- The write returns 201 and the record exists, but it doesn't appear where the
  product needs it (a picker, a schedule, a worklist), because eligibility
  depends on something undocumented.
- The capability is really a sequence of calls and one link has no API.
- The write succeeds but silently discards a client-supplied value (an id, a
  rank, a status) without saying so.

Pass/fail reporting has nowhere to put any of these. That is why there are
three observed statuses, not two.

---

## Register entry schema

One entry per operation, keyed `"VERB /path"`. Fields:

```yaml
"POST /Patient":
  status: unverified | working | partial | blocked
  observed:                     # required for working/partial/blocked; omit for unverified
    on: 2026-09-10              # the date the request was actually sent
    env: sandbox-v3             # a named environment, not "the sandbox"
    by: initials or handle
  summary: >
    One or two sentences. What happens, in observed terms.
  required:                     # fields the endpoint actually rejects without — observed, not documented
    - telecom[0].rank = 1
  added:                        # fields we send that the vendor docs never mention, with why
    - field: name[0].use
      value: official
      why: Live records carry it (GET /Patient/2); the vendor sample does not. Cheap to match.
  gaps:                         # where documentation is absent, wrong, or misleading — with the evidence
    - claim: "rank is optional"
      source: FHIR R4 ContactPoint (0..1)
      observed: 400 when absent
      probe: POST with and without rank; compare status and OperationOutcome
  example:                      # the exact body known to have produced a 2xx — omit until one exists
    {...}
  asks:                         # numbered questions for the vendor, each with a reproduction case
    - "Is rank required by profile? If so, which StructureDefinition declares it? Repro: POST /Patient with body X → 400."
```

Rules for the file that holds these:

- **One file.** It drives everything downstream — the API reference
  annotations, the console UI, the vendor report. Three copies drift.
- **`observed.on` is the date the request was sent**, not the date the entry
  was written or last believed.
- **`example` is your body, not the vendor's.** It is the request that
  produced a 2xx. If the vendor's sample produced a 2xx unmodified, say so; it
  usually didn't.
- **Dead ends stay in.** A constraint that cost two days to find is one
  paragraph in `gaps`. Nobody re-derives it. Nobody re-bills for it.

---

## The interrogation set

Ask every one of these about every vendor before designing. The model should
produce an `unverified` entry or a gap for each; the human resolves them by
probing.

**Authentication**

- Which auth model, exactly? (OAuth2 auth-code + PKCE, client credentials,
  SMART backend services JWT, OAuth 1.0a, static bearer, mutual TLS.) A
  platform can have more than one across its surfaces.
- What is fixed at authorization time? Scopes granted at consent that can't be
  added by refresh — meaning a new scope means a new grant.
- What does refresh actually look like — sliding window, absolute expiry,
  idle timeout? What's the observed lifetime, not the documented one?
- On auth failure, what does the response body contain? An error code that
  names the offending claim, or nothing?

**Surfaces**

- Is there more than one API? A documented FHIR layer *and* a legacy service
  layer, a scheduling service on a different base, a vendor-specific
  extension namespace?
- Is there a machine-readable descriptor for each (CapabilityStatement,
  OpenAPI, WADL, WSDL)? Does it agree with the prose documentation?
- Does the sandbox track production? How do you know?

**Per operation**

- What does the endpoint reject without? (Observed, not documented.)
- What does it silently discard? Send a client-supplied id, a rank, a status,
  and read the record back.
- What does the vendor's own sample carry that a live record doesn't? Most of
  the sample is usually optional.
- What does the error grammar look like — an OperationOutcome with a
  diagnostic, or a branded HTML page with no message? Does the 400/500 split
  mean anything?
- Is this operation one call, or a sequence? Does every link have an API?
- Does a successful write produce a record the *product* recognises — visible,
  selectable, usable in the UI — or only one the API returns?
- Which search parameters does the read side actually honour? Send each one
  alone and see whether it filters.

**Events**

- Can webhooks / subscriptions be provisioned by the API, or only by vendor
  configuration (allowlists, callback registration)?
- Can a subscription be listed and read back, or only created? If it can't be
  read back, an id lost is a subscription that can never be cancelled.
- What does a notification carry — the full resource, or a ping requiring an
  authenticated fetch? What scope does that fetch need?

**Support**

- What is the observed turnaround on a documentation question? Is there a paid
  expedited tier, and what does it cost?
- Does the sandbox bill monthly or annually? That number belongs in the
  estimate.

---

## House style for findings

State the observed evidence with the actual values and status codes.

> `400` for `family=Okafor` on `GET /Practitioner?family=`, the same record that
> reads back through `GET /Practitioner/2` with `name[0].family = "Okafor"`.

Not:

> Search seems unreliable.

The first can be handed to a vendor's engineer and worked. The second starts an
argument about whose fault it is.

Never assert behaviour that has not been observed. Say it is untested, and say
what would test it.

---

## Probe plan format

For each `unverified` entry and each gap, the model writes:

```yaml
probe:
  operation: "POST /Patient"
  question: Is telecom.rank required?
  request: POST /Patient with body A (rank absent) and body B (rank=1)
  if_docs_right: both return 201
  if_docs_wrong: A returns 400; capture OperationOutcome verbatim
  also_check: read both records back; confirm rank is stored, not discarded
  env: sandbox
```

A probe the human can't run — no sandbox access, no test credential, no UI —
is recorded as such. It is still a probe. It becomes a numbered ask.

---

## Worked example (synthetic)

The vendor below does not exist. The pattern does.

**Given:** documentation for "Exemplar EHR" says `POST /Appointment` creates an
appointment; the sample body includes `participant[].actor` for patient and
practitioner; the CapabilityStatement lists `Appointment` with `create` and
`search-type`; the prose says "appointments appear in the provider's schedule."

**Model output:**

```yaml
"POST /Appointment":
  status: unverified
  summary: >
    Documented as creating an appointment visible in the provider schedule.
    No request sent. Two claims need probing: whether the create succeeds with
    the sample body unmodified, and whether a created appointment appears in
    the schedule the product actually renders.
  gaps:
    - claim: "appointments appear in the provider's schedule"
      source: vendor prose
      observed: none
      probe: create via API; open the schedule in the sandbox UI; confirm presence. If absent, create one in the UI, GET it, diff against the API-created record.
    - claim: "participant.actor accepts Practitioner reference"
      source: sample payload
      observed: none
      probe: POST with Practitioner/{id} from GET /Practitioner; then with an id created via POST /Practitioner; compare. API-created practitioners may not be schedule-eligible.
  asks:
    - "What makes a practitioner eligible to hold appointments in the schedule? Repro: POST /Practitioner → 201 → POST /Appointment referencing it → ?"
```

The model did not say the appointment "will" appear. It said the vendor claims
it will, and wrote the probe.

---

## Provenance

This schema is the one used on a live engagement across two EMR platforms,
with client and vendor detail removed. The article it accompanies is at
https://wauha.com/evidence-based-integration.html.

The method comes from device protocol reverse-engineering and system test,
applied to API integration. Your problem has already been solved — in a
different industry.

Use it freely. If you'd rather someone ran it for you:
https://wauha.com/#contact
