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

> Submits a patient's questionnaire answers for clinical review. The patient's consent flags must all be true and the notice version must be the current one (otherwise 422 consent_notice_outdated with current_notice_version). schema_version and schema_hash must name the published questionnaire (otherwise 409 schema_outdated with the current pair). Retries with the same Idempotency-Key and body return the original response.

Answer only the questions that apply after branching: an answer to a step the other answers hide (conditional display) is refused with 422 unexpected_question_key, one error item per hidden question_key. Errors never repeat a submitted answer value or identifier; they name the question_key instead (an unexpected question_key is echoed as sent). A pharmacy selection that is not valid for the patient's country is refused with 422 pharmacy_selection_invalid (question_key pharmacy_selection), whatever the directory's reason.

patient.national_identifier is optional: send it where the product country's prescribing needs one (otherwise 422 patient_identifier_required, unless the identifier is already stored for the patient). An unknown type or invalid value is refused with 422 invalid_request (field patient.national_identifier, code invalid_identifier); it is never echoed or returned. In live mode it is stored on the patient's account the first time; a different identifier for the same patient is refused with 409 patient_conflict and never overwrites it. Test mode validates it and stores nothing.

patient.identity_document is optional: send it where electronic prescribing in the patient's country needs the patient's identity document before approval (a national id_card or a passport, with the country that issued it). It is validated like the patient's own profile (5 to 20 letters or digits; for a few issuing countries an id_card number is also checked against its national format); one that breaks those rules is refused with 422 invalid_request (field patient.identity_document, code invalid_identity_document). Structural errors keep their nested field errors (for example field patient.identity_document.type, code invalid_choice, for an unsupported type; patient.identity_document.number, code required, when the number is missing). It is never echoed or returned. In live mode it is stored on the patient's account the first time; a different document for the same patient is refused with 409 patient_conflict and never overwrites it. Test mode validates it and stores nothing. Without it, the reviewing doctor, if they need it, asks through an information request with reason_code identity_document_required.

patient.address line1 and line2 are stored joined with ", " in one street line of at most 200 characters (separator included); a longer address is refused with 422 invalid_request (field patient.address), never truncated. patient.address.country is the patient's country of residence and must be an EU member state (ISO 3166-1 alpha-2 or alpha-3), in test and live mode alike; any other value is refused with 422 invalid_request (field patient.address.country, code residence_outside_eu). Without it, the product country is the residence. A patient who withdrew consent is refused with 409 consent_withdrawn. Requires the consultations:write scope.

### Access, effects and retries
**Ownership:** The API key's own partner and mode (test or live).

**Prerequisites:** A partner API key with the `consultations:write` scope.

**Effects:** Creates the consultation (and the patient on first submission) and sends it to clinical review; emits consultation webhook events.

**Retries:** Required Idempotency-Key: a retry with the same key and body within 24 hours returns the stored response.



## OpenAPI

````yaml /api-reference/partner/openapi.json post /v1/partner/consultations/
openapi: 3.0.3
info:
  title: Hi Doctor Partner API
  version: '1.0'
  description: >-
    The Hi Doctor Partner API lets a partner submit online medical consultations
    for its patients, follow their clinical review, answer the doctor's
    information requests and read the resulting prescriptions. Partner requests
    use Authorization: Bearer <api key> with a partner API key; test keys and
    live keys are separate. See https://docs.hi-doctor.ai/partners/overview for
    the partner guides.
  contact:
    name: Hi Doctor
    email: hello@hi-doctor.ai
    url: https://docs.hi-doctor.ai
servers:
  - url: https://api.hi-doctor.ai
    description: Production
security: []
tags:
  - name: Connectivity
    description: Check that a key works.
  - name: Products
    description: What you can offer, where, and at what price.
  - name: Questionnaires
    description: The questions a patient answers for a product.
  - name: Files
    description: Photos and documents attached to answers.
  - name: Consultations
    description: Submit consultations and follow their review.
  - name: Patients
    description: The patients you submitted consultations for, and their consent.
  - name: Information requests
    description: Questions the doctor sends back during review.
  - name: Prescriptions
    description: Prescriptions issued after an approval.
  - name: Webhooks
    description: Receive events instead of polling.
  - name: Test mode
    description: Tools that only work with test keys.
