Docs

Write and sign a prescription

Your system can add patients to Priorly, write their prescriptions, and have the prescriber sign each one without leaving your system. Once signed, it goes straight to the dispensing pharmacy as an order.

The prescriber still signs every prescription themselves. Your system sends their own signing token, which says who is signing, and the PIN they type for that prescription. A token never signs on its own.

Start with Getting started: the base URL, your API key, the headers and what to do when a call fails are the same for every guide. For this one you also need:

The signing scope on your API key
Signing needs "Sign with a prescriber's token".
A signing token for each prescriberprly_rx_live_…
A business admin makes it for the prescriber in Settings › API keys, or the prescriber makes their own in Prescriptions › Sign from other systems. Store it against that prescriber. It lasts up to 12 months, and works with sandbox and live keys.

Each token is shown once, so put it straight into your secret manager.

The rules that matter most

  • Call the API from your server only. Never put a key or token in a browser, an app, a URL, a log line or a prompt to an AI tool.
  • Never store a prescriber's PIN. They type it in your system for each prescription. Send it in that one sign request, then discard it: no database, log, cache or queue.
  • Make a new Idempotency-Key each time the prescriber types their PIN. Reuse one only to retry the same request after a timeout or a 5xx.
  • Name medicines by dm+d SNOMED code, found with GET /medicines, never by name alone.

In the sandbox

WhatSandbox keyLive key
PrescribersThe same prescribers, signing tokens and PINsThe same
Signed with "route": "pharmacy"A test order at Priorly's test pharmacy. Nothing is dispensed or chargedA real order at the clinic's dispensing pharmacy
Signed with "route": "email_to_patient"The prescription is made, and nothing is emailedEmailed to the patient

Signing in the sandbox is still the prescriber's. They use their real signing token and type their real PIN, and wrong PINs count towards the same 5-try lock.

This run adds a made-up patient, writes a draft, then has the prescriber sign it with a wrong PIN and then the right one. Pick your language on any code block and every block on the page follows. The IDs in the answers are examples: use the ones your calls return.

