Clinical note generation


Generates a clinical note by intelligently filling a structured template with information extracted from a raw text (stt, vtt, plain-text,…) transcription.

Below we will describe the entire process that allows the successful completion of note generation.

Request description

Endpoint: /api/v1/generate-clinical-note
Method: POST

Remember: you can see the URL to consume this API in the Introduction section.

{
  "Content-Type": "application/json",
  "Authorization": "Bearer <accessToken>"
}

Authorization: We'll pass the token obtained during the authentication process. You can reuse the generated tokens, as they have a 2-hour lifespan. Review this page: Credentials

To set the API version on a specific request, you can add a specific header named X-Invox-Medical-Api-Version with the value of the version you are targeting:

X-Invox-Medical-Api-Version: 2026-10-01

Payload structure

type GenerateReportPayloadType = {
  template: IAPITemplate; // Template used to generate the report
  transcription: string; // Transcription used as input to the report
  consultationSessionId?: string; // Optional consultation session ID to provide context for the identification of a clinical notes and assets related to the same consultation
};

IAPITemplate and APITemplateField below are generated from the published contract template-definition v1 (see the links after this block).

export interface IAPITemplate {
  // Optional template identifier. Free text, not required to be a UUID.
  id?: string;
  // Template name. At most 64 characters once trimmed.
  template_name: string;
  // Template description. At most 256 characters once trimmed.
  template_description: string;
  // Language of the template.
  template_language: "es_ES" | "ca_ES" | "pt_PT" | "pt_BR" | "en_GB" | "en_US" | "va" | "gl" | "eu";
  // Fields to fill. At least one. The id of each field is unique.
  fields: APITemplateField[];
}

export interface APITemplateField {
  // Field identifier: text with at least one non-whitespace character, unique in the template. A UUID is recommended but not required.
  id: string;
  // Field name: text with at least one non-whitespace character.
  name: string;
  // Type of the value to generate. Lower case.
  type: "string" | "number" | "boolean" | "array" | "enum";
  // Optional instructions for the field.
  description?: string;
  // Allowed options. Required, as a non-empty array, when type is enum.
  enumerableOptions?: string[];
  // Whether an enum field takes one option or several. Documented, not validated in v1.
  enumSelection?: "single" | "multi";
  // Optional context for the field.
  context?: string;
  // Optional history for the field.
  history?: unknown[];
}

For more details related to the template structure, use the versioned JSON Schemas published by the API:

  • Template definition: <baseUrl>/api/v1/contracts/template-definition/v1
  • Report output: <baseUrl>/api/v1/contracts/report-output/v1

Both are public, need no Authorization header and are immutable: a change is published as a new version (v2). The unversioned JSON Schema stored in S3 is legacy and is no longer updated.

Responses

Correct response

Successful request

Describe the characteristics of a satisfactory response

200

Response example:

{
  "isSuccessRequest": true,
  "templateFields": [
    {
      "id": "field-001",
      "value": "Dolor de cabeza intenso desde hace 3 días.",
      "name": "chief_complaint",
      "type": "string"
    },
    {
      "id": "field-002",
      "value": ["Ibuprofeno 400mg", "Paracetamol 1g"],
      "name": "current_medication",
      "type": "array"
    }
  ]
}
{
  "isSuccessRequest": true,
  "templateFields": [
    {
      "id": "field-001",
      "value": "Dolor de cabeza intenso desde hace 3 días.",
      "name": "chief_complaint",
      "type": "string"
    },
    {
      "id": "field-002",
      "value": ["Ibuprofeno 400mg", "Paracetamol 1g"],
      "name": "current_medication",
      "type": "array"
    }
  ],
  "generationMetadata": {
    "partial": false,
    "partialFailureCount": 0,
    "failedFields": [],
    "statusByField": {
      "field-001__chief_complaint": "SUCCESS",
      "field-002__current_medication": "SUCCESS"
    },
    "recommendation": "NONE"
  }
}
{
  "isSuccessRequest": true,
  "templateFields": [
    {
      "id": "field-001",
      "value": "Dolor de cabeza intenso desde hace 3 días.",
      "name": "chief_complaint",
      "type": "string"
    },
    {
      "id": "field-002",
      "value": [],
      "name": "current_medication",
      "type": "array"
    }
  ],
  "generationMetadata": {
    "partial": true,
    "partialFailureCount": 1,
    "failedFields": [
      {
        "fieldKey": "field-002__current_medication",
        "reasonCode": "REQUEST_TIMEOUT",
        "retryable": true
      }
    ],
    "statusByField": {
      "field-001__chief_complaint": "SUCCESS",
      "field-002__current_medication": "DEFAULTED"
    },
    "recommendation": "RETRY_RECOMMENDED"
  }
}
Response structure:

These types are generated from the published contract report-output v1. The value of a number or boolean field is null when the model could not extract it, and a field may carry extra properties.