paths:
  /v1/partner/consultations/:
    post:
      tags:
        - Consultations
      summary: Submit a consultation
      description: >-
        Submits a patient's questionnaire answers for clinical review. The
        patient's consent flags must all be true and the notice version must be
        the current one (otherwise 422 consent_notice_outdated with
        current_notice_version). schema_version and schema_hash must name the
        published questionnaire (otherwise 409 schema_outdated with the current
        pair). Retries with the same Idempotency-Key and body return the
        original response.


        Answer only the questions that apply after branching: an answer to a
        step the other answers hide (conditional display) is refused with 422
        unexpected_question_key, one error item per hidden question_key. Errors
        never repeat a submitted answer value or identifier; they name the
        question_key instead (an unexpected question_key is echoed as sent). A
        pharmacy selection that is not valid for the patient's country is
        refused with 422 pharmacy_selection_invalid (question_key
        pharmacy_selection), whatever the directory's reason.


        patient.national_identifier is optional: send it where the product
        country's prescribing needs one (otherwise 422
        patient_identifier_required, unless the identifier is already stored for
        the patient). An unknown type or invalid value is refused with 422
        invalid_request (field patient.national_identifier, code
        invalid_identifier); it is never echoed or returned. In live mode it is
        stored on the patient's account the first time; a different identifier
        for the same patient is refused with 409 patient_conflict and never
        overwrites it. Test mode validates it and stores nothing.


        patient.identity_document is optional: send it where electronic
        prescribing in the patient's country needs the patient's identity
        document before approval (a national id_card or a passport, with the
        country that issued it). It is validated like the patient's own profile
        (5 to 20 letters or digits; for a few issuing countries an id_card
        number is also checked against its national format); one that breaks
        those rules is refused with 422 invalid_request (field
        patient.identity_document, code invalid_identity_document). Structural
        errors keep their nested field errors (for example field
        patient.identity_document.type, code invalid_choice, for an unsupported
        type; patient.identity_document.number, code required, when the number
        is missing). It is never echoed or returned. In live mode it is stored
        on the patient's account the first time; a different document for the
        same patient is refused with 409 patient_conflict and never overwrites
        it. Test mode validates it and stores nothing. Without it, the reviewing
        doctor, if they need it, asks through an information request with
        reason_code identity_document_required.


        patient.address line1 and line2 are stored joined with ", " in one
        street line of at most 200 characters (separator included); a longer
        address is refused with 422 invalid_request (field patient.address),
        never truncated. patient.address.country is the patient's country of
        residence and must be an EU member state (ISO 3166-1 alpha-2 or
        alpha-3), in test and live mode alike; any other value is refused with
        422 invalid_request (field patient.address.country, code
        residence_outside_eu). Without it, the product country is the residence.
        A patient who withdrew consent is refused with 409 consent_withdrawn.
        Requires the consultations:write scope.


        ### Access, effects and retries

        **Ownership:** The API key's own partner and mode (test or live).


        **Prerequisites:** A partner API key with the `consultations:write`
        scope.


        **Effects:** Creates the consultation (and the patient on first
        submission) and sends it to clinical review; emits consultation webhook
        events.


        **Retries:** Required Idempotency-Key: a retry with the same key and
        body within 24 hours returns the stored response.
      operationId: create_consultation
      parameters:
        - in: header
          name: Idempotency-Key
          schema:
            type: string
            pattern: ^[A-Za-z0-9_-]{8,128}$
          description: >-
            A unique key per logical request. Retrying with the same key and
            body returns the stored response for 24 hours instead of creating a
            second consultation; the same key with a different body is refused.
          required: true
          example: order-10045-submit-1
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConsultationCreateRequest'
            examples:
              example-1:
                summary: Illustrative request example
                value:
                  product_id: oral_finasteride
                  country: ES
                  plan_key: one_off
                  dosage: 1 mg
                  locale: es
                  schema_version: '412'
                  schema_hash: <HASH>
                  external_reference: order-10045
                  patient:
                    external_patient_id: cust-8812
                    email: patient@example.com
                    first_name: Sam
                    last_name: Example
                    date_of_birth: '1988-04-02'
                    sex: male
                    phone: '+34600000000'
                    address:
                      line1: Calle Example 1
                      line2: 2B
                      city: Madrid
                      postal_code: '28001'
                      country: ES
                    national_identifier:
                      type: dni
                      value: 12345678Z
                    consent:
                      terms_accepted: true
                      privacy_accepted: true
                      clinical_data_processing_accepted: true
                      accepted_at: '2026-11-03T10:10:00Z'
                      notice_version: pn-2026-11
                      evidence_reference: consent-log-55102
                      ip: 203.0.113.24
                  answers:
                    - question_key: hair_loss_duration
                      value: over_1_year
        required: true
      responses:
        '201':
          headers:
            Idempotent-Replayed:
              schema:
                type: string
                enum:
                  - 'true'
              description: >-
                true when the response is the stored one, replayed for a retry
                with the same Idempotency-Key and body; absent on the original
                response.
            X-RateLimit-Limit:
              schema:
                type: integer
              description: >-
                Requests allowed in the current window for the most restrictive
                limit that applies. Absent on refusals made before the throttles
                run (brand header, flag off, partner_not_live).
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left in the current window.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time, in seconds, when the window frees up.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Consultation'
              examples:
                example-1:
                  value:
                    id: 7f8b1c2e-4d5a-4e6f-9a0b-1c2d3e4f5a6b
                    object: consultation
                    mode: test
                    status: submitted
                    version: 1
                    status_history:
                      - status: submitted
                        version: 1
                        at: '2026-11-03T10:15:00Z'
                    external_reference: order-10045
                    product:
                      product_id: oral_finasteride
                      name: Oral finasteride
                    country: ES
                    plan_key: one_off
                    dosage: 1 mg
                    patient:
                      id: 0e1d2c3b-4a59-4687-8b9a-0b1c2d3e4f50
                      external_patient_id: cust-8812
                    submitted_at: '2026-11-03T10:15:00Z'
                    updated_at: '2026-11-03T10:15:00Z'
                    reviewer: null
                    decision: null
                    open_info_request: null
                    prescriptions: []
                    billing:
                      state: pending
                      price:
                        amount_cents: 2900
                        currency: EUR
                    cancellable: true
                  summary: Illustrative response example
          description: The new consultation.
        '400':
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: >-
                Requests allowed in the current window for the most restrictive
                limit that applies. Absent on refusals made before the throttles
                run (brand header, flag off, partner_not_live).
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left in the current window.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time, in seconds, when the window frees up.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerError'
              examples:
                example-1:
                  value:
                    error:
                      type: invalid_request
                      code: parse_error
                      message: >-
                        JSON parse error - Expecting value: line 1 column 1
                        (char 0)
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-2:
                  value:
                    error:
                      type: invalid_request
                      code: idempotency_key_required
                      message: This endpoint requires an Idempotency-Key header.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-3:
                  value:
                    error:
                      type: invalid_request
                      code: invalid_idempotency_key
                      message: Idempotency-Key must match [A-Za-z0-9_-]{8,128}.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-4:
                  value:
                    error:
                      type: invalid_request
                      code: brand_header_not_allowed
                      message: >-
                        Brand headers are not accepted on partner requests; the
                        brand comes from the API key.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
          description: >-
            The request is malformed. Partner routes also refuse the patient-app
            brand header with 400 brand_header_not_allowed, before
            authentication (no rate-limit headers).
        '401':
          headers:
            WWW-Authenticate:
              schema:
                type: string
              description: >-
                The authentication scheme to use: Bearer
                realm="hi-doctor-partner".
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerError'
              examples:
                example-1:
                  value:
                    error:
                      type: authentication_error
                      code: not_authenticated
                      message: Authentication credentials were not provided.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-2:
                  value:
                    error:
                      type: authentication_error
                      code: invalid_token
                      message: The API key is not valid.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-3:
                  value:
                    error:
                      type: authentication_error
                      code: key_revoked
                      message: The API key has been revoked.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-4:
                  value:
                    error:
                      type: authentication_error
                      code: key_expired
                      message: The API key has expired.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-5:
                  value:
                    error:
                      type: authentication_error
                      code: partner_suspended
                      message: The partner account is suspended.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
          description: >-
            The API key is missing, malformed, unknown, revoked or expired, or
            the partner account is suspended. The response carries
            WWW-Authenticate: Bearer realm="hi-doctor-partner" and no rate-limit
            headers.
        '403':
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: >-
                Requests allowed in the current window for the most restrictive
                limit that applies. Absent on refusals made before the throttles
                run (brand header, flag off, partner_not_live).
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left in the current window.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time, in seconds, when the window frees up.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerError'
              examples:
                example-1:
                  value:
                    error:
                      type: permission_error
                      code: insufficient_scope
                      message: This endpoint requires the consultations:write scope.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-2:
                  value:
                    error:
                      type: permission_error
                      code: partner_not_live
                      message: Live mode is not enabled for this partner.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
          description: >-
            The key lacks the consultations:write scope (insufficient_scope), or
            a live key was used while live mode is not enabled for the partner
            (partner_not_live).
        '404':
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: >-
                Requests allowed in the current window for the most restrictive
                limit that applies. Absent on refusals made before the throttles
                run (brand header, flag off, partner_not_live).
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left in the current window.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time, in seconds, when the window frees up.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/PartnerError'
                  - $ref: '#/components/schemas/PartnerFlagOffError'
              examples:
                example-1:
                  value:
                    error:
                      type: not_found
                  summary: Illustrative response example
          description: >-
            While the Partner API is switched off for the environment, every
            route answers 404 with only {"error": {"type": "not_found"}}, before
            authentication.
        '409':
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: >-
                Requests allowed in the current window for the most restrictive
                limit that applies. Absent on refusals made before the throttles
                run (brand header, flag off, partner_not_live).
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left in the current window.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time, in seconds, when the window frees up.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerError'
              examples:
                example-1:
                  value:
                    error:
                      type: conflict
                      code: duplicate_external_reference
                      message: >-
                        A consultation with this external_reference already
                        exists.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-2:
                  value:
                    error:
                      type: conflict
                      code: patient_conflict
                      message: An account with this email already exists for the brand.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-3:
                  value:
                    error:
                      type: conflict
                      code: patient_conflict
                      message: >-
                        The national identifier does not match the patient's
                        account.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-4:
                  value:
                    error:
                      type: conflict
                      code: patient_conflict
                      message: >-
                        The identity document does not match the patient's
                        account.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-5:
                  value:
                    error:
                      type: conflict
                      code: schema_outdated
                      message: The questionnaire schema has changed; fetch it again.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                      current:
                        schema_version: '413'
                        schema_hash: <HASH>
                  summary: Illustrative response example
                example-6:
                  value:
                    error:
                      type: conflict
                      code: consent_withdrawn
                      message: >-
                        The patient withdrew consent; no further consultations
                        are processed.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-7:
                  value:
                    error:
                      type: conflict
                      code: idempotency_in_progress
                      message: A request with this Idempotency-Key is still executing.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-8:
                  value:
                    error:
                      type: conflict
                      code: context_unsupported
                      message: >-
                        The submission needs a context the partner API does not
                        offer.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
          description: The request conflicts with the resource's current state.
        '422':
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: >-
                Requests allowed in the current window for the most restrictive
                limit that applies. Absent on refusals made before the throttles
                run (brand header, flag off, partner_not_live).
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left in the current window.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time, in seconds, when the window frees up.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerError'
              examples:
                example-1:
                  value:
                    error:
                      type: validation_error
                      code: invalid_request
                      message: The request body is invalid.
                      errors:
                        - field: answers
                          code: required
                          message: This field is required.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-2:
                  value:
                    error:
                      type: validation_error
                      code: invalid_request
                      message: The request body is invalid.
                      errors:
                        - field: patient.national_identifier
                          code: invalid_identifier
                          message: >-
                            Enter a valid identifier of a type the product
                            country uses.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-3:
                  value:
                    error:
                      type: validation_error
                      code: invalid_request
                      message: The request body is invalid.
                      errors:
                        - field: patient.identity_document
                          code: invalid_identity_document
                          message: >-
                            Enter a valid document number for this document type
                            and issuing state.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-4:
                  value:
                    error:
                      type: validation_error
                      code: invalid_request
                      message: The request body is invalid.
                      errors:
                        - field: patient.identity_document.type
                          code: invalid_choice
                          message: This is not a valid choice.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-5:
                  value:
                    error:
                      type: validation_error
                      code: invalid_request
                      message: The request body is invalid.
                      errors:
                        - field: patient.address
                          code: max_length
                          message: >-
                            line1 and line2 together must not exceed 200
                            characters.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-6:
                  value:
                    error:
                      type: validation_error
                      code: invalid_request
                      message: The request body is invalid.
                      errors:
                        - field: patient.address.country
                          code: residence_outside_eu
                          message: Country of residence must be within the EU
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-7:
                  value:
                    error:
                      type: validation_error
                      code: unexpected_question_key
                      message: >-
                        Only questions that apply after branching may be
                        answered.
                      errors:
                        - question_key: hair_loss_pattern
                          code: unexpected_question_key
                          message: >-
                            Only questions that apply after branching may be
                            answered.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-8:
                  value:
                    error:
                      type: validation_error
                      code: invalid_answers
                      message: >-
                        The answer to question hair_loss_duration does not match
                        the questionnaire schema.
                      errors:
                        - question_key: hair_loss_duration
                          code: invalid_answers
                          message: >-
                            The answer to question hair_loss_duration does not
                            match the questionnaire schema.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-9:
                  value:
                    error:
                      type: validation_error
                      code: out_of_range
                      message: A value is outside the schema's input bounds.
                      errors:
                        - question_key: body_measurements
                          field: height
                          code: out_of_range
                          message: The value is outside the allowed range.
                          min: 100
                          max: 250
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-10:
                  value:
                    error:
                      type: validation_error
                      code: upload_unusable
                      message: >-
                        The upload does not exist, has expired or was already
                        used.
                      errors:
                        - question_key: scalp_photo_top
                          code: upload_unusable
                          message: >-
                            The upload does not exist, has expired or was
                            already used.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-11:
                  value:
                    error:
                      type: validation_error
                      code: pharmacy_selection_invalid
                      message: >-
                        The pharmacy selection is not valid for this patient's
                        country.
                      errors:
                        - question_key: pharmacy_selection
                          code: pharmacy_selection_invalid
                          message: Choose a pharmacy from the directory.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-12:
                  value:
                    error:
                      type: validation_error
                      code: patient_identifier_required
                      message: >-
                        The patient's national identifier is required for this
                        country.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-13:
                  value:
                    error:
                      type: validation_error
                      code: consent_required
                      message: Every consent flag must be true.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-14:
                  value:
                    error:
                      type: validation_error
                      code: consent_notice_outdated
                      message: The patient notice version is not the current one.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                      current_notice_version: pn-2026-12
                  summary: Illustrative response example
                example-15:
                  value:
                    error:
                      type: validation_error
                      code: product_unavailable
                      message: >-
                        This product is not available for that country, plan or
                        dosage.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-16:
                  value:
                    error:
                      type: validation_error
                      code: country_conflict
                      message: >-
                        The answers name a country other than the product
                        country enabled for this partner.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-17:
                  value:
                    error:
                      type: validation_error
                      code: country_closed
                      message: New consultations are not accepted for this country.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-18:
                  value:
                    error:
                      type: validation_error
                      code: clinical_ineligibility
                      message: The answers do not meet the clinical eligibility rules.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
                example-19:
                  value:
                    error:
                      type: validation_error
                      code: idempotency_key_reuse
                      message: The Idempotency-Key was used with a different request.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
          description: The request body is invalid.
        '429':
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: >-
                Requests allowed in the current window for the most restrictive
                limit that applies. Absent on refusals made before the throttles
                run (brand header, flag off, partner_not_live).
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left in the current window.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time, in seconds, when the window frees up.
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerError'
              examples:
                example-1:
                  value:
                    error:
                      type: rate_limited
                      code: throttled
                      message: Request was throttled. Expected available in 12 seconds.
                      request_id: req_5f0c3a9d2b7e4f18a6c1d9e3b2a7f4c8
                  summary: Illustrative response example
          description: Too many requests; wait for the number of seconds in Retry-After.
      security:
        - PartnerApiKey: []
      x-codeSamples:
        - lang: Shell
          label: curl
          source: >
            set -eu

            umask 077

            # Path values must be URL-encoded; query string excludes the leading
            question mark.

            url="${HD_BASE_URL:?Set API
            origin}/v1/partner/consultations/${HD_QUERY_STRING:+?${HD_QUERY_STRING}}"

            printf 'Accept: application/json\n' >
            "${HD_REQUEST_HEADERS_FILE:?Private header file}"

            printf 'Authorization: Bearer %s\n' "${HD_PARTNER_API_KEY:?Set
            partner API key}" >> "$HD_REQUEST_HEADERS_FILE"

            # Create the Idempotency-Key once per logical request, store it
            before the first attempt and send the same value on every retry.

            printf 'Idempotency-Key: %s\n' "${HD_IDEMPOTENCY_KEY:?Set the stored
            Idempotency-Key; reuse it on retry}" >> "$HD_REQUEST_HEADERS_FILE"

            curl --silent --show-error --max-time 30 --request POST \
              --header "@$HD_REQUEST_HEADERS_FILE" \
              --dump-header "${HD_HEADERS_FILE:?Private response header file}" \
              --output "${HD_RESPONSE_FILE:?Private response file}" \
              --write-out '%{http_code}\n%{content_type}\n' \
              --header 'Content-Type: application/json' \
              --data-binary "@${HD_REQUEST_FILE:?JSON fixture file}" \
              "$url"
        - lang: Python
          label: requests
          source: >
            import os, json

            from pathlib import Path

            from urllib.parse import quote

            import requests

            os.umask(0o077)

            url = os.environ["HD_BASE_URL"].rstrip("/") +
            '/v1/partner/consultations/'

            if os.environ.get("HD_QUERY_STRING"): url += "?" +
            os.environ["HD_QUERY_STRING"]

            headers = {"Accept": 'application/json'}

            headers["Authorization"] = "Bearer " +
            os.environ["HD_PARTNER_API_KEY"]

            # Create the Idempotency-Key once per logical request, store it
            before the first attempt and send the same value on every retry.

            headers["Idempotency-Key"] = os.environ["HD_IDEMPOTENCY_KEY"]

            kwargs = {}

            kwargs["json"] =
            json.loads(Path(os.environ["HD_REQUEST_FILE"]).read_text())

            response = requests.request('POST', url, headers=headers,
            timeout=30, allow_redirects=False, **kwargs)

            Path(os.environ["HD_RESPONSE_FILE"]).write_bytes(response.content)

            Path(os.environ["HD_HEADERS_FILE"]).write_text(json.dumps(dict(response.headers)))

            print(response.status_code)

            print(response.headers.get("Content-Type", ""))
        - lang: JavaScript
          label: fetch
          source: >
            import { readFileSync, writeFileSync } from "node:fs";

            const env = process.env;

            let url = env.HD_BASE_URL.replace(/\/$/, "") +
            "/v1/partner/consultations/";

            if (env.HD_QUERY_STRING) url += "?" + env.HD_QUERY_STRING;

            const headers = { "Accept": "application/json" };

            headers.Authorization = "Bearer " + env.HD_PARTNER_API_KEY;

            // Create the Idempotency-Key once per logical request, store it
            before the first attempt and send the same value on every retry.

            headers["Idempotency-Key"] = env.HD_IDEMPOTENCY_KEY;

            const options = { method: "POST", headers, redirect: "manual",
            signal: AbortSignal.timeout(30000) };

            headers["Content-Type"] = "application/json";

            options.body = readFileSync(env.HD_REQUEST_FILE, "utf8");

            const response = await fetch(url, options);

            writeFileSync(env.HD_RESPONSE_FILE, Buffer.from(await
            response.arrayBuffer()), {mode: 0o600});

            writeFileSync(env.HD_HEADERS_FILE,
            JSON.stringify(Object.fromEntries(response.headers)), {mode:
            0o600});

            console.log(response.status);

            console.log(response.headers.get("content-type") ?? "");
