Production access is free and self-serve with an account. · Synthetic demo · no account needed

FinchNode

Conformance

Conformance Suite: a scripted run that grades your integration against a synthetic patient.

A scripted run that grades how your sandbox app handles consent, pagination, rate limits, and webhooks.

A conformance run binds your sandbox application, one record scenario, and a dedicated run key. FinchNode then acts as the patient and as an adversary: it completes a Connect session, forces up to two rate limits, revokes consent, and sends webhook probes, observes how your application responds, and grades each check in a report anyone with its link can read. A passing run is recommended before production, not required.

What it is

Built for: Teams that want to test consent handling, pagination, rate-limit retries, and webhook verification end to end before a real patient connects.

Base URL: https://api.finchnode.com/api/v1

Authentication: Send a sandbox key as a Bearer token from your backend: Authorization: Bearer ck_test_... A live key (ck_live_...) receives 403 sandbox_only, and a live application receives 409 app_not_sandbox. The report link needs no key. Keys are hashed at rest, revocable, and must never be embedded in browser or mobile code.

Create a free account to get sandbox keys: https://finchnode.com/signup

At a glance

Status
Available
Auth
Sandbox key on a sandbox application
Contract
OpenAPI 3.1
Authenticated MCP endpoint (API key)
https://api.finchnode.com/api/mcp

Documents for this product

Try it in 30 seconds

Set FINCHNODE_API_KEY once in the shell that runs these commands or starts your agent client (replace ck_test_... with your sandbox key; it is the only key value to substitute)

export FINCHNODE_API_KEY=ck_test_...

Start a conformance run

curl -X POST https://api.finchnode.com/api/v1/conformance/runs -H "Authorization: Bearer $FINCHNODE_API_KEY" -H "Content-Type: application/json" -d '{"scenario":"polypharmacy-senior"}'

Use a sandbox key of an application in sandbox status. scenario is a record scenario: baseline-adult, polypharmacy-senior, pediatric-asthma, multi-source-overlap, sparse-record, messy-coding. Add "sync_mode":"continuous" for an application whose sync mode is continuous to add the incremental-change check; a continuous run on an application whose sync mode is one-time returns 400 invalid_request. One run can be open per application at a time (409 run_in_progress).

Expected response: 201 with a conformance_run: its id (crun_...), status awaiting_session, phase session, the scenario, instructions, each check pending, expires_at, links.report (the public report URL), and run_key, a sandbox key shown only in this response. This command prints the whole response, run_key included: a sandbox credential that never outlives its run, which must stay out of logs and transcripts.

For coding agents