// Additional properties may be present.
export type ReportField =
  | { id: string; name: string; type: "string"; value: string }
  | { id: string; name: string; type: "number"; value: number | null }
  | { id: string; name: string; type: "boolean"; value: boolean | null }
  | { id: string; name: string; type: "array"; value: string[] }
  | { id: string; name: string; type: "enum"; value: string | string[] };

export type GenerateReportResponseType = {
  isSuccessRequest: true;
  templateFields: ReportField[];
};

These types are generated from the published contract report-output v1. The value of a number or boolean field is null when the model could not extract it, and a field may carry extra properties.

// Additional properties may be present.
export type ReportField =
  | { id: string; name: string; type: "string"; value: string }
  | { id: string; name: string; type: "number"; value: number | null }
  | { id: string; name: string; type: "boolean"; value: boolean | null }
  | { id: string; name: string; type: "array"; value: string[] }
  | { id: string; name: string; type: "enum"; value: string | string[] };

export type FieldGenerationFailure = {
  // Failed field, as <id>__<name>.
  fieldKey: string;
  // Why the field failed.
  reasonCode: "REQUEST_TIMEOUT" | "FIELD_PROCESSING_ERROR" | "VALIDATION_FAILED" | "UNKNOWN";
  // Whether a new request can recover the field.
  retryable: boolean;
};

export type GenerationMetadata = {
  // true when some fields failed and were returned with a default value.
  partial: boolean;
  // Number of fields that failed.
  partialFailureCount: number;
  // One entry per failed field.
  failedFields: FieldGenerationFailure[];
  // Status of each field, keyed by <id>__<name>: SUCCESS (generated) or DEFAULTED (returned with its default value).
  statusByField: Record<string, "SUCCESS" | "DEFAULTED">;
  // NONE, or RETRY_RECOMMENDED when a new request is likely to recover failed fields.
  recommendation: "NONE" | "RETRY_RECOMMENDED";
};

export type GenerateReportResponseType = {
  isSuccessRequest: true;
  templateFields: ReportField[];
  // Outcome of the generation per field. Always present from 2026-08-01.
  generationMetadata: GenerationMetadata;
};

Wrong responses

Unauthorized request

Describes the response when the request is not authorized


401

Response body

type GenerateReportResponseType = {
  message: string;
};


Forbidden request

Describes the response when the API Key is not allowed to consume this service


403

Response body

type GenerateReportResponseType = {
  message: string;
  errorType: string;
};

Remember that to consume this service, the API Key must have the permission: GENERATE-REPORT

Bad request

Describe the features of a bad request

400

Response bodyerrorType is optional: it is absent when the request has no body.

type GenerateReportResponseType = {
    message: string
    errorType?: GENERATE_REPORT_ERROR_TYPE
}

enum GENERATE_REPORT_ERROR_TYPE {
    INVALID_LANGUAGE= 'invalidLanguage'
    INVALID_TRANSCRIPTION = 'invalidTranscription',
    EMPTY_TRANSCRIPTION = 'emptyTranscription',
    INVALID_TEMPLATE_NAME_LENGTH = 'invalidTemplateNameLength', // Max: 64 Characters
    INVALID_TEMPLATE_DESCRIPTION_LENGTH= 'invalidTemplateDescriptionLength', // Max: 256 Charachers
    EMPTY_TEMPLATE_FIELDS= 'emptyTemplateFields',
    ALL_FIELDS_MUST_HAVE_TYPE= 'allFieldsMustHaveType',
    FIELDS_WITH_INVALID_TYPE = 'fieldsWithInvalidType',
    ALL_FIELDS_MUST_HAVE_ID_AND_NAME= 'allFieldsMustHaveIdAndName',
    ALL_IDS_MUST_BE_UNIQUE= 'allIdsMustBeUnique',
    ENUMERABLE_OPTIONS_ARE_MISSING_OR_EMPTY_FOR_ENUM= 'enumerableOptionsAreMissingOrEmptyForEnum',
    ERROR_GENERATING_MEDICAL_REPORT='errorGeneratingMedicalReport'
}

From version 2026-11-01 (and when the version header is omitted), a template with a structural defect that used to end in a 500 (for example a missing template_name or template_description, or a value that is not a string) is rejected with 400 and errorType invalidTemplateSchema. The message has the format Template schema validation failed: <path>: <rule>, where <path> is the location in the template and <rule> the rule that failed. It never includes the values you sent. A missing transcription is also a 400 (invalidTranscription).

enum GENERATE_REPORT_ERROR_TYPE {
    // ... same values as above, plus:
    INVALID_TEMPLATE_SCHEMA = 'invalidTemplateSchema',
}

Remember that the valid languages are: es_ES, ca_ES, pt_PT, pt_BR, en_GB, en_US, gl, eu, va
Remember that the valid types are: string, number, boolean, array, enum