All Products

Ontario health card validation in one HTTP request

Instead of a SOAP integration and a conformance project. Send a health number over HTTPS; get the ministry's eligibility answer back as JSON — typically in under a second — with the official response code and a plain valid / eligible verdict your code can branch on.

HTTPS · JSON in, JSON out · no SDK required · data stays in Canada

terminal
curl --request POST 'https://validate2.drtools.ca/v1/validate' \
  --header 'Content-Type: application/json' \
  --header 'Api-Key: YOUR_API_KEY' \
  --data '{
    "MOH_EBS_USERNAME": "your-moh-username",
    "MOH_EBS_PASSWORD": "your-moh-password",
    "provider_number":  "123456",
    "health_number":    "1234567890",
    "version_code":     "AB"
  }'

The ministry validates health cards. Using its channels is the hard part.

The IVR phone line

A human dials a number and keys in digits, one patient at a time. It doesn't scale past a few cards a day, and it integrates with nothing.

The official HCV web service

SOAP/XML with WS-Security, a ministry-issued client certificate, signed timestamps, a conformance key, and mandatory conformance testing before go-live. Weeks of specialist work — then yours to maintain forever.

The ministry web app

Manual, one card at a time, in a browser. Fine for the odd lookup; nothing you can build a check-in workflow on.

We did the SOAP, certificate, WS-Security and conformance plumbing once. You send a small JSON object with an API key header. That's the whole integration.

Three steps, no ceremony

1

Get an API key

Sign up in the portal, add a payment method, create a key. Keys are shown once and stored only as an Argon2id hash.

2

Send a request

One HTTPS POST with the health number and version code. Batch an array of cards in a single call if you have many.

3

Read the answer

The ministry's official response code and text, the registered demographics, and our derived status.eligible boolean.

Your first validation, in your language

curl --request POST 'https://validate2.drtools.ca/v1/validate' \
  --header 'Content-Type: application/json' \
  --header 'Api-Key: YOUR_API_KEY' \
  --data '{
    "MOH_EBS_USERNAME": "your-moh-username",
    "MOH_EBS_PASSWORD": "your-moh-password",
    "provider_number":  "123456",
    "health_number":    "1234567890",
    "version_code":     "AB"
  }'
const res = await fetch("https://validate2.drtools.ca/v1/validate", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Api-Key": process.env.DRTOOLS_API_KEY,
  },
  body: JSON.stringify({
    MOH_EBS_USERNAME: process.env.MOH_EBS_USERNAME,
    MOH_EBS_PASSWORD: process.env.MOH_EBS_PASSWORD,
    provider_number:  "123456",
    health_number:    "1234567890",
    version_code:     "AB",
  }),
});
const { results } = await res.json();
if (results[0].status.eligible) {
  // card passed — book the visit
}
import os, requests

res = requests.post(
    "https://validate2.drtools.ca/v1/validate",
    headers={"Api-Key": os.environ["DRTOOLS_API_KEY"]},
    json={
        "MOH_EBS_USERNAME": os.environ["MOH_EBS_USERNAME"],
        "MOH_EBS_PASSWORD": os.environ["MOH_EBS_PASSWORD"],
        "provider_number":  "123456",
        "health_number":    "1234567890",
        "version_code":     "AB",
    },
)
result = res.json()["results"][0]
if result["status"]["eligible"]:
    ...  # card passed — book the visit
require "net/http"
require "json"

uri = URI("https://validate2.drtools.ca/v1/validate")
req = Net::HTTP::Post.new(uri, {
  "Content-Type" => "application/json",
  "Api-Key"      => ENV["DRTOOLS_API_KEY"],
})
req.body = {
  MOH_EBS_USERNAME: ENV["MOH_EBS_USERNAME"],
  MOH_EBS_PASSWORD: ENV["MOH_EBS_PASSWORD"],
  provider_number:  "123456",
  health_number:    "1234567890",
  version_code:     "AB",
}.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
result = JSON.parse(res.body)["results"][0]
puts result["status"]  # => {"card"=>"valid", "eligible"=>true}

The portal generates these for your actual account, plus an OpenAPI schema for generating clients. Prefer not to keep ministry credentials in your app at all? The /v2 flow stores them once, encrypted, in the portal — your application then sends only the health card data, and your MOH username and password never cross the wire again.

The ministry's answer, made usable

  • status is ours. We derive { card, eligible } from the official response-code ranges, so you write if (result.status.eligible) instead of memorising code bands.
  • response_code + the ministry's own wording. The official description and recommended action pass straight through, untouched.
  • response_audit_id. The ministry's own audit identifier for the transaction — keep it for disputes and record-keeping.
  • Registered demographics. Name, gender, date of birth and card expiry as the ministry has them on file.

Eligibility is what the ministry reports for that day; claims remain subject to adjudication by the ministry.

