> ## 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.

# Submit a consultation with the Partner API

> Create a Hi Doctor partner consultation in one POST: product, country, plan, patient identity, consent and entries-v1 answers, and the 201 response.

Submitting a consultation through the Partner API is one call to `POST /v1/partner/consultations`, which creates a consultation for an EU-licensed doctor to review. The request carries everything at once: the product choice, the patient's identity and consent, and every questionnaire answer, so there is no draft to manage. Send it with the `consultations:write` scope and an `Idempotency-Key` header, then track the result by webhook.

Last reviewed: 2026-11-04

Every consultation is reviewed in full by an EU-licensed doctor, who decides on medical grounds only (see [Safety](https://docs.hi-doctor.ai/essentials/safety.md)). Submitting never approves anything: it hands the doctor a complete case. The `Idempotency-Key` header is required; see [Partner API idempotency](https://docs.hi-doctor.ai/partners/idempotency.md).

## Request

```json theme={null}
{
  "product_id": "<product_id>",
  "country": "NL",
  "plan_key": "<plan_key>",
  "dosage": "<dosage>",
  "locale": "nl",
  "schema_version": "<schema_version>",
  "schema_hash": "<schema_hash>",
  "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",
    "phone": "+31600000000",
    "address": {
      "line1": "Example street 1",
      "line2": "Apartment 2",
      "city": "Amsterdam",
      "postal_code": "1011 AA",
      "country": "NL"
    },
    "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>",
      "evidence_reference": "consent-log-55102",
      "ip": "203.0.113.24"
    }
  },
  "answers": [
    { "question_key": "height_weight", "values": { "height": 172, "weight": 96 } },
    { "question_key": "body_photo", "value": "<upload_id>" }
  ]
}
```

### Top-level fields

| Field | Required | Notes |
| - | - | - |
| `product_id` | yes | From `GET /v1/partner/products`. |
| `country` | yes | ISO 3166-1 alpha-2 code of a country your partner catalogue offers the product in, for example `NL`. See [accepted codes](#which-country-codes-does-country-accept) and [Country binding](#country-binding). |
| `plan_key` | yes | One of the product's `plans[].plan_key` for that country. |
| `dosage` | when the product lists dosages | One of the product's `dosages[].dosage`. |
| `locale` | no | Two-letter language code, saved as the language of a new patient account. When empty, your partner default language is used. |
| `schema_version`, `schema_hash` | yes | From the questionnaire you rendered. A mismatch returns `409 schema_outdated`. |
| `external_reference` | no | Your order or case reference, up to 128 characters. Unique per partner and mode; a duplicate returns `409 duplicate_external_reference`. You can search by it. |
| `patient` | yes | Patient identity and consent, below. |
| `answers` | yes | Non-empty `entries-v1` list. See [Questionnaires and schema](https://docs.hi-doctor.ai/partners/questionnaires-and-schema.md#the-entries-v1-answer-format). |

The product, country, plan and dosage must be a combination returned by the product list, otherwise the request fails with `422 product_unavailable`. This includes a country that has closed to new consultations since you fetched the list: it is no longer in the list, so the request fails with `422 product_unavailable`.

A body that is missing a required field, or has a field of the wrong type, fails with `422 invalid_request`. Its `errors` list names each `field` with a `code` and `message`. The body is checked after the request itself: authentication, scope, rate limits and the `Idempotency-Key` header come first, so a request with no `Idempotency-Key` fails with `400 idempotency_key_required` even when its body is also invalid. Body validation then runs before the product, schema, consent and answer checks.

### Patient

| Field | Required | Notes |
| - | - | - |
| `external_patient_id` | yes | Your stable identifier for the patient, up to 128 characters. Reusing it links new consultations to the same patient. |
| `email` | yes | The patient's own email address. It is checked the first time you submit for a given `external_patient_id` in live mode: if your brand already has a Hi Doctor account with this email, the request fails with `409 patient_conflict`. Later submissions for the same `external_patient_id` reuse the linked patient, and test mode does not run this check. |
| `first_name`, `last_name` | yes | Legal names, as on the patient's identity document. |
| `date_of_birth` | yes | `YYYY-MM-DD`. |
| `sex` | yes | `female`, `male`, `other` or `unspecified`. |
| `phone` | no | E.164 format. |
| `address` | no | `line1`, `line2`, `city`, `postal_code`, `country`. `line1` and `line2` are stored as one street line, joined with `", "`. Together, separator included, they must not exceed 200 characters; a longer address fails with `422 invalid_request` (field `patient.address`) and is never truncated. `country` is the patient's country of residence, ISO 3166-1 alpha-2 or alpha-3, and must be an EU member state: any other code fails with `422 invalid_request` (field `patient.address.country`, code `residence_outside_eu`). When it is left out, the request `country` is recorded as the residence. |
| `national_identifier` | where the country needs one | The patient's national identifier, `{"type": "...", "value": "..."}`. See [National identifier](#national-identifier). |
| `identity_document` | where prescribing needs one | The patient's identity document, `{"type": "...", "number": "...", "issuing_state": "..."}`. See [Identity document](#identity-document). |
| `consent` | yes | See below. |

### National identifier

Some countries require a national patient identifier before a prescription can be issued. Send it as `patient.national_identifier`. A submission for such a country fails with `422 patient_identifier_required` when the identifier is neither in the request nor already stored for the patient. In live mode an existing patient whose identifier is stored can omit it on later submissions.

```json theme={null}
{
  "patient": {
    "national_identifier": { "type": "nif", "value": "123456789" }
  }
}
```

The other `patient` fields are omitted above.

`type` is an identifier type of the **product country** (the request `country`), not of the patient's residence. A country without a row in this table has no identifier type, so you send no `national_identifier` for it.

| Country | Types |
| - | - |
| BE | `niss`, `eid_card_number` |
| ES | `dni`, `nie` |
| IE | `ppsn` |
| IT | `codice_fiscale`, `stp`, `eni` |
| PT | `nif` |
| SE | `personnummer` |

* `value` is the identifier as printed. It is validated (format and check digits) and normalised.
* An unknown type, a type the product country does not use, or a value that fails validation fails with `422 invalid_request`, with an error item whose `field` is `patient.national_identifier` and `code` is `invalid_identifier`.
* The value is never echoed: not in errors, not in any response and not in any event.
* In live mode it is stored on the patient's account the first time. A later submission for the same patient with a different identifier fails with `409 patient_conflict` and never overwrites the stored one.
* Test mode validates it and stores nothing.

### Identity document

Electronic prescribing in some countries needs the patient's identity document before the doctor can approve. Send it as `patient.identity_document`. It is optional on submission; when it is missing and the doctor needs it, they ask for it with an [information request](https://docs.hi-doctor.ai/partners/information-requests.md#identity-document-requests) with `reason_code` `identity_document_required`.

```json theme={null}
{
  "patient": {
    "identity_document": { "type": "passport", "number": "C01X00T47", "issuing_state": "DE" }
  }
}
```

The other `patient` fields are omitted above.

| Field | Required | Notes |
| - | - | - |
| `type` | yes | `id_card` or `passport`. |
| `number` | yes | The document number as printed, up to 50 characters. It is normalised. |
| `issuing_state` | yes | ISO 3166-1 alpha-2 or alpha-3 code of the country that issued the document. |

* It is validated with the same rules as a patient's own profile: identity card numbers that carry a check letter have it verified; other numbers must be 5 to 20 letters or digits once spaces, dots and hyphens are removed.
* A document that breaks the rules (an unknown issuing state, a wrong check letter, a bad number format) fails with `422 invalid_request`, with an error item whose `field` is `patient.identity_document` and `code` is `invalid_identity_document`.
* A structural error keeps its own nested field instead: a value that is not an object is field `patient.identity_document.non_field_errors` with code `invalid`, an unsupported type is field `patient.identity_document.type` with code `invalid_choice`, and a missing, blank or too long member is that member's field, for example `patient.identity_document.number` with code `required`, `blank` or `max_length`.
* The document is never echoed: not in errors, not in any response and not in any event.
* In live mode it is stored on the patient's account the first time. A later submission for the same patient with a different document fails with `409 patient_conflict` and never overwrites the stored one.
* Test mode validates it and stores nothing.

### Consent

Hi Doctor needs the patient's own consent, captured by you before submission. Send all three flags as JSON booleans; all must be `true`. A flag that is `false` fails with `422 consent_required`. Some non-boolean values are read as booleans: `"true"` and `1` count as `true`, while `"false"` and `0` count as `false` and so fail with `422 consent_required`. A flag that is missing, or a value that cannot be read as a boolean, fails earlier with `422 invalid_request`. The API reference therefore lists `true` as the only accepted value of each flag.

| Field | Required | Notes |
| - | - | - |
| `terms_accepted` | yes | The patient accepted Hi Doctor's terms for the consultation. |
| `privacy_accepted` | yes | The patient was shown Hi Doctor's privacy notice. |
| `clinical_data_processing_accepted` | yes | The patient agreed that their health data is processed for the consultation. |
| `accepted_at` | yes | When the patient gave consent, ISO 8601. |
| `notice_version` | yes | The version of Hi Doctor's patient notice that you showed the patient. If it is not the current one, the request fails with `422 consent_notice_outdated` and the error includes `current_notice_version`, so you can show the current notice and ask again. |
| `evidence_reference` | no | Your reference to the stored consent record, up to 128 characters. |
| `ip` | no | The IPv4 or IPv6 address the patient consented from, or `null`. Any other value fails with `422 invalid_request`. |

[Security and compliance](https://docs.hi-doctor.ai/partners/security-and-compliance.md#consent-capture) describes how to capture consent.

### Upload answers

Upload each photo first with `POST /v1/partner/files`, then answer the upload step with the scalar shape `{"question_key": "<key>", "value": "<upload_id>"}`. An upload that does not exist, has expired or is already attached fails with `422 upload_unusable`. See [Photos and files](https://docs.hi-doctor.ai/partners/photos.md).

## Which country codes does `country` accept?

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.

`country` takes the ISO 3166-1 alpha-2 code of an EU member state from the table below. Which codes your platform can use for each product is set in your partner agreement and returned by `GET /v1/partner/products`; some treatments are limited to specific countries by local rules.

| Country | Code |
| - | - |
| Austria | AT |
| Belgium | BE |
| Bulgaria | BG |
| Croatia | HR |
| Cyprus | CY |
| Czechia | CZ |
| Denmark | DK |
| Estonia | EE |
| Finland | FI |
| France | FR |
| Germany | DE |
| Greece | GR |
| Hungary | HU |
| Ireland | IE |
| Italy | IT |
| Latvia | LV |
| Lithuania | LT |
| Luxembourg | LU |
| Malta | MT |
| Netherlands | NL |
| Poland | PL |
| Portugal | PT |
| Romania | RO |
| Slovakia | SK |
| Slovenia | SI |
| Spain | ES |
| Sweden | SE |

See [Countries](https://docs.hi-doctor.ai/essentials/regions.md) for languages and how the service works in each country.

## Country binding

The request `country` is the country you submit the product for, and the questionnaire must agree with it:

* If the schema has an explicit country or pickup step, answer it, and the answer must be the same country.
* An answer that names another country, or two answers that disagree, fail with `422 country_conflict`. Hi Doctor never overwrites the answer.
* If the schema has no country step, the country derived from the answers must still be the requested `country`.
* A requested country that is closed to new consultations fails earlier, with `422 product_unavailable` (see [Top-level fields](#top-level-fields)). `422 country_closed` comes from a later check on the consultation's clinical country, during submission.

## Response

`201 Created` returns the consultation resource:

```json theme={null}
{
  "id": "6b1c9a52-2f0d-4e8e-9d1e-7a4c0b2e5f10",
  "object": "consultation",
  "mode": "live",
  "status": "submitted",
  "version": 1,
  "status_history": [{ "status": "submitted", "version": 1, "at": "2026-11-03T10:15:02Z" }],
  "external_reference": "ORDER-8812",
  "product": { "product_id": "<product_id>", "name": "<product name>" },
  "country": "NL",
  "plan_key": "<plan_key>",
  "dosage": "<dosage>",
  "patient": { "id": "0d6f3c1e-91a4-4b8a-b0f7-3c5e2a9d8e41", "external_patient_id": "u_123" },
  "submitted_at": "2026-11-03T10:15:02Z",
  "updated_at": "2026-11-03T10:15:02Z",
  "reviewer": null,
  "decision": null,
  "open_info_request": null,
  "prescriptions": [],
  "billing": { "state": "pending" },
  "cancellable": true
}
```

`billing` also carries `price` (`amount_cents`, `currency`), the price agreed in your partner agreement for this product. [Consultation lifecycle](https://docs.hi-doctor.ai/partners/consultation-lifecycle.md) explains every field as the consultation moves on.

## Errors

A rejected submission returns the standard error envelope with a stable `code`, and nothing is created. The submission codes, what each means and which ones to retry are listed once, in [Partner API errors](https://docs.hi-doctor.ai/partners/errors.md#submission). Answer problems name the `question_key` when the API can tie them to a step, so you can send the patient back to the right question.

## Repeat consultations

Submitting again with the same `external_patient_id` creates a new consultation for the same patient. When the patient has an earlier approved consultation, the doctor sees it. Subscription renewals and reorders are not offered in API v1.

## Frequently asked questions

### Can I save a draft and submit it later?

No. A consultation is created in one request that carries the product, the patient, their consent and every answer. Keep any draft state on your side until the patient has finished the questionnaire.

### What happens if I send the same request twice?

Reuse the same Idempotency-Key header and the same body: a retry within 24 hours returns the stored result instead of creating a second consultation. See [Partner API idempotency](https://docs.hi-doctor.ai/partners/idempotency.md).

### Which countries can I submit a consultation for?

Any EU member state code that your partner catalogue offers the product in. The catalogue comes from the product list endpoint and follows your partner agreement.

### Does the patient need a Hi Doctor account first?

No. Hi Doctor links the consultation to a patient through your own patient identifier, external\_patient\_id. In live mode, if your brand already has a Hi Doctor account with the same email, the first submission fails with a patient conflict error; see [Partner API errors](https://docs.hi-doctor.ai/partners/errors.md).

### Is a national identifier always required?

No. Only countries that need one for prescribing ask for it, and an identifier already stored for the patient is reused.

### Who decides whether the patient receives treatment?

Every consultation is reviewed in full by an EU-licensed doctor, who decides on medical grounds only. Submitting a consultation never approves it.

### Can I submit a renewal or a reorder?

Not in API v1. A submission whose answers need such a context fails with a context error; see [Partner API errors](https://docs.hi-doctor.ai/partners/errors.md).

## Next steps

> [card] [Consultation lifecycle](https://docs.hi-doctor.ai/partners/consultation-lifecycle.md)
>
> Statuses, the consultation resource and cancelling.

> [card] [Webhooks](https://docs.hi-doctor.ai/partners/webhooks.md)
>
> Learn when the doctor opens, asks about or decides on the consultation.

> [card] [Partner API errors](https://docs.hi-doctor.ai/partners/errors.md)
>
> Every submission error code and how to handle it.

> [card] [Partnership FAQ](https://docs.hi-doctor.ai/partnerships/faq.md)
>
> Commercial questions about partnering with Hi Doctor.


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