components:
  schemas:
    ConsultationCreateRequest:
      type: object
      properties:
        product_id:
          type: string
          maxLength: 64
          minLength: 1
          pattern: \S
          description: Product id from GET /v1/partner/products.
        country:
          type: string
          maxLength: 3
          minLength: 1
          pattern: \S
          description: >-
            ISO 3166-1 alpha-2 code of a country your partner catalogue offers
            the product in.
        plan_key:
          type: string
          maxLength: 32
          minLength: 1
          pattern: \S
          description: One of the product's plans[].plan_key for the country.
        dosage:
          type: string
          default: ''
          maxLength: 32
          description: >-
            One of the product's dosages[].dosage, when the product lists
            dosages.
        locale:
          type: string
          default: ''
          maxLength: 2
          description: >-
            Two-letter language code saved as the language of a new patient
            account; empty uses your partner default.
        schema_version:
          type: string
          maxLength: 64
          minLength: 1
          pattern: \S
          description: schema_version of the questionnaire you rendered.
        schema_hash:
          type: string
          maxLength: 64
          minLength: 1
          pattern: \S
          description: >-
            schema_hash of the questionnaire you rendered, with or without a
            locale.
        external_reference:
          type: string
          default: ''
          maxLength: 128
          description: Your order or case reference, unique per partner and mode.
        patient:
          allOf:
            - $ref: '#/components/schemas/PatientInput'
          description: Patient identity, consent and optional national identifier.
        answers:
          type: array
          items:
            $ref: '#/components/schemas/ConsultationAnswerEntry'
          minItems: 1
          description: >-
            The patient's answers as entries-v1 entries keyed by question_key.
            Answer only the steps that apply after branching.
      required:
        - answers
        - country
        - patient
        - plan_key
        - product_id
        - schema_hash
        - schema_version
    Consultation:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The consultation id.
        object:
          allOf:
            - $ref: '#/components/schemas/ConsultationObjectEnum'
          description: Always consultation.
        mode:
          allOf:
            - $ref: '#/components/schemas/ModeEnum'
          description: test or live, from the API key.
        status:
          allOf:
            - $ref: '#/components/schemas/ConsultationStatusEnum'
          description: Current consultation status.
        version:
          type: integer
          description: >-
            Increases with every change to the consultation's public state, i.e.
            every field of this object except version, status_history,
            updated_at and product.name (catalogue copy: a product rename does
            not advance it). Events carry the same number.
        status_history:
          type: array
          items:
            $ref: '#/components/schemas/StatusHistoryEntry'
          description: Every status change, oldest first.
        external_reference:
          type: string
          nullable: true
          description: Your order or case reference; null when none was sent.
        product:
          allOf:
            - $ref: '#/components/schemas/ConsultationProduct'
          description: The product.
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code.
        plan_key:
          type: string
          nullable: true
          description: The chosen plan.
        dosage:
          type: string
          nullable: true
          description: The chosen dosage; null when the product has none.
        patient:
          allOf:
            - $ref: '#/components/schemas/ConsultationPatientRef'
          description: The patient.
        submitted_at:
          type: string
          format: date-time
          description: When the consultation was submitted.
        updated_at:
          type: string
          format: date-time
          description: When it last changed.
        reviewer:
          description: Present once the reviewing doctor has opened the case.
          anyOf:
            - $ref: '#/components/schemas/Reviewer'
            - enum:
                - null
              type: string
              nullable: true
        decision:
          description: The clinical decision; null until one is recorded.
          anyOf:
            - $ref: '#/components/schemas/Decision'
            - enum:
                - null
              type: string
              nullable: true
        open_info_request:
          description: The open information request; null when there is none.
          anyOf:
            - $ref: '#/components/schemas/OpenInfoRequest'
            - enum:
                - null
              type: string
              nullable: true
        prescriptions:
          type: array
          items:
            $ref: '#/components/schemas/Prescription'
          description: Prescriptions issued for the consultation.
        billing:
          allOf:
            - $ref: '#/components/schemas/Billing'
          description: Billing state and price of the consultation.
        cancellable:
          type: boolean
          description: >-
            true exactly when POST /consultations/{consultation_id}/cancel would
            succeed now: the consultation is submitted, has no prescription, and
            (live) no doctor has opened it, no decision or electronic
            prescription signing has started, and no prescription issuance or
            replacement is in flight.
      required:
        - billing
        - cancellable
        - country
        - decision
        - dosage
        - external_reference
        - id
        - mode
        - object
        - open_info_request
        - patient
        - plan_key
        - prescriptions
        - product
        - reviewer
        - status
        - status_history
        - submitted_at
        - updated_at
        - version
    PartnerError:
      type: object
      properties:
        error:
          allOf:
            - $ref: '#/components/schemas/PartnerErrorDetail'
          description: The error.
      required:
        - error
    PartnerFlagOffError:
      type: object
      description: >-
        The reduced body every route answers while the Partner API is switched
        off for the environment: no code, message or request_id.
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum:
                - not_found
              description: Always not_found.
          required:
            - type
          additionalProperties: false
          description: Only the error type; no code, message or request_id.
      required:
        - error
      additionalProperties: false
    PatientInput:
      type: object
      properties:
        external_patient_id:
          type: string
          maxLength: 128
          minLength: 1
          pattern: \S
          description: Your stable identifier for the patient.
        email:
          type: string
          format: email
          minLength: 1
          pattern: \S
          description: The patient's own email address.
        first_name:
          type: string
          maxLength: 150
          minLength: 1
          pattern: \S
          description: Legal first name.
        last_name:
          type: string
          maxLength: 150
          minLength: 1
          pattern: \S
          description: Legal last name.
        date_of_birth:
          type: string
          format: date
          description: Date of birth (YYYY-MM-DD).
        sex:
          allOf:
            - $ref: '#/components/schemas/SexEnum'
          description: female, male, other or unspecified.
        phone:
          type: string
          maxLength: 16
          description: Phone number in E.164 format.
        address:
          allOf:
            - $ref: '#/components/schemas/AddressInput'
          description: The patient's postal address.
        national_identifier:
          allOf:
            - $ref: '#/components/schemas/NationalIdentifierInput'
          description: >-
            Optional. The patient's national identifier for the product country,
            needed where that country's prescribing requires one (without it, a
            submission is refused with 422 patient_identifier_required unless
            the identifier is already stored for the patient). A type the
            product country does not use, or a value that fails validation, is
            refused with 422 invalid_request and an error item with field
            patient.national_identifier and code invalid_identifier; a missing,
            blank or too long type or value keeps its own field error instead
            (for example field patient.national_identifier.value, code required
            or max_length); the submitted value is never echoed in errors or
            returned in any response or event. Live mode stores it on the
            patient's account the first time; a later submission for the same
            patient with a different identifier is refused with 409
            patient_conflict and never overwrites the stored one. Test mode
            validates it and stores nothing.
        identity_document:
          allOf:
            - $ref: '#/components/schemas/IdentityDocumentInput'
          description: >-
            Optional. The patient's identity document: a national id_card or a
            passport, with the country that issued it. Electronic prescribing in
            some countries needs it before a doctor can approve. It is validated
            like a patient's own profile: every number must be 5 to 20 letters
            or digits after spaces, dots and hyphens are removed, and for a few
            issuing countries an id_card number is also checked against its
            national format and check character. A document that breaks these
            rules (unknown issuing state, wrong check character, number format)
            is refused with 422 invalid_request and an error item with field
            patient.identity_document and code invalid_identity_document. A
            structural error keeps its nested field error 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. It is never echoed in errors or returned in any
            response or event. Live mode stores it on the patient's account the
            first time; a later submission for the same patient with a different
            document is refused with 409 patient_conflict and never overwrites
            the stored one. Test mode validates it and stores nothing. If the
            reviewing doctor needs it later, they ask with the information
            request reason identity_document_required.
        consent:
          allOf:
            - $ref: '#/components/schemas/ConsentInput'
          description: The patient's consent, captured by you before submission.
      required:
        - consent
        - date_of_birth
        - email
        - external_patient_id
        - first_name
        - last_name
        - sex
    ConsultationAnswerEntry:
      type: object
      description: >-
        One entries-v1 answer: question_key plus exactly one payload, value,
        values or skip, as the question kind

        requires.
      properties:
        question_key:
          type: string
          description: >-
            A question key from the questionnaire schema. Required, a non-empty
            string: an entry without one is refused with 422 invalid_answers.
          minLength: 1
          pattern: \S
        value:
          description: >-
            The answer for option, confirm, upload and scalar kinds: an option
            value, a list of option values for multi kinds, true for a confirm,
            or an upload_id for an upload question.
        values:
          type: object
          additionalProperties: {}
          description: >-
            The answer for input, iief5 and likert-grid kinds: an object of
            field values, for example {"height": 172, "weight": 96}.
        skip:
          allOf:
            - $ref: '#/components/schemas/SkipEnum'
          description: >-
            true to skip an optional question (required false). A skipped entry
            carries only its locator (question_key, or on an information-request
            answer its alias key), skip and, optionally, the question's own
            step_id: value, values, option_inputs or any other member alongside
            it is refused with 422 invalid_answers.
        option_inputs:
          type: object
          additionalProperties:
            oneOf:
              - type: string
              - type: number
              - type: boolean
          description: Extra input for options that require it, keyed by option value.
      required:
        - question_key
      anyOf:
        - not:
            required:
              - skip
        - properties:
            question_key:
              description: The question key of the skipped question.
            step_id:
              description: 'Optional: the skipped question''s own step_id.'
            skip:
              description: 'true: the question is skipped.'
          additionalProperties: false
    ConsultationObjectEnum:
      enum:
        - consultation
      type: string
    ModeEnum:
      enum:
        - live
        - test
      type: string
    ConsultationStatusEnum:
      enum:
        - submitted
        - in_review
        - info_requested
        - approved
        - rejected
        - cancelled
      type: string
    StatusHistoryEntry:
      type: object
      properties:
        status:
          allOf:
            - $ref: '#/components/schemas/ConsultationStatusEnum'
          description: The status entered.
        version:
          type: integer
          description: Consultation version at this status.
        at:
          type: string
          format: date-time
          description: When the consultation entered this status.
      required:
        - at
        - status
        - version
    ConsultationProduct:
      type: object
      properties:
        product_id:
          type: string
          description: Product id from GET /v1/partner/products.
        name:
          type: string
          description: Product display name.
      required:
        - name
        - product_id
    ConsultationPatientRef:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The patient id.
        external_patient_id:
          type: string
          description: Your stable identifier for the patient.
      required:
        - external_patient_id
        - id
    Reviewer:
      type: object
      properties:
        description:
          type: string
          description: Who reviewed the consultation.
        licence_country:
          type: string
          nullable: true
          description: >-
            ISO 3166-1 alpha-2 country of the reviewing doctor's licence; null
            when not recorded.
      required:
        - description
        - licence_country
    Decision:
      type: object
      properties:
        outcome:
          allOf:
            - $ref: '#/components/schemas/DecisionOutcomeEnum'
          description: approved or rejected.
        reason_category:
          oneOf:
            - $ref: '#/components/schemas/ReasonCategoryEnum'
            - $ref: '#/components/schemas/NullEnum'
          description: Category of a rejection; null otherwise.
        decided_at:
          type: string
          format: date-time
          nullable: true
          description: When the decision was recorded.
      required:
        - decided_at
        - outcome
        - reason_category
    OpenInfoRequest:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The information request id.
        reason_code:
          allOf:
            - $ref: '#/components/schemas/ReasonCodeEnum'
          description: Why the doctor needs more information.
        requested_at:
          type: string
          format: date-time
          nullable: true
          description: When the doctor raised the request.
      required:
        - id
        - reason_code
        - requested_at
    Prescription:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The prescription id.
        consultation_id:
          type: string
          format: uuid
          nullable: true
          description: The consultation this belongs to.
        status:
          allOf:
            - $ref: '#/components/schemas/PrescriptionStatusEnum'
          description: >-
            issued until the prescription is cancelled. It does not say whether
            the prescription can still be used: read active, and compare
            expires_at yourself.
        active:
          type: boolean
          description: >-
            false once the prescription is cancelled or no longer published to
            the patient, for example after a dispensing is recorded during a
            manual recovery. It does not check expiry: an expired prescription
            can stay active true, so compare expires_at yourself.
        issued_at:
          type: string
          format: date-time
          nullable: true
          description: When the prescription was issued.
        cancelled_at:
          type: string
          format: date-time
          nullable: true
          description: When the prescription was cancelled; null if it was not.
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            When the prescription expires, or null when not recorded. Always
            null in test mode.
        dispensing_country:
          type: string
          nullable: true
          description: >-
            ISO 3166-1 alpha-2 country where the prescription is dispensed, or
            null when not recorded. Always null in test mode.
        medication:
          allOf:
            - $ref: '#/components/schemas/Medication'
          description: The prescribed medication.
        fulfilment:
          allOf:
            - $ref: '#/components/schemas/Fulfilment'
          description: How the patient obtains the medication.
        collected_at:
          type: string
          format: date-time
          nullable: true
          description: When the medication was collected, when known; otherwise null.
        replaced_by:
          type: string
          format: uuid
          nullable: true
          description: The prescription that replaced this one.
      required:
        - active
        - cancelled_at
        - collected_at
        - consultation_id
        - dispensing_country
        - expires_at
        - fulfilment
        - id
        - issued_at
        - medication
        - replaced_by
        - status
    Billing:
      type: object
      properties:
        state:
          oneOf:
            - $ref: '#/components/schemas/BillingStateEnum'
            - $ref: '#/components/schemas/NullEnum'
          description: pending, billable, void or reversed.
        price:
          allOf:
            - $ref: '#/components/schemas/Money'
          description: Price agreed for the consultation.
      required:
        - price
        - state
    PartnerErrorDetail:
      type: object
      properties:
        type:
          allOf:
            - $ref: '#/components/schemas/PartnerErrorDetailTypeEnum'
          description: >-
            Error type, for example validation_error or rate_limited; see Errors
            in the partner guides.
        code:
          type: string
          description: A stable, machine-readable error code.
        message:
          type: string
          description: A human-readable explanation; do not parse it.
        errors:
          type: array
          items:
            $ref: '#/components/schemas/PartnerErrorItem'
          description: Field-level problems, when any.
        request_id:
          type: string
          description: Quote this identifier when you contact support.
        current:
          allOf:
            - $ref: '#/components/schemas/SchemaVersion'
          description: >-
            On 409 schema_outdated: the schema_version and schema_hash currently
            published.
        current_notice_version:
          type: string
          description: >-
            On 422 consent_notice_outdated: the patient notice version to show
            and record.
      required:
        - code
        - message
        - request_id
        - type
    SexEnum:
      enum:
        - female
        - male
        - other
        - unspecified
      type: string
    AddressInput:
      type: object
      properties:
        line1:
          type: string
          description: >-
            line1 and line2 are stored as one street line, joined with ", ";
            together (separator included) they must not exceed 200 characters,
            or the request is refused with 422 invalid_request (field
            patient.address). Nothing is truncated.
          maxLength: 200
        line2:
          type: string
          description: >-
            line1 and line2 are stored as one street line, joined with ", ";
            together (separator included) they must not exceed 200 characters,
            or the request is refused with 422 invalid_request (field
            patient.address). Nothing is truncated.
          maxLength: 200
        city:
          type: string
          maxLength: 120
          description: City.
        postal_code:
          type: string
          maxLength: 20
          description: Postal code.
        country:
          type: string
          description: >-
            Country of residence, ISO 3166-1 alpha-2 or alpha-3. It must be one
            of the 27 EU member states (AT, BE, BG, CY, CZ, DE, DK, EE, ES, FI,
            FR, GR, HR, HU, IE, IT, LT, LU, LV, MT, NL, PL, PT, RO, SE, SI, SK).
            Defaults to the request country when absent or blank. A well-formed
            code outside the EU is refused with 422 invalid_request and an error
            item with field patient.address.country and code
            residence_outside_eu. A malformed value (null, or longer than 3
            characters) fails field validation first: 422 invalid_request with
            the same field and the field validation code (null or max_length).
          maxLength: 3
    NationalIdentifierInput:
      type: object
      description: >-
        A national identifier, validated against the product country's
        identifier rules.
      properties:
        type:
          type: string
          description: >-
            An identifier type of the product country (not the residence
            country). Types by country: BE: niss, eid_card_number; ES: dni, nie;
            IE: ppsn; IT: codice_fiscale, stp, eni; PT: nif; SE: personnummer.
          maxLength: 32
          minLength: 1
          pattern: \S
        value:
          type: string
          description: >-
            The identifier as printed; it is validated (format and check digits)
            and normalised.
          maxLength: 32
          minLength: 1
          pattern: \S
      required:
        - type
        - value
    IdentityDocumentInput:
      type: object
      description: >-
        An identity document, validated with the same rules as a patient's own
        profile.
      properties:
        type:
          allOf:
            - $ref: '#/components/schemas/IdentityDocumentInputTypeEnum'
          description: passport or id_card.
        number:
          type: string
          description: The document number as printed; it is normalised.
          maxLength: 50
          minLength: 1
          pattern: \S
        issuing_state:
          type: string
          description: >-
            ISO 3166-1 alpha-2 or alpha-3 code of the country that issued the
            document.
          maxLength: 3
          minLength: 1
          pattern: \S
      required:
        - issuing_state
        - number
        - type
    ConsentInput:
      type: object
      properties:
        terms_accepted:
          type: boolean
          enum:
            - true
          description: >-
            The patient accepted Hi Doctor's terms for the consultation. Must be
            true.
        privacy_accepted:
          type: boolean
          enum:
            - true
          description: The patient was shown Hi Doctor's privacy notice. Must be true.
        clinical_data_processing_accepted:
          type: boolean
          enum:
            - true
          description: >-
            The patient agreed that their health data is processed for the
            consultation. Must be true.
        accepted_at:
          type: string
          format: date-time
          description: When the patient gave consent (ISO 8601).
        notice_version:
          type: string
          maxLength: 32
          minLength: 1
          pattern: \S
          description: >-
            The version of the patient notice you showed; must be the current
            one.
        evidence_reference:
          type: string
          maxLength: 128
          description: Your reference to the stored consent record.
        ip:
          type: string
          nullable: true
          anyOf:
            - format: ipv4
            - format: ipv6
          description: >-
            The IPv4 or IPv6 address the patient accepted from, or null; any
            other value is refused with 422 invalid_request.
      required:
        - accepted_at
        - clinical_data_processing_accepted
        - notice_version
        - privacy_accepted
        - terms_accepted
    SkipEnum:
      enum:
        - true
      type: boolean
    DecisionOutcomeEnum:
      enum:
        - approved
        - rejected
      type: string
    ReasonCategoryEnum:
      enum:
        - safety
        - not_eligible
      type: string
    NullEnum:
      enum:
        - null
      type: string
      nullable: true
    ReasonCodeEnum:
      enum:
        - answer_unclear
        - answer_inconsistent
        - photo_quality
        - photo_missing
        - measurement_check
        - medication_history
        - medical_history
        - identity_check
        - document_missing
        - identity_document_required
        - other_clarification
      type: string
    PrescriptionStatusEnum:
      enum:
        - issued
        - cancelled
      type: string
    Medication:
      type: object
      properties:
        name:
          type: string
          description: Medication name.
        prescribed_label:
          type: string
          description: The medication as prescribed.
        dosage:
          type: string
          nullable: true
          description: Prescribed dosage.
        supply_days:
          type: integer
          nullable: true
          description: Days of supply prescribed.
      required:
        - dosage
        - name
        - prescribed_label
        - supply_days
    Fulfilment:
      type: object
      properties:
        route:
          allOf:
            - $ref: '#/components/schemas/FulfilmentRouteEnum'
          description: How the patient obtains the medication; see the fulfilment guide.
      required:
        - route
    BillingStateEnum:
      enum:
        - pending
        - billable
        - void
        - reversed
      type: string
    Money:
      type: object
      properties:
        amount_cents:
          type: integer
          description: Amount in the currency's minor unit.
        currency:
          type: string
          description: ISO 4217 currency code.
      required:
        - amount_cents
        - currency
    PartnerErrorDetailTypeEnum:
      enum:
        - api_error
        - authentication_error
        - conflict
        - invalid_request
        - not_found
        - permission_error
        - rate_limited
        - validation_error
      type: string
    PartnerErrorItem:
      type: object
      properties:
        field:
          type: string
          nullable: true
          description: The request field the problem refers to, or null when none.
        question_key:
          type: string
          nullable: true
          description: >-
            The questionnaire question the problem refers to, or null when the
            entry named none.
        code:
          type: string
          description: Machine-readable error code for this item.
        message:
          type: string
          description: Human-readable message. It never repeats the submitted value.
        min:
          type: number
          format: double
          description: 'On out_of_range: the lowest allowed value, when the schema sets one.'
        max:
          type: number
          format: double
          description: >-
            On out_of_range: the highest allowed value, when the schema sets
            one.
      required:
        - code
        - message
    SchemaVersion:
      type: object
      properties:
        schema_version:
          type: string
          description: The published schema_version.
        schema_hash:
          type: string
          description: A hash accepted for the published version.
      required:
        - schema_hash
        - schema_version
    IdentityDocumentInputTypeEnum:
      enum:
        - passport
        - id_card
      type: string
    FulfilmentRouteEnum:
      enum:
        - national_e_prescription
        - signed_pdf
        - pharmacy_delivery
        - cross_border_pdf
      type: string
  securitySchemes:
    PartnerApiKey:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: >-
        Partner API key sent as Authorization: Bearer <api key>. Test keys and
        live keys are separate.

````

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