response.json
{
  "results": [
    {
      "response_code": "51",
      "response_id": "IS_ON_ACTIVE_ROSTER",
      "response_description": "Health card passed validation",
      "response_action": "You will receive payment for billable
                          services rendered on this day.",
      "first_name": "JANE",
      "last_name": "DOE",
      "gender": "F",
      "date_of_birth": "1985-04-12",
      "expiry_date": "2029-04-12T00:00:00.000-04:00",
      "health_number": "1234567890",
      "version_code": "AB",
      "response_audit_id": "…",
      "status": { "card": "valid", "eligible": true },
      "duration": 0.435
    }
  ]
}

Every HCV response code, handled

Code bandWhat it meansstatus verdict
00–49Card not valid and cardholder not eligible — number problems, expired eligibilitycard: "invalid", eligible: false
50–59Card passed validation; cardholder eligible todaycard: "valid", eligible: true
60–89Card itself not valid (stolen, cancelled, expired, damaged, returned mail, bad version code) though the cardholder may still be eligiblecard: "invalid", eligibility per code
90+HCV system information and authorization codessystem-level

Building your error handling? Read the complete OHIP response code reference — every code, what it means, and what to do about it.

Write your integration against every possible ministry response

Sandbox

Mock responses covering every response code, with no ministry round trip. Build and test your error handling without touching real patient data.

Live test

Run one real validation from the browser and see exactly what your integration will receive.

Key management

Create, label, rename and revoke keys. Argon2id-hashed, displayed exactly once, never retrievable afterwards — including by us.

Validation logs

Every call, searchable, with the ministry's response — your audit trail for disputes and reconciliation.

Code examples & OpenAPI

Copy-paste samples in cURL, Node.js, Python and Ruby generated for your account, plus schemas for generating clients.

Self-serve billing

Add a card, manage the subscription, see charges. Payments processed by Helcim, a Canadian processor — we never see card numbers.

Three jobs, one API

EMR & software vendors

You've been asked to add health card validation and discovered the official channel is SOAP + certificates + conformance testing. Ship it this sprint instead: one endpoint, a sandbox for every response code, an OpenAPI spec, and a stated 120 requests/minute limit.

Clinics & practices

Catch the expired card while the patient is still standing at the desk — not weeks later as a rejected claim. Validate at booking or check-in through the software you already use, or straight from the portal. No IT project.

Billing agencies

Validate at volume across many providers: batch requests, a per-request provider_number, per-call logs for audit, and the ministry's own audit id on every transaction for disputes.

Built for the obligations Ontario healthcare providers have under PHIPA

Canadian data residency

The service runs in a Toronto data centre. Patient data does not leave Canada.

Encrypted, in transit and at rest

TLS on every request. Stored ministry credentials are encrypted; API keys exist only as Argon2id hashes.

Health numbers hashed in logs

Audit logs prove a validation happened without storing the health number in the clear.

Full audit trails

Per-call validation logs, a security event log, and a separate administrative action log.

Hardened infrastructure

Read-only container filesystems, all Linux capabilities dropped, no application port on the public internet — all traffic through a controlled gateway.

Live standby & backups

A continuously replicated standby database in a second location with a tested failover procedure, plus daily encrypted backups and continuous uptime monitoring.

$49CAD / month
  • Production API access at 120 requests per minute
  • Batch validation, both integration modes, time-limited service codes
  • Developer portal: sandbox, live test, keys, logs, OpenAPI, code examples
  • Self-serve — add a payment method and the account activates immediately
  • Cancel any time from the portal; access runs to the end of the paid period
Create your account

You bring your own Ministry of Health EBS credentials. Our activation is immediate; obtaining MOH credentials is a ministry process on your side and can take time.

Questions developers and clinics actually ask

Do I need my own MOH EBS credentials?

Yes. You'll use your existing Ministry of Health EBS credentials with the API.

How fast is a validation?

Typically under a second, including the round trip to the ministry.

Can I validate more than one card at a time?

Yes — send an array of requests and receive results in the same order.

What languages and frameworks are supported?

Any. It's a plain HTTPS POST with JSON. The portal generates ready-to-paste examples in cURL, Node.js, Python and Ruby, and an OpenAPI schema is available for generating clients.

Can I test without using real patient data?

Yes. The sandbox returns realistic responses covering every ministry response code without contacting the ministry.

Where is patient data stored?

Processing happens in Canada, in a Toronto data centre. Health card numbers are hashed in audit logs rather than stored in the clear.

What happens if I lose my API key?

Keys are hashed and shown only once — nobody can retrieve one, including us. Create a new key and revoke the old one; both take seconds in the portal.

How much does it cost?

$49 CAD per month. Add a payment method in the portal and the account activates immediately.

Can I cancel?

Yes, from the portal. Cancelling stops the next renewal; access continues to the end of the period you've paid for.

Validate your first card today

One HTTPS request. The ministry's answer in under a second. $49 CAD a month, self-serve, cancel any time.

Part of the Dr. Tools suite — the same account also offers MCEDT claim-file integration.