> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hi-doctor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Connect the Hi Doctor MCP connector at https://mcp.hi-doctor.ai/mcp. Add this URL as a remote HTTP MCP server in your client, then sign in and authorise access through OAuth. Setup: https://docs.hi-doctor.ai/mcp/connect.
>
> The REST API base is https://api.hi-doctor.ai. Hi Doctor Docs at https://docs.hi-doctor.ai/mcp searches documentation; connect to the product endpoint above for patient tools.
>
> Hi Doctor operates across the whole European Union: residents of all 27 EU member states can use the service, and every consultation is reviewed by an EU-licensed doctor. Every consultation is reviewed in full by an EU-licensed doctor, who decides on medical grounds only. No algorithm, assistant or API call approves a consultation, issues a prescription or changes a dose. The service is available in eight languages: English, Spanish, German, Dutch, Italian, French, Portuguese and Chinese.
>
> ## Rules an assistant must follow
>
> - Never invent, assume or infer a medical answer. "I don't know" is a real answer: ask the patient to pick the closest option rather than guessing for them.
> - Never give medical advice and never suggest a medication or a dose. An EU-licensed doctor decides those.
> - If a questionnaire returns `ineligible`, say so plainly. Do not retry with altered answers.
> - Before anything that costs money or cancels a plan, state exactly what will happen and get an explicit yes.
> - Cancellation takes effect at the **end of the paid period**, not immediately. Say so.
> - An early order is irreversible: it creates the renewal consultation immediately and uses up the period's one early order. Explain that and get an explicit yes.
> - Prescription and invoice links are medical documents. A `signed_redirect` link opens without signing in: give it only to the patient, never repeat it anywhere shared.
> - Send option `value`s, not the labels shown to the patient. Labels are translated, values are stable.
> - Never ask for card details. Payment happens on Stripe's hosted checkout via a link.
> - On severe or sudden symptoms (chest pain, difficulty breathing, severe allergic reaction, suicidal thoughts), stop and direct the patient to their local emergency number.

# Partner API quickstart: your first test consultation

> Create a first test consultation with the Hi Doctor Partner API in seven steps, from key check to a signed webhook, with cURL, Node.js and Python.

This quickstart creates your first Partner API consultation in test mode, in seven steps from a key check to a signed webhook. Test consultations are synthetic and never reach a doctor, so you can repeat the walkthrough as often as you like. You need a test key that starts with `hdp_test_` and cURL, Node.js 18 or Python 3.

Last reviewed: 2026-11-04

