Server-side APIEndpointsSession packs

Buy a pack

Sell a session pack on its own, with no appointment attached.

POST /v1/session/packs

A standalone pack purchase: the patient buys the block of sessions now and books visits against it later. It mints a hosted checkout and returns the URL — the pack itself is created only when the payment is confirmed, so an abandoned checkout buys nothing.

If the patient wants to buy a pack and take their first session in one go, use book-appointment-unified with bookingType: "PACKAGE_PURCHASE" instead.

OTP-gated

The patient's phone must have been verified in the last 30 minutes, and this request must carry the same x-end-user-id as that verification. See OTP-gated routes.

Body

Patient identification works as on the booking routes: patientId, or a patientPayload object to find-or-create the record.

FieldTypeRequiredDescription
packageConfigIdnumberYesThe pack, from the catalog.
packageAmountnumberYesWhat the patient pays. Use the catalog's discountedPrice.
currencystringYesISO 4217. Match the pricing you quoted.
doctorIdnumberYesThe practitioner the pack is bought against.
addressIdnumberYesThe clinic location. workspaceAddressId is accepted as an alias.
returnUrlstringYesWhere the gateway sends the patient afterwards.
patientIdnumberYes**Or a patientPayload.

Nothing else in the body is forwarded — this route sends a fixed set of fields upstream, so extra keys are ignored rather than rejected.

Headers

HeaderDescription
Idempotency-KeyRecommended. De-duplicates a double submit so a retry returns the same checkout instead of a second one. Generated for you when absent, which does nothing for your own retries.

Try it

POST/v1/session/packs

The pack is minted PAID on verify, not here — a purchase that is never confirmed creates nothing.

Body

Request
POST /v1/session/packs
{
  "currency": "INR",
  "doctorId": "4",
  "addressId": "1"
}
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/session/packs" \
  -H "x-api-key: $MEDOS_API_KEY" \
  -H "x-end-user-id: $PATIENT_ID" \
  -H "Idempotency-Key: 4c0f9e2a-0b77-4a51-9d3f-2b6f0a91c774" \
  -H "Content-Type: application/json" \
  -d '{
    "patientId": 774,
    "packageConfigId": 12,
    "packageAmount": 4500,
    "currency": "INR",
    "doctorId": 4,
    "addressId": 1,
    "returnUrl": "https://clinic.example.com/packs/return"
  }'

Response

{
  "checkoutUrl": "https://checkout.example-gateway.com/s/pk_9931",
  "provider": "RAZORPAY",
  "sessionRef": "pks_01J9ZB4M7T"
}
FieldMeaning
checkoutUrlSend the patient here.
sessionRefThe only handle that settles this purchase.
providerThe gateway the clinic has configured.

Persist sessionRef before redirecting

There is no reference to fall back on here — a pack purchase has no booking and no appointment ref, so sessionRef is the whole story. Store it against your own order record before the patient leaves the page.

Common failures

StatusCause
400packageConfigId is not a pack in this workspace, or is no longer isActive.
400packageAmount does not match the pack's configured price.
403The OTP gate.
4xxGATEWAY_NOT_CONFIGURED — the clinic admin has not enabled online payment. Passed through unchanged. Nothing was created.

Next

Confirm a pack payment

Settle the purchase when the patient returns.

On this page