Server-side APIEndpointsPatients

Verify and fetch records

Check the code, open the OTP gate, and get the patient's records back in the same call.

POST /v1/patients/verify-phone-verification-otp

Verifies the code sent by send-phone-verification-otp and does two things at once: it opens the OTP gate for the next 30 minutes, and it returns everything the clinic already holds for that phone — the patient records on it, their active session packs, and the packs available to buy.

That second half is why this pair exists alongside /v1/otp/*: one call gets you both consent and the data you need to book.

Body

FieldTypeRequiredDescription
otpCodestringYesThe code the patient received.
phoneNumberstringYes*The same number you sent to.
countryCodestringNoDefaults to +91. Must match what you sent to.
channelstringNowhatsapp (default) or email.
emailstringYes**Instead of phoneNumber on the email channel.

Try it

POST/v1/patients/verify-phone-verification-otp

Body

Request
POST /v1/patients/verify-phone-verification-otp
{
  "countryCode": "+91",
  "phoneNumber": "9811100001"
}
to https://api.medos.one
Use a dedicated test key, and put your browser's address on its allowlist

A Developer API key authenticates on its own, so it only works from the addresses registered against it — and this page calls from your browser, not your servers. Unless your own public address is on the list you get a 403 naming it, which is the allowlist doing its job. Your browser may also reach us over IPv6 even when your server does not, so the address in the error is often not the one you expected. The key here is kept in memory only and never written to storage, but create a test key for it and deactivate that key when you are done.

Request

curl -sX POST "https://api.medos.one/v1/patients/verify-phone-verification-otp" \
  -H "x-api-key: $MEDOS_API_KEY" \
  -H "x-end-user-id: $PATIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{ "countryCode": "+91", "phoneNumber": "9876543210", "otpCode": "418206" }'

Response

{
  "success": true,
  "message": "Phone number verified successfully",
  "channel": "whatsapp",
  "data": {
    "associatedPatients": [
      {
        "id": 774,
        "mrnNumber": "MRN-0774",
        "firstName": "Asha",
        "lastName": "Nair",
        "countryCode": "+91",
        "phoneNumber": "9876543210",
        "dob": "1991-04-02",
        "gender": "FEMALE",
        "patientFormValues": {}
      }
    ],
    "activeSessionPackResponses": [
      {
        "id": 4411,
        "packageConfigId": 12,
        "sessionPackName": "Physio — 10 sessions",
        "totalSessions": 10,
        "sessionsUsed": 3,
        "sessionsRemaining": 7,
        "durationMins": 30,
        "purchasedOn": "2026-07-14",
        "expiresOn": "2026-10-12",
        "allowedDoctorIds": [4, 7],
        "allowFamilyBooking": true
      }
    ],
    "allSessionPackResponses": []
  }
}
FieldMeaning
associatedPatients[]Every patient record on that phone. One number often covers a household — a parent booking for a child shares it — so this is a list, and picking the right member is your job.
activeSessionPackResponses[]Packs the patient can spend now. id is the patientPackageId a USE_ACTIVE_PACKAGE booking takes.
allSessionPackResponses[]The clinic's purchasable catalog, same shape as the catalog route.

An empty associatedPatients is a successful verification of a phone the clinic has never seen. Book with a patientPayload and the record is created.

The gate is now open for 30 minutes

Every OTP-gated route will accept requests carrying the same x-end-user-id for the next 30 minutes. A different value is a different patient, and starts unverified.

Common failures

StatusCause
400otpCode missing.
400Invalid or expired code — { "success": false, "message": "Invalid or expired OTP" }. Send a new one.
400Verifying a different destination than you sent to. The code is bound to the exact countryCode + phoneNumber pair.

Next

On this page