Every request on this page is listed in the generated [Partner API reference](https://docs.hi-doctor.ai/api-reference/connectivity/check-an-api-key.md). For the concepts behind the steps, read [How the Partner API works](https://docs.hi-doctor.ai/partners/how-it-works.md).

## Before you start

* A test API key that starts with `hdp_test_`, with the scopes `consultations:read`, `consultations:write`, `prescriptions:read` and `webhooks:manage`. The first two cover steps 1 to 6; registering a webhook endpoint with the API in step 7 needs all four. Add `patients:read` if you also want to read the patients you create. See [Authentication](https://docs.hi-doctor.ai/partners/authentication.md#which-scopes-can-a-key-have) for every scope. Listing products and fetching a questionnaire need no scope. Your Hi Doctor contact issues the first key; after that you manage keys in the [Business portal](https://docs.hi-doctor.ai/partners/business-portal.md).
* `curl`, Node.js 18 or later, or Python 3 with `requests`.

Set the key once in your shell:

```bash theme={null}
export HD_PARTNER_API_KEY="hdp_test_..."
export HD_BASE_URL="https://api.hi-doctor.ai"
```

## 1. Check the key

`GET /v1/partner/ping/` confirms the key is accepted and tells you its mode.

```bash cURL theme={null}
curl -sS "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/ping/" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY"
```

```javascript Node.js theme={null}
const base = process.env.HD_BASE_URL ?? "https://api.hi-doctor.ai";
const response = await fetch(`${base}/v1/partner/ping/`, {
  headers: { Authorization: `Bearer ${process.env.HD_PARTNER_API_KEY}` },
});
console.log(response.status, await response.json());
```

```python Python theme={null}
import os
import requests

base = os.environ.get("HD_BASE_URL", "https://api.hi-doctor.ai")
response = requests.get(
    f"{base}/v1/partner/ping/",
    headers={"Authorization": f"Bearer {os.environ['HD_PARTNER_API_KEY']}"},
    timeout=30,
)
print(response.status_code, response.json())
```

The response names your partner and the key's mode:

```json theme={null}
{
  "ok": true,
  "partner": "your-partner-slug",
  "mode": "test",
  "key_id": "k1a2b3c4d5e6"
}
```

## 2. List your products for a country

`GET /v1/partner/products/?country=NL` returns the products in your agreement that are open in that country. Each product names its questionnaire and the current `schema_version` and `schema_hash`. Plans and dosages can only be chosen from this list.

```bash cURL theme={null}
curl -sS "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/products/?country=NL" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY"
```

```javascript Node.js theme={null}
const base = process.env.HD_BASE_URL ?? "https://api.hi-doctor.ai";
const response = await fetch(`${base}/v1/partner/products/?country=NL`, {
  headers: { Authorization: `Bearer ${process.env.HD_PARTNER_API_KEY}` },
});
const { results } = await response.json();
for (const product of results) {
  console.log(product.product_id, product.questionnaire_slug, product.schema_version);
}
```

```python Python theme={null}
import os
import requests

base = os.environ.get("HD_BASE_URL", "https://api.hi-doctor.ai")
response = requests.get(
    f"{base}/v1/partner/products/",
    params={"country": "NL"},
    headers={"Authorization": f"Bearer {os.environ['HD_PARTNER_API_KEY']}"},
    timeout=30,
)
for product in response.json()["results"]:
    print(product["product_id"], product["questionnaire_slug"], product["schema_version"])
```

Each result has `product_id`, `name`, `category`, `questionnaire_slug`, `schema_version`, `schema_hash`, `country`, `plans` (`plan_key`, `supply_days`), `dosages` (`dosage`, `label`) and `price` (`amount_cents`, `currency`, as agreed in your partner agreement).

## 3. Fetch the questionnaire schema

`GET /v1/partner/questionnaires/{slug}/` returns the schema the doctor's review is based on. Set `QUESTIONNAIRE_SLUG` to the `questionnaire_slug` of the product you chose in step 2. Render every applicable step to the patient and collect the answers in the `entries-v1` format. [Questionnaires and schema](https://docs.hi-doctor.ai/partners/questionnaires-and-schema.md) documents every step kind.

```bash cURL theme={null}
curl -sS "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/questionnaires/$QUESTIONNAIRE_SLUG/" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY"
```

```javascript Node.js theme={null}
const base = process.env.HD_BASE_URL ?? "https://api.hi-doctor.ai";
const slug = process.env.QUESTIONNAIRE_SLUG;
const response = await fetch(`${base}/v1/partner/questionnaires/${slug}/`, {
  headers: { Authorization: `Bearer ${process.env.HD_PARTNER_API_KEY}` },
});
const questionnaire = await response.json();
console.log(questionnaire.schema_version, questionnaire.schema_hash, questionnaire.answer_format);
```

```python Python theme={null}
import os
import requests

base = os.environ.get("HD_BASE_URL", "https://api.hi-doctor.ai")
slug = os.environ["QUESTIONNAIRE_SLUG"]
response = requests.get(
    f"{base}/v1/partner/questionnaires/{slug}/",
    headers={"Authorization": f"Bearer {os.environ['HD_PARTNER_API_KEY']}"},
    timeout=30,
)
questionnaire = response.json()
print(questionnaire["schema_version"], questionnaire["schema_hash"], questionnaire["answer_format"])
```

## 4. Upload a photo

Upload each photo the schema asks for with `POST /v1/partner/files/` as `multipart/form-data`. JPEG, PNG and PDF are accepted, detected from the file's bytes. Keep the returned `upload_id`. See [Photos and files](https://docs.hi-doctor.ai/partners/photos.md).

```bash cURL theme={null}
curl -sS -X POST "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/files/" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY" \
  -F "purpose=photo" \
  -F "file=@body-photo.jpg"
```

```javascript Node.js theme={null}
import { readFile } from "node:fs/promises";

const base = process.env.HD_BASE_URL ?? "https://api.hi-doctor.ai";
const form = new FormData();
form.append("purpose", "photo");
form.append("file", new Blob([await readFile("body-photo.jpg")]), "body-photo.jpg");
const response = await fetch(`${base}/v1/partner/files/`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.HD_PARTNER_API_KEY}` },
  body: form,
});
const upload = await response.json();
console.log(response.status, upload.upload_id, upload.expires_at);
```

```python Python theme={null}
import os
import requests

