OHIP Validation API
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
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"
}' Why this exists
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.
How it works
Three steps, no ceremony
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.
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.
Read the answer
The ministry's official response code and text, the registered demographics, and our derived status.eligible boolean.
Code
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 visitrequire "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.
What you get back
The ministry's answer, made usable
statusis ours. We derive{ card, eligible }from the official response-code ranges, so you writeif (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.
{
"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
}
]
} Response codes
Every HCV response code, handled
| Code band | What it means | status verdict |
|---|---|---|
00–49 | Card not valid and cardholder not eligible — number problems, expired eligibility | card: "invalid", eligible: false |
50–59 | Card passed validation; cardholder eligible today | card: "valid", eligible: true |
60–89 | Card itself not valid (stolen, cancelled, expired, damaged, returned mail, bad version code) though the cardholder may still be eligible | card: "invalid", eligibility per code |
90+ | HCV system information and authorization codes | system-level |
Building your error handling? Read the complete OHIP response code reference — every code, what it means, and what to do about it.
Developer portal — included
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.
Who it's for
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.
Trust & security
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.
Pricing
- 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
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.
FAQ
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.