Use a sandbox key for this run. With a sandbox key, the prescription goes to Priorly's test pharmacy as a test order. With a live key, signing creates a real prescription and a real order at the pharmacy.

  1. Step 1. Set up

    Run these in a terminal on your own computer: Mac, Linux, or Git Bash on Windows. read -rs asks for each secret without showing it or saving it in your shell history.

    cURL
    API=https://api.priorly.io/v1
    printf 'API key: '; read -rs KEY; echo
    printf "Prescriber's token: "; read -rs TOKEN; echo
  2. Step 2. Find who can sign at the key's location

    GET/prescribers

    Keep the prescriber's id.

    In the answer, for each prescriber

    idstring
    Write a draft for this prescriber with it, as prescriber.
    namestring
    The prescriber's name.
    regulatorstring
    Who they're registered with, like GMC, with their registrationNumber.
    cURL
    curl -s $API/prescribers -H "Authorization: Bearer $KEY"
    Response200
    {
      "data": [
        {
          "id": "usr_9Qm2xYk7Lp",
          "name": "Sam Sample",
          "regulator": "GMC",
          "registrationNumber": "7654321"
        }
      ]
    }
  3. Step 3. Find the medicine's dm+d code

    GET/medicines

    If your system already stores dm+d codes, use those.

    Query

    qstringRequired
    What to look for, like the medicine's name and strength: at least 3 characters.

    In the answer, for each medicine

    snomedCodestring
    Its dm+d SNOMED code. Prescribe with this.
    customMedicineIdstring
    For one of the clinic's own medicines, in place of snomedCode.
    packSizesarray of strings
    The pack sizes it comes in.
    prescribableboolean
    Whether it can be prescribed.
    cURL
    curl -s "$API/medicines?q=finasteride%201mg" \
      -H "Authorization: Bearer $KEY"
    Response200
    {
      "data": [
        {
          "snomedCode": "42035911000001103",
          "customMedicineId": null,
          "name": "Finasteride 1mg tablets",
          "form": "Oral tablet",
          "packSizes": ["28", "30", "84", "90"],
          "controlledDrugSchedule": null,
          "prescribable": true
        }
      ]
    }
  4. Step 4. Add the patient

    POST/patients

    A home address and a date of birth are needed before a prescription can be signed. Send your own ID as externalId and keep Priorly's id. Adding a patient sends them nothing.

    Body

    externalIdstring
    Your own ID for the patient. A patient whose email or externalId is already there is refused with patient_exists.
    firstName, lastNamestringRequired
    The patient's names.
    dateOfBirthstringNeeded to sign
    The patient's date of birth, like 1990-01-01.
    homeAddressobjectNeeded to sign
    Where the patient lives.
    line1stringRequired
    The first line of the address.
    line2string
    The second line, if there is one.
    citystring
    The town or city.
    postcodestringRequired
    A UK postcode. It's saved in capitals with one space, so ls14ab comes back as LS1 4AB.
    cURL
    curl -s $API/patients \
      -H "Authorization: Bearer $KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: test-patient-1" \
      -d '{"externalId":"CLINIC-000417",
           "firstName":"Alex","lastName":"Example",
           "dateOfBirth":"1990-01-01",
           "homeAddress":{"line1":"1 Test Street",
             "city":"Leeds","postcode":"ls14ab"}}'
    Response201
    {
      "id": "cus_8f3b2c1d9e",
      "externalId": "CLINIC-000417",
      "firstName": "Alex",
      "lastName": "Example",
      "dateOfBirth": "1990-01-01",
      "homeAddress": {
        "line1": "1 Test Street",
        "line2": null,
        "city": "Leeds",
        "postcode": "LS1 4AB"
      },
      "status": "active",
      "…": "…"
    }
  5. Step 5. Write the draft

    POST/prescriptions

    Check readyToSign is true. The draft also appears in the prescriber's Prescriptions › Drafts in Priorly, if they'd rather sign it there.

    Body

    patientIdstringRequired
    Priorly's id for the patient, from step 4.
    prescriberstringRequired
    The id from step 2, or "any" for whichever prescriber signs it.
    externalReferencestring
    Your own reference for the prescription.
    itemsarray of objectsRequired
    What's prescribed.
    medicine.snomedCodestring
    The dm+d code from step 3. Each item needs this or customMedicineId.
    medicine.customMedicineIdstring
    One of the clinic's own medicines from step 3, in place of snomedCode.
    quantityintegerRequired
    How many, as a whole number.
    unitstring
    What the quantity counts, like tablets.
    directionsstringRequired
    The directions for the patient.

    In the answer

    readyToSignboolean
    true when it can be signed.
    problemsarray of objects
    What's stopping it, each with a code and a message: usually the patient's home address or date of birth.
    cURL
    curl -s $API/prescriptions \
      -H "Authorization: Bearer $KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: test-rx-1" \
      -d '{"patientId":"cus_8f3b2c1d9e",
           "prescriber":"any",
           "externalReference":"RX-TEST-1",
           "items":[{
             "medicine":{"snomedCode":"42035911000001103"},
             "quantity":28,"unit":"tablets",
             "directions":"Take ONE tablet daily."}]}'
    Response201
    {
      "id": "script_AB12-CD34-EF56-GH78",
      "status": "draft",
      "readyToSign": true,
      "problems": [],
      "prescriber": null,
      "…": "…"
    }
  6. Step 6. The prescriber signs

    POST/prescriptions/{id}/sign

    In your system they check the prescription and type their PIN; your server sends it with their token. Here, the prescriber types it at the prompt, and printf hands it to curl through a pipe, so it never shows in the list of running programs.

    Headers

    Priorly-Prescriber-TokenstringRequired
    The signing token of the prescriber who's signing.

    Body

    pinstringRequired
    The 4-digit PIN the prescriber just typed, as a string. Send it in this one request, then discard it.
    routestring
    pharmacy, the default, sends it to the clinic's dispensing pharmacy as an order. email_to_patient emails it to the patient.

    A wrong PIN is refused, and nothing is signed. Run it again with a new Idempotency-Key (test-sign-2) and the right PIN, and it's signed and sent.

    With a sandbox key, pharmacy is Priorly's test pharmacy and the order is a test order. With a live key, it's the clinic's own dispensing pharmacy.

    If that request times out, send it again with the same Idempotency-Key. You get the first answer back with Idempotent-Replayed: true, and it is never signed twice.

    cURL
    RX=script_AB12-CD34-EF56-GH78
    printf 'Prescriber, your PIN: '; read -rs PIN; echo
    printf '{"pin":"%s","route":"pharmacy"}' "$PIN" |
      curl -s $API/prescriptions/$RX/sign \
        -H "Authorization: Bearer $KEY" \
        -H "Priorly-Prescriber-Token: $TOKEN" \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: test-sign-1" \
        --data-binary @-
    unset PIN
    Response · a wrong PIN422
    {
      "error": {
        "code": "wrong_pin",
        "message": "That PIN isn’t right. Ask the prescriber to type it again.",
        "attemptsLeft": 4,
        "requestId": "req_GI4BAJMiqdr3tA4s"
      }
    }
    Response · the right PIN200
    {
      "id": "script_AB12-CD34-EF56-GH78",
      "status": "sent_to_pharmacy",
      "route": "pharmacy",
      "signedThrough": "api",
      "signedBy": {
        "id": "usr_9Qm2xYk7Lp",
        "name": "Sam Sample",
        "regulator": "GMC",
        "registrationNumber": "7654321"
      },
      "pharmacy": { "name": "Parkside Pharmacy" },
      "orderNumber": "40123",
      "…": "…"
    }
  7. Step 7. Follow it

    GET/prescriptions/{id}

    It shows the status as it moves to dispensed, with tracking once it's posted. A prescription's status is one of these:

    draft

    Written, and waiting to be signed.

    sent_to_pharmacy

    Signed and sent to the pharmacy as an order.

    not_sent

    Signed, but it couldn't reach the pharmacy yet. The prescriber can send it again in Priorly.

    partly_dispensed

    Some of its items dispensed.

    dispensed

    Dispensed at the pharmacy.

    not_dispensed

    The pharmacy cancelled the order.

    emailed

    Signed and emailed to the patient, to take to any pharmacy.

    voided

    Cancelled by a prescriber after it was signed.

    draft_deleted

    Deleted before it was signed.

    for_order

    Made in Priorly for an approved online order. Your system doesn't write these.

    Or add a webhook in Settings › API keys, and Priorly tells you when it's signed, sent, dispensed and posted.

    cURL
    curl -s $API/prescriptions/$RX \
      -H "Authorization: Bearer $KEY"
    Response · once posted200
    {
      "id": "script_AB12-CD34-EF56-GH78",
      "status": "dispensed",
      "tracking": [
        {
          "carrier": "Royal Mail",
          "number": "AB123456789GB",
          "url": null
        }
      ],
      "…": "…"
    }

