Future Technologies Company
Submit a consultation recording, poll until it is ready, and receive a structured clinical note for your record system. This page is the reference; every other path on this host returns JSON.
This host serves machines. There is no sign-in and no interface here. If you are looking for the clinician application, it is on a different address, and your FTC contact will give it to you.
Your system posts a recording and gets an identifier back in under a second. Transcription and note generation happen in the background, so nothing waits on a long request. You poll the identifier until the note is ready, typically within about thirty-five seconds.
The answer comes back in two halves that cannot be confused with each other.
note is what belongs in the patient record. advisory is what a
clinician reads before signing: warnings, drug register verdicts, and the transcript.
Nothing under advisory may be written into a patient record.
It is decision support, not a clinical conclusion. The two halves are separate keys
precisely so that writing response["note"] into your record is the correct
thing to do and needs no filtering on your side.
Every request carries the API key FTC issued you, in either header:
Authorization: Bearer ftcs_a1b2c3d4_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X-API-Key: ftcs_a1b2c3d4_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Keys are 46 characters: a fixed ftcs label, an 8-character identifier, and a
32-character secret. The identifier is safe to log and is how we tell your keys apart. The
secret is shown once, when the key is issued, and is stored only as a hash, so we cannot
recover it for you. If a key is lost, ask for a new one and the old one is revoked.
Every rejection returns the same 401 with the same body, whether the key is
unknown, wrong, revoked, or belongs to a suspended account. That is deliberate, so the
response cannot be used to work out which keys exist.
Send multipart/form-data:
| Field | Required | Meaning |
|---|---|---|
| audio | yes | The consultation recording. Up to 90 MB. |
| mrn | no | Your patient identifier, kept on the audit record. |
| patient_sex | no | Male or Female. Omitted means the sex-based safety checks do not run. |
| patient_age | no | Age in years. Omitted means the age-based safety checks do not run. |
| language | no | ar, en, or omit. The recording is always recognised as Arabic and English together, so a consultation that switches language is heard whole; this value only says which language was mainly spoken. The note is written in English; anything that could not be translated is flagged in advisory.warnings. |
Send an Idempotency-Key header. If you repeat a request with the same key, you
get the original submission back rather than a second consultation. Keys are scoped to your
account, so they cannot collide with another hospital's.
Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7
202 Accepted, with a Location header pointing at the poll URL.
{
"id": "0b7f3d5a-9c21-4a8e-b4f1-3d2c9a8e7f06",
"status": "queued",
"stage": "received",
"percent": 0
}
Poll every few seconds. note and advisory are empty objects until
status becomes ready.
| Field | Meaning |
|---|---|
| status | queued, running, ready or failed. The last two are final. |
| stage | received, transcribing, drafting, ready. |
| percent | 0, 15, 40 or 100. |
| elapsed_ms | Milliseconds since you submitted. |
| typical_total_ms | 35000. What a normal consultation takes. |
| note | The record fields. See below. |
| advisory | Review material. Never written to a record. |
percent is fixed per stage, not measured. It tells you which
stage the work has reached and nothing about how long the rest will take. Showing it as a
progress bar is fine; treating it as a time estimate is not.
A failed job keeps the stage and percent it stopped at, so a failure can
report drafting and 40. Read status, never
percent, to decide whether the work finished.
The poll still returns 200, with status: "failed" and an
error object carrying code, message and
retryable. Retry only when retryable is true.
| code | Retryable | What it means |
|---|---|---|
| no_speech_detected | no | There was no speech in the recording. |
| engine_restarted | yes | Interrupted before it finished. Send it again. |
| speech_unavailable | yes | A fault at our end, not a problem with your recording. |
| speech_not_configured | no | Misconfiguration at our end. Retrying will not help; contact us. |
| internal_error | no | Something else went wrong. |
Eleven fields, named to match the Patient Episode form. A field the consultation did not
cover may be absent or present and null, so treat the two the same.
The one exception is allergies: in the note this API returns it is always present
and never null. What a clinician later signs in your own system is theirs to edit.
| Field | Type | Contents |
|---|---|---|
| primary_chief_complaint | text | The main presenting complaint, stated concisely. |
| onset_course_duration | text | When it started, how it progressed, for how long. |
| other_complaints | text | Secondary complaints or associated symptoms. |
| present_illness | text | The history of present illness. |
| allergies | text | One of three values: the allergies stated; exactly "Denied (patient or companion states no known allergies)" when the patient, or a relative or companion answering for them, was recorded as asked and saying none; or exactly "Not discussed" when the note records no allergy statement at all (including an empty or placeholder answer). Always present. These values report what the note records from the conversation; they are not a separate check against the transcript, so the clinician confirms them. |
| smoker | text | "Yes", "No", or "Not mentioned". |
| investigations_ordered | text | Tests, imaging and procedures ordered, with any stated timing. |
| medications_prescribed | text | Drugs prescribed, changed or stopped, with dose, route and frequency. |
| current_medications | list | Medicines the patient already takes, unchanged at this visit. |
| new_prescriptions | list | Medicines prescribed, changed or stopped at this visit. |
| plan_follow_up | text | Advice given and the follow-up interval. |
Each entry in the two medication lists:
{
"name_as_said": "Metformin",
"dose": "500 mg",
"route": "oral",
"frequency": "twice daily",
"duration": "for 3 months",
"evidence_segments": [14, 15]
}
name_as_said is the drug exactly as the clinician named it, before any
register lookup. evidence_segments point at the numbered transcript in
advisory.segments, so any entry can be checked against what was said.
allergies distinguishes a denial from silence.
"Denied (patient or companion states no known allergies)" means the note records the question
as asked and answered no, by the patient or by someone answering for them.
"Not discussed" means the note records no allergy statement: it never came up, or the answer
was empty or unclear. Neither value is a separate check against the transcript; the clinician
confirms both against the conversation. With "Not discussed", advisory.warnings carries an
allergies_not_discussed item. Only a denial is an allergy history; "Not
discussed" is not. Notes stored before this change may still read "None stated", which
does not tell the two apart.
| Field | Contents |
|---|---|
| warnings | What a clinician should look at before signing. |
| drug_matches | Each drug mentioned, checked against the Saudi drug register. |
| segments | The numbered transcript. evidence_segments point here. Each segment carries role, who said it (clinician, patient or companion, a relative or companion speaking for or with the patient), and role_source: voice when the speakers were told apart by voice, content when the speaker was inferred from what was said. Both are null when not known. |
| labeled_transcript | The dialogue in English, each turn labelled Doctor, Patient or Relative/companion. When every segment has a role, it is built from those roles. |
| raw_transcript | The recognised speech verbatim, in the language spoken. |
| transcript_english | Present only when the consultation was translated. |
| source_segments | Present only when translated: the pre-translation segments, with the same numbers and roles. |
| unmapped_content | Clinically significant statements that fit no note field. For review, not for the record. |
Three severities, and they describe how likely something is to be a mistake:
| Severity | Meaning |
|---|---|
| critical | We believe something is wrong. Do not sign without looking. |
| caution | We cannot tell. Worth checking. |
| info | Worth knowing. Nothing is wrong. |
Each warning carries a stable code for counting and testing, a
message for a clinician to read, a detail explaining it, and
evidence_segments pointing at the transcript so any warning can be checked
against what was actually said.
The drugs listed in the note's current medications and new prescriptions (or, only when both are empty, in its free-text medication field) are checked by our own code against the Saudi register, never by a language model. A drug mentioned elsewhere in the note is not checked. Three outcomes:
| status | Meaning |
|---|---|
| matched | One registered product fits. product carries it, with its registration number. |
| ambiguous | Several fit. candidates lists them; a clinician picks. |
| not_found | Nothing in the register fits. |
not_found does not mean the drug is wrong. The Saudi register
does not contain every medicine legally dispensed in the Kingdom. A medicine imported for a
named patient, one your pharmacy compounded, one the patient brought from abroad, and
supplements, which are registered separately, will all return not_found. Present
it as "not on the register", never as an error.
Every non-2xx response uses one shape:
{
"error": {
"code": "not_found",
"message": "No such encounter.",
"request_id": "d719bfc0adbe"
}
}
Quote request_id when you contact us. It is how we find your exact request in
our logs.
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | The request could not be read. The message names the fields to check. |
| 401 | unauthorised | Missing, malformed, unknown or revoked key. |
| 404 | not_found | No such encounter, or no such path. |
| 405 | method_not_allowed | Wrong method for that path. |
| 413 | payload_too_large | The recording is over 90 MB. |
| 429 | rate_limited | Over your rate limit. Back off and retry. This one is generated before your request is processed, so its request_id is the fixed string rate-limited rather than a traceable reference. |
| 503 | service_unavailable | Temporarily unable to serve. Retry shortly. |
| 500 | internal_error | A fault at our end. Quote the request id. |
Validation failures return 400, not 422. A 404 is
returned for an encounter belonging to another account exactly as for one that does not
exist, so the API cannot be used to discover whether an identifier is real.
curl -X POST https://api.ftech.sa/v1/encounters \
-H "Authorization: Bearer $FTC_API_KEY" \
-H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
-F "audio=@consultation.m4a" \
-F "mrn=1140027" \
-F "patient_sex=Female" \
-F "patient_age=52"
# 202 Accepted
# {"id":"0b7f3d5a-...","status":"queued","stage":"received","percent":0}
curl https://api.ftech.sa/v1/encounters/0b7f3d5a-... \
-H "Authorization: Bearer $FTC_API_KEY"
# 200 OK, once status is "ready"
# {"status":"ready","stage":"ready","percent":100,
# "note":{...},"advisory":{...}}