base = os.environ.get("HD_BASE_URL", "https://api.hi-doctor.ai")
with open("body-photo.jpg", "rb") as photo:
    response = requests.post(
        f"{base}/v1/partner/files/",
        headers={"Authorization": f"Bearer {os.environ['HD_PARTNER_API_KEY']}"},
        data={"purpose": "photo"},
        files={"file": photo},
        timeout=60,
    )
upload = response.json()
print(response.status_code, upload["upload_id"], upload["expires_at"])
```

## 5. Submit the consultation

Put the request body in `consultation.json`. The question keys below are illustrative; use the keys from the schema you fetched. `locale` is one of the questionnaire locales served (`en`, `es` or `zh`; anything else falls back to English). Send the `schema_hash` from the schema you fetched; the hash of any served locale is accepted. An upload answer is the scalar `"value": "<upload_id>"`.

```json theme={null}
{
  "product_id": "<product_id from step 2>",
  "country": "NL",
  "plan_key": "<plan_key from step 2>",
  "dosage": "<dosage from step 2>",
  "locale": "en",
  "schema_version": "<schema_version from step 3>",
  "schema_hash": "<schema_hash from step 3>",
  "external_reference": "ORDER-8812",
  "patient": {
    "external_patient_id": "u_123",
    "email": "patient@example.com",
    "first_name": "Sam",
    "last_name": "Example",
    "date_of_birth": "1988-04-02",
    "sex": "female",
    "consent": {
      "terms_accepted": true,
      "privacy_accepted": true,
      "clinical_data_processing_accepted": true,
      "accepted_at": "2026-11-03T10:14:00Z",
      "notice_version": "<current patient notice version>"
    }
  },
  "answers": [
    { "question_key": "height_weight", "values": { "height": 172, "weight": 96 } },
    { "question_key": "body_photo", "value": "<upload_id from step 4>" }
  ]
}
```

Send it with an `Idempotency-Key` that you create once for this consultation and store before the first attempt. If the request times out, rerun the same sample: it reads the stored key back, so the retry carries the **same** key and body and you get the original response instead of a second consultation. Delete `idempotency-key.txt` only when you submit a different consultation. See [Partner API idempotency](https://docs.hi-doctor.ai/partners/idempotency.md).

```bash cURL theme={null}
# Create the key once and keep it; a retry reuses the stored value.
[ -s idempotency-key.txt ] || uuidgen > idempotency-key.txt
curl -sS -X POST "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/consultations/" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(tr -d '[:space:]' < idempotency-key.txt)" \
  --data @consultation.json
```

```javascript Node.js theme={null}
import { readFile, writeFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import { randomUUID } from "node:crypto";

const base = process.env.HD_BASE_URL ?? "https://api.hi-doctor.ai";
const body = await readFile("consultation.json", "utf8");
// Create the key once and keep it; a retry reuses the stored value.
const keyFile = "idempotency-key.txt";
if (!existsSync(keyFile)) await writeFile(keyFile, randomUUID());
const idempotencyKey = (await readFile(keyFile, "utf8")).trim();
const response = await fetch(`${base}/v1/partner/consultations/`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.HD_PARTNER_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body,
});
const consultation = await response.json();
console.log(response.status, consultation.id, consultation.status, consultation.version);
```

```python Python theme={null}
import json
import os
import uuid
import requests

base = os.environ.get("HD_BASE_URL", "https://api.hi-doctor.ai")
with open("consultation.json") as handle:
    body = json.load(handle)
# Create the key once and keep it; a retry reuses the stored value.
key_file = "idempotency-key.txt"
if not os.path.exists(key_file):
    with open(key_file, "w") as handle:
        handle.write(str(uuid.uuid4()))
with open(key_file) as handle:
    idempotency_key = handle.read().strip()