Every error has the same shape, {"error":{"code","message",…,"requestId"}}. What your system should do with the ones signing meets most:

status: sent_to_pharmacy200

Signed and sent.

Show it as sent, with orderNumber.

status: not_sent200

Signed, but it couldn't reach the pharmacy yet.

Tell the prescriber: they can send it again in Priorly.

wrong_pin422

The PIN is wrong. attemptsLeft says how many tries are left.

Ask the prescriber to type it again, and send it with a new Idempotency-Key. Never resend a PIN yourself.

pin_locked423

5 wrong PINs in a row. Signing is locked until lockedUntil (15 minutes), in Priorly too.

Tell the prescriber, and don't retry.

not_ready_to_sign422

The draft has problems, usually the patient's home address or date of birth.

PATCH /patients/{id} with what's missing, then ask for the PIN again.

not_your_draft403

The draft was written for another prescriber.

Have that prescriber sign it, or write drafts for "any".

invalid_prescriber_token401

The token was revoked or expired, the prescriber changed or reset their password, or they no longer prescribe at the clinic.

Ask for a new token for that prescriber.

not_a_draft409

Already signed, voided or deleted.

GET the prescription for its status.

patient_exists409

Adding a patient whose email or externalId is already there.

PATCH the existingPatientId it gives instead.

Too many, or a fault4295xx

Too many requests, or a fault at Priorly.

Retry with backoff and the same Idempotency-Key. A 429 says how long to wait in Retry-After.

Any other 4xx means the request needs fixing, so don't repeat it unchanged.

An error422
{
  "error": {
    "code": "wrong_pin",
    "message": "That PIN isn’t right. Ask the prescriber to type it again.",
    "attemptsLeft": 4,
    "requestId": "req_GI4BAJMiqdr3tA4s"
  }
}
cURL
printf 'Prescriber, your PIN: '; read -rs PIN; echo
answer=$(
  printf '{"pin":"%s","route":"pharmacy"}' "$PIN" |
  curl -s $API/prescriptions/$RX/sign \
    -H "Authorization: Bearer $KEY" \
    -H "Priorly-Prescriber-Token: $TOKEN" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: test-sign-3" \
    --data-binary @-)
unset PIN
code=$(jq -r '.error.code // empty' <<< "$answer")
case "$code" in
  "")
    jq -r .status <<< "$answer" ;;
  wrong_pin)
    echo "Wrong PIN. Ask them to type it again." ;;
  pin_locked)
    echo "Signing is locked. Don't retry." ;;
  not_ready_to_sign)
    echo "Fill in the patient's details, then sign." ;;
  invalid_prescriber_token)
    echo "Ask for a new signing token." ;;
  *)
    echo "$code" ;;
esac

Check that your system:

0 of 6 checked

When something doesn't work, send Priorly the requestId from the error, or the Priorly-Request-Id header on any answer. Never send a key, token, PIN or patient details. Priorly can send the full API reference, which covers every field, error and webhook.