Three ways in for a coding agent: connect the MCP endpoint below, load the OpenAPI document (https://finchnode.com/openapi.yaml), or fetch this page as markdown (https://finchnode.com/products/conformance-suite.md). https://finchnode.com/llms.txt lists every product’s markdown twin.

Authenticated MCP endpoint (API key)

MCP endpoint
https://api.finchnode.com/api/mcp
Transport
Streamable HTTP (JSON-RPC over POST)
Auth
Authorization: Bearer ck_test_... (sandbox keys) or Authorization: Bearer ck_live_... (live keys)
Clients
Server-side and desktop MCP clients only; the endpoint is not CORS-enabled
MCP Registry name
com.finchnode/health-records

Tools at https://api.finchnode.com/api/mcp

  • start_conformance_run
  • get_conformance_run
  • advance_conformance_run

Claude Code

claude mcp add --scope project --transport http finchnode https://api.finchnode.com/api/mcp --header 'Authorization: Bearer ${FINCHNODE_API_KEY}'

Claude Code writes this server to the project's .mcp.json, which is meant to be checked into version control and shared with the project, and expands ${FINCHNODE_API_KEY} when it connects, so the file never holds the key: set FINCHNODE_API_KEY in the shell that runs Claude Code.

Codex CLI

codex mcp add finchnode --url https://api.finchnode.com/api/mcp --bearer-token-env-var FINCHNODE_API_KEY

Cursor (.cursor/mcp.json)

{
  "mcpServers": {
    "finchnode": {
      "url": "https://api.finchnode.com/api/mcp",
      "headers": {
        "Authorization": "Bearer ${env:FINCHNODE_API_KEY}"
      }
    }
  }
}

Machine-readable resources

How it works

  1. Start a run

    Start a run with a sandbox key on an application in sandbox status, naming a record scenario and, optionally, sync_mode. A live application receives 409 app_not_sandbox: an application has one webhook URL and signing secret across environments, and a run sends deliberately corrupted webhook probes, so run conformance on a separate sandbox application. The run key in the response is shown once: an ordinary sandbox key whose requests are attributed to the run, revoked when the run ends. This request prints the whole response, run_key included: a sandbox credential that never outlives its run, which must stay out of logs and transcripts.

    curl -X POST https://api.finchnode.com/api/v1/conformance/runs -H "Authorization: Bearer $FINCHNODE_API_KEY" -H "Content-Type: application/json" -d '{"scenario":"polypharmacy-senior","sync_mode":"one-time"}'
  2. Configure the run key and create a session

    Configure your application’s backend with run_key in place of its usual sandbox key, then let it create a Connect session as it normally does. FinchNode completes the session as the patient with the run’s scenario, as simulate does, and consent.granted is delivered as usual. A run that never gets a session expires 60 minutes after it was created.

    POST /api/v1/connect/sessions
    Authorization: Bearer {run_key}
    Idempotency-Key: {a fresh UUID}
    Content-Type: application/json
  3. Serve traffic through the phases

    When the session’s status is completed, read the subject’s records and follow nextCursor to the end on any category that answers hasMore: true. Keep serving traffic: FinchNode forces up to two rate limits, answering up to two of your next record reads for the subject with a 429 and Retry-After: 3 (the retry of the first can be refused too), advances the records (continuous runs), revokes consent, and sends webhook probes to your application’s webhook URL. Retry each forced 429 no sooner than Retry-After, stop reading after consent.revoked, and reject bad signatures. Send your record requests for the run’s subject one at a time: a duplicate sent alongside a request that receives a forced 429 reads as an early retry. Set the lifecycle and sync webhook URL on the application in the console under Applications, when you create it (New application) or with Edit on its Configuration tab; once a URL is saved, the Webhook signatures panel on that tab reveals and rotates the signing secret.

    HTTP/1.1 429 Too Many Requests
    Retry-After: 3
  4. Poll or advance the run

    RUN_ID is the run’s id (crun_...) from the response that started it. Poll the run for its live phase, each check’s status, and its evidence (counts, timings, and status codes). Advance stops waiting in the current phase: it is graded with what was observed and the run moves on, so advancing early can leave its checks inconclusive. The session phase cannot be advanced, nor can a probes phase while its probes are being sent (409 phase_not_advanceable). Cancel ends a run early. Advancing or cancelling a run that has ended returns 409 run_not_open. Run and session polls are never forced and never graded.

    curl https://api.finchnode.com/api/v1/conformance/runs/$RUN_ID -H "Authorization: Bearer $FINCHNODE_API_KEY"
  5. Read the report

    REPORT_URL is the run’s links.report. It works without a key and answers JSON with Cache-Control: no-store and X-Robots-Tag: noindex. It names your application only by its name and never carries a subject, a session, a key, or a record. verdict is pass when every MUST check passed or was skipped, fail when any failed or was inconclusive, and incomplete for a run that did not finish. Reports are kept for 90 days.

    curl "$REPORT_URL"

What is checked

11 checks
CheckPhaseLevelPasses when
Creates a Connect sessionsessionMUSTThe application created a Connect session with the run key.
Reads the record after completionreadMUSTAfter the session completed, the application read the subject through /users/{subject}/records or a category page.
Follows cursors to the endpaginationMUSTFor at least one category, each page request carried the nextCursor the previous response issued, and the chain ended with hasMore false.
Honors Retry-Afterrate_limitMUSTAfter a forced 429 with Retry-After: 3 on a record operation for the run subject, the application retried that operation no sooner than 3 seconds later.
Reads changes incrementallyupdateSHOULDAfter records.updated, the application called the change feed with a cursor instead of re-reading everything.
Stops reading after revocationrevokeMUSTAfter consent.revoked, the application made at most two further reads for the subject and then stopped.
Acknowledges a genuine eventrevokeMUSTThe application returned 2xx to the genuine consent.revoked delivery.
Accepts a fresh valid probeprobesMUSTThe application returned 2xx to a fresh, validly signed conformance.probe control event.
Rejects a corrupted signatureprobesMUSTThe application returned 4xx to a fresh probe with a corrupted v1 signature and still accepted a following valid control.
Rejects a stale timestampprobesMUSTThe application returned 4xx to a fresh probe whose t value was 3600 seconds old.
Tolerates an exact replayprobesSHOULDThe application returned 2xx or 4xx, never 5xx, to an exact replay of the acknowledged control.

Every report carries this disclaimer: This report describes behavior observed against synthetic data in the FinchNode sandbox environment on the run date. It is not a security assessment, a certification, or a statement about production readiness.

MUST checks decide the verdict; SHOULD checks are reported and never fail a run. A check whose precondition is absent, such as a webhook check for an application with no webhook URL, is skipped with its reason, never failed. Evidence is counts, timings, and status codes, never a record.

A check is inconclusive when the suite could not decide it from what it observed, for example on FinchNode’s side (webhook probes that could not be sent), on your receiver’s (a 5xx, a 429, or a transport error on a probe), or in your application’s traffic (no record request in a phase, page reads sent in parallel, or a phase advanced before its traffic arrived); it names the cause as a reason, counts against a pass, and is never a pass.

The suite looks for honest implementation mistakes, not for a receiver built to pass it: each probe event names its kind in plain text, so a receiver that recognised probes could answer them differently from real events.

Phases
PhaseWaits up toWhat FinchNode does
session60 minutesWaits for the run key’s Connect session and its synthetic patient
read3 minutesWaits for a read of the subject after the session completed
pagination2 minutesWaits for a complete cursor chain (only when a category the session was granted, and the application may read, holds more than one default page of 25 records)
rate_limit2 minutesAnswers up to two of the next record reads for the subject with a forced 429 and Retry-After: 3
update2 minutesAdvances the records (continuous runs only)
revoke2 minutesRevokes consent as the patient; consent.revoked follows
probesabout 1 minute; longer while other runs' probes are being sentSends the webhook probes once (only when the application has a webhook URL)

Reference

The run routes take sandbox keys. The report route takes no key: its token is the report’s only credential, so share the link with care. The MCP tools start_conformance_run, get_conformance_run, and advance_conformance_run start, read, and advance a run, and the last two take run_id; listing and cancelling runs are REST only.

Endpoints
MethodPathOpenAPI operationId
POSThttps://api.finchnode.com/api/v1/conformance/runscreateConformanceRun
GEThttps://api.finchnode.com/api/v1/conformance/runslistConformanceRuns
GEThttps://api.finchnode.com/api/v1/conformance/runs/{runId}getConformanceRun
POSThttps://api.finchnode.com/api/v1/conformance/runs/{runId}/advanceadvanceConformanceRun
POSThttps://api.finchnode.com/api/v1/conformance/runs/{runId}/cancelcancelConformanceRun
GEThttps://api.finchnode.com/conformance/reports/{token}getConformanceReport
MCP tools
ToolEndpoint
start_conformance_runhttps://api.finchnode.com/api/mcp (sandbox keys)
get_conformance_runhttps://api.finchnode.com/api/mcp (sandbox keys)
advance_conformance_runhttps://api.finchnode.com/api/mcp (sandbox keys)

What it does not do

  • It runs only for applications in sandbox status, with sandbox keys: a live key receives 403 sandbox_only, and a live application receives 409 app_not_sandbox. Run conformance on a separate sandbox application.
  • It sends webhook probes only to your application’s webhook URL, only during a run you started. Each is sent once and never retried; one carries a deliberately corrupted signature and one a timestamp 3600 seconds old.
  • It grades behavior against synthetic data in the sandbox environment, and its report says nothing about production readiness. A passing run is recommended before production, not required, and it unlocks nothing: production activation stays self-serve.
  • One run can be open per application at a time (409 run_in_progress).

Frequently asked questions

What does inconclusive mean?

The suite could not decide the check from what it observed. The cause can be on FinchNode’s side, such as webhook probes that could not be sent, or on yours, such as a 5xx or a transport error on a probe, or a phase advanced before its traffic arrived. The check names its cause as a reason and is never a pass. An inconclusive MUST check counts against a pass; an inconclusive SHOULD check does not.

Why does a receiver that only deduplicates event ids fail the signature checks?

The corrupted-signature and stale-timestamp probes are fresh events with never-seen event ids and bodies, so deduplication alone cannot reject them. webhook.reject.invalid_signature passes only when your endpoint answers 4xx to a fresh probe with a corrupted v1 signature and still accepts the valid control that follows, and webhook.reject.stale_timestamp only when it answers 4xx to a fresh probe whose t is 3600 seconds old. Read t and v1 from FinchNode-Signature, compute HMAC-SHA256 with your webhook secret over the timestamp, a period, and the exact raw body, compare in constant time, and reject a timestamp more than 300 seconds from now.

Does a passing run unlock anything?

No. A passing run is recommended before production, not required, and it changes nothing about your application: production activation stays self-serve. The report describes behavior observed against synthetic data in the sandbox environment and says nothing about production readiness.

Which scenarios can a run use?

A record scenario: baseline-adult, polypharmacy-senior, pediatric-asthma, multi-source-overlap, sparse-record, messy-coding. A behavior or session scenario returns 400 invalid_scenario. With sync_mode continuous, which needs an application whose sync mode is continuous, the run adds the incremental-change check.

Can a coding agent drive a run?

Yes, through the authenticated MCP endpoint with a sandbox key: start_conformance_run starts a run and returns its run key once, get_conformance_run reads it, and advance_conformance_run stops waiting in the current phase; the last two take run_id. The agent’s own traffic must then switch to the run key: create the Connect session and make the record reads with run_key as the bearer (for example, through a second MCP server entry whose FINCHNODE_API_KEY is the run key). Otherwise the run never sees the session and expires 60 minutes after it was created. Listing and cancelling runs are REST only.