response = requests.post(
    f"{base}/v1/partner/consultations/",
    headers={
        "Authorization": f"Bearer {os.environ['HD_PARTNER_API_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
    json=body,
    timeout=60,
)
consultation = response.json()
print(response.status_code, consultation["id"], consultation["status"], consultation["version"])
```

A `201` response returns the [consultation resource](https://docs.hi-doctor.ai/partners/submit-a-consultation.md#response) with `status: "submitted"` and `version: 1`.

## 6. Move the test consultation and read it

In test mode nothing happens until you advance the consultation. Set `CONSULTATION_ID` to the `id` from the step 5 response, move the consultation to `in_review`, then read it back:

```bash cURL theme={null}
curl -sS -X POST "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/test/consultations/$CONSULTATION_ID/advance/" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{"to": "in_review"}'

curl -sS "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/consultations/$CONSULTATION_ID/" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY"
```

```javascript Node.js theme={null}
const base = process.env.HD_BASE_URL ?? "https://api.hi-doctor.ai";
const id = process.env.CONSULTATION_ID;
const headers = {
  Authorization: `Bearer ${process.env.HD_PARTNER_API_KEY}`,
  "Content-Type": "application/json",
};
await fetch(`${base}/v1/partner/test/consultations/${id}/advance/`, {
  method: "POST",
  headers,
  body: JSON.stringify({ to: "in_review" }),
});
const response = await fetch(`${base}/v1/partner/consultations/${id}/`, { headers });
const consultation = await response.json();
console.log(consultation.status, consultation.version);
```

```python Python theme={null}
import os
import requests

base = os.environ.get("HD_BASE_URL", "https://api.hi-doctor.ai")
consultation_id = os.environ["CONSULTATION_ID"]
headers = {"Authorization": f"Bearer {os.environ['HD_PARTNER_API_KEY']}"}
requests.post(
    f"{base}/v1/partner/test/consultations/{consultation_id}/advance/",
    headers=headers,
    json={"to": "in_review"},
    timeout=30,
)
consultation = requests.get(
    f"{base}/v1/partner/consultations/{consultation_id}/", headers=headers, timeout=30
).json()
print(consultation["status"], consultation["version"])
```

Polling works for a first test. In production, use webhooks and treat polling as a fallback.

## 7. Receive the webhook

Register an HTTPS endpoint for test mode in the [Business portal](https://docs.hi-doctor.ai/partners/business-portal.md) or with `POST /v1/partner/webhook-endpoints/` (scopes `webhooks:manage`, `consultations:read` and `prescriptions:read`). The response returns the signing secret once, as `whsec_` followed by base64: store it straight away.

```bash theme={null}
curl -sS -X POST "${HD_BASE_URL:-https://api.hi-doctor.ai}/v1/partner/webhook-endpoints/" \
  -H "Authorization: Bearer $HD_PARTNER_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{"url": "https://partner.example.com/hooks/hi-doctor", "events": ["*"], "description": "Quickstart"}'
```

Then advance the test consultation again, for example to `approved`. Each change produces one signed event whose `data` is the full consultation snapshot. Deliveries follow the Standard Webhooks specification: verify the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers with the code in [Webhooks](https://docs.hi-doctor.ai/partners/webhooks.md#verify-the-signature), or any Standard Webhooks library, before trusting the body.

## Frequently asked questions

### Do test consultations reach a doctor?

No. Test consultations are synthetic, never reach a doctor, create no medical record and are never billed.

### Why does my test consultation stay submitted?

Nothing moves on its own in test mode. Advance the consultation with the test advance endpoint to reach each status.

### Which scopes does the quickstart key need?

consultations:read and consultations:write cover steps 1 to 6. Registering a webhook endpoint in step 7 also needs prescriptions:read and webhooks:manage.

### Where do I get the questionnaire slug?

From the product list in step 2. Each product names its questionnaire\_slug together with the current schema\_version and schema\_hash.

### What happens if I retry the submission after a timeout?

Send the same Idempotency-Key and the same body. You get the original response instead of a second consultation.

### Which locale should I send?

One of the questionnaire locales served, en, es or zh. Any other value falls back to English. Send the schema\_hash from the schema you fetched; the hash of any served locale is accepted.

### Do Partner API paths end with a slash?

The samples use the paths exactly as the Partner API reference lists them, with a trailing slash. Keep the same form on a retry, because an idempotent retry must use the same path as the first attempt.

## Next steps

> [card] [Questionnaires and schema](https://docs.hi-doctor.ai/partners/questionnaires-and-schema.md)
>
> Render every step kind correctly.

> [card] [Webhooks](https://docs.hi-doctor.ai/partners/webhooks.md)
>
> Signatures, retries and the ordering rule.

> [card] [Go-live checklist](https://docs.hi-doctor.ai/partners/go-live-checklist.md)
>
> What must be in place before live mode.

Commercial questions: read the [partnership FAQ](https://docs.hi-doctor.ai/partnerships/faq.md) or write to [hello@hi-doctor.ai](mailto:hello@hi-doctor.ai).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.