NAV Navbar
javascript shell

Introduction

This is the documentation for the Truepill UK Pharmacy API. This API allows you to digitally integrate with the Truepill Pharmacy in the UK. You can place Orders on behalf of your users that will be prescribed, processed & fulfilled by our Prescribing & Pharmacy teams.

Partnering with Truepill in the UK

If you are currently operating / considering starting a business that could benefit from integration with our digital pharmacy services please get in touch at https://truepill.co.uk/contact.

Technical notes

Security

All API requests are securely transmitted using the TLS 1.2 protocol. All data is encrypted in transit & at rest in the database systems.

JSON API

Our API adheres to the JSON:API 1.0 specification, as detailed here.

JSON:API is a specification for how a client should request that resources be fetched or modified, and how a server should respond to those requests.

Important note: JSON:API requires the use of the JSON:API media type (application/vnd.api+json) for exchanging data.

Content-Type: application/vnd.api+json

For more information on some of the benefits of JSON API have a look at this blog post: https://nordicapis.com/the-benefits-of-using-json-api/

Getting Started

Before you begin

Before you can access the Pharmacy API you will need some credentials. These are issued to you when you sign up to work with Truepill and must be kept secure at all times.

Once you have obtained your credentials, which are issued in the form of a pair of keys referred to as a Client ID & Client Secret, you may proceed to the Accessing the API section below.

If you are not yet registered with us please see the information in Partnering with Truepill in the UK

Sandbox Environment

We provide a "sandbox" environment so that you can test your integrations before using the production systems. This is currently provided free of charge & any API requests made to the system will not incur billing costs.

The sandbox environment is kept up-to-date with the latest live version of the Pharmacy API.

The URL for the sandbox environment is: https://sandbox.truepill.co.uk

We provide different types of test products to help you onboard quickly with the sandbox environment.

Product ID Type
2021-GBP-A1-TESTPOM-63-TESTPOM63 POM
2021-GBP-A1-TESTP-1-TESTP1 P
2021-GBP-A1-TESTGSL-1-TESTGSL1 GSL

Accessing the API

To access the API an Authorization HTTP Header is required for all endpoints.

The Pharmacy API Authentication is OpenID Connect compatible which means that it is OAuth 2.0 compliant.

For now we support the Client Credentials authentication flow. For more details on what that means have a look at this information from Auth0.

Continue reading to see how this looks in practice.

Obtaining an initial Token

Tokens are obtained from the Truepill Auth server which can be found at https://auth.truepill.co.uk/.

curl --request POST \
  --url https://auth.truepill.co.uk/auth/realms/pharmacy/protocol/openid-connect/token \
  --data 'grant_type=client_credentials' \
  --data 'client_id=YourClientId'\
  --data 'client_secret=your-client-secret-string' \
  --data 'response_type=code'
const response = await fetch(
  'https://auth.truepill.co.uk/auth/realms/pharmacy/protocol/openid-connect/token',
  {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
    body: 'grant_type=client_credentials'
      + '&client_id=<YOUR_CLIENT_ID>'
      + '&client_secret=<YOUR_CLIENT_SECRET>'
      + '&response_type=code'
  }
);

return response.json();

Successful responses look like this:

{
  "access_token": "eyJ...TEfg",
  "expires_in": 36000,
  "refresh_expires_in": 1800,
  "refresh_token": "\"eyJhb...gwuF56ahx5uMlE8neo\"",
  "token_type": "bearer",
  "not-before-policy": 0,
  "session_state": "c8ea91e6-f9c6-44c3-b685-c14f6f62b63d",
  "scope": "acceptable scopes"
}

The important part of this is the access_token property.

The Access Token is what you submit with each of your API requests as a Bearer Token in an Authentication header.

Making an API request

API Request with Auth example

You should now have an Access Token from the previous step. You can now make an authenticated request to the Pharmacy API.

To submit your access token with a request it must be included in the Authorization header in the Bearer Token format.

Example header: Authorization: Bearer <Your Access Token>

You should then be able to successfully make a request to an endpoint, e.g. POST /orders.

Errors

Our API uses the following error codes. Click here to read more about JSON:API errors.

Error Code Meaning
400 Bad Request - Your request is invalid.
401 Unauthorized - We cannot identify you. Please acquire a new Access Token using the Authentication workflow.
403 Forbidden - You cannot access this resource.
404 Not Found - The specified resource could not be found.
500 Internal Server Error - We had a problem with our server. Try again later.

Orders

Order process

Creating an order is the first step to providing medication to your customers. You simply need to provide us with a few key bits of information and we can handle the rest for you.

The first step is to place an order via the API.

Depending on the type of order, it may need to be prescribed by one of our prescribing team or reviewed by our pharmacy. If there is a medical reason the order cannot proceed it will be REJECTED at this stage.

If the order is approved it is sent to our pharmacy and marked as READY_FOR_DISPATCH.

This is where our pharmacists will find and package the correct medication for the concerned order.

Once an order is handed to a shipping carrier it is marked as DISPATCHED.

This completes the order lifecycle.

Note: We are looking to add shipping carrier tracking integration in the future.

Classes of Medication

There are three classes of medication under the Medicines Act 1968, and each one has a slightly altered workflow.

  1. General Sale List medicines (GSL).
  2. Pharmacy medicines (P).
  3. Prescription Only Medicines (POM).

POM (Prescription Only Medication) Orders

This type of medication requires a prescriber to read over any necessary consultation notes and prescribe it.

The prescriber can approve or reject an order. The approval creates a prescription which can be signed.

Once approved the prescription is awaiting dispensing where it can then be fulfilled.

P (Pharmacy Medication)

Much like POM medication this requires consultation notes to be checked over however instead of a prescriber a pharmacist is able to approve/reject orders.

GSL (General Sales List)

These medications can be obtained without a pharmacist or prescriber and can be attached to any order with other medications.

To read in more detail about medication classifications, you can look here

You can stay up to date with order status updates by making use of webhooks.

Create an Order

curl --request POST \
  --url https://pharmacy.truepill.co.uk/api/orders \
  -H "Authorization: Bearer eyJ...TEfg" \
  -H "Content-Type: application/vnd.api+json" \
  --data '{
    "data": {
      "type": "order",
      "attributes": {
        "shippingAddress": {
          "name": "The Royal Family",
          "line1": "Buckingham Palace",
          "postcode": "SW1A 1AA"
        },
        "consultation": [
          {
            "question": "Do you have any existing medical conditions?",
            "answer": "No"
          }
        ],
        "patient": {
          "sex": "female",
          "telephone": "07111111111",
          "dob": "2011-10-05",
          "email": "jane@foo.com",
          "firstName": "Jane",
          "lastName": "Doe",
          "genderIdentity": "female",
          "preferredPronouns": "She/Her",
          "preferredName": "Ms Doe"
        },
        "items": [{
          "id": "2020-GBP-A1-FRCON-63-MCGY3075",
          "quantity": 3
        }]
      }
    }
  }'
return fetch('https://pharmacy.truepill.co.uk/api/orders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/vnd.api+json',
    'Authorization': 'Bearer eyJ...TEfg',
  },
  body: {
    "data": {
      "type": "order",
      "attributes": {
        "upstreamId": "BHA1634",
        "shippingAddress": {
          "name": "The Royal Family",
          "line1": "Buckingham Palace",
          "postcode": "SW1A 1AA"
        },
        "consultation": [
          {
            "question": "Do you have any existing medical conditions?",
            "answer": "No"
          }
        ],
        "patient": {
          "sex": "female",
          "telephone": "07111111111",
          "dob": "1998-10-05",
          "email": "jane@foo.com",
          "firstName": "Jane",
          "lastName": "Doe",
          "genderIdentity": "female",
          "preferredPronouns": "She/Her",
          "preferredName": "Ms Doe"
        },
        "items": [{
          "id": "2020-GBP-A1-FRCON-63-MCGY3075",
          "quantity": 3
        }]
      }
    }
  }
});

POST https://pharmacy.truepill.co.uk/api/orders

Field Type Required Notes
upstreamId String No An optional identifier that identifies the Order record in your system. This could be a reference from a 3rd party system or your own internal Order number.
Can be helpful for customer-services cases.
shippingAddress Address Yes If no name is provided in shippingAddress, the combination of firstName and lastName from patient will be used.
consultation Consultation Yes
patient Patient Yes
items Items Yes

Shipping Address

Field Name Type Example Required
name String The Royal Family No
line1 String Buckingham Palace Yes
line2 String Room 12 No
city String London No
region String London No
postcode String SW1A 1AA Yes

Consultation

Array of answers with their attached questions. The consultations are important for pharmacists and prescribers understand whether the medication selected is right for the customer.

Field Name Type Example Required
question string "Do you have any existing medical conditions?" Yes
answer string No No

Patient

Field Name Type Example Required
firstName String john Yes
lastName String smith Yes
sex String male or female Yes
dob String 2011-10-05: yyyy-mm-dd format Yes
email String john.smith@example.com Yes
telephone String 07500555555 Yes
genderIdentity String Non Binary No
preferredPronouns String They/Them No
preferredName String Jon No

Items

Array of items with their attached ids and quantities.

Field Name Type Example Required
id String 2020-GBP-A1-FRCON-63-MCGY3075 Yes
quantity Number 3 Yes

Responses

This is an example response:

{
  "data": {
    "type": "order",
    "id": "ord_31232",
    "attributes": {
      "upstreamId": "BHA1634",
      "status": "READY_FOR_PRESCRIBER",
      "shippingAddress": {
        "name": "The Royal Family",
        "line1": "Buckingham Palace",
        "postcode": "SW1A 1AA"
      },
      "patient": {
        "id": "1223"
      },
      "createdAt": "2011-10-05T14:48:00.000Z",
      "items": [{
        "id": "2020-GBP-A1-FRCON-63-MCGY3075",
        "quantity": 3,
        "productType": "medication",
        "medicationType": "POM"
      }]
    }
  }
}
Status Code Meaning
200 Your order has successfully been created
400 The order was invalid and could not be created. Usually this is a simple validation error and you will get a detailed error object explaining what is missing. In some cases it may be because the patient data clashed with existing patient data, and could neither be merged nor stored separately. In this case you will get the error "Conflict found with existing data."

Get an Order

curl --request GET \
  --url https://pharmacy.truepill.co.uk/api/orders/:orderId/ \
  -H "Authorization: Bearer eyJ...TEfg" \
  -H "Content-Type: application/vnd.api+json"
return fetch('https://pharmacy.truepill.co.uk/api/orders/:orderId', {
  headers: {
    'Content-Type': 'application/vnd.api+json',
    'Authorization': 'Bearer eyJ...TEfg',
  }
});

GET https://pharmacy.truepill.co.uk/api/orders/:orderId/

You can retrieve an individual order with its current status and full order information.

Responses

This is an example response:

{
  "data": {
    "type": "order",
    "id": "ord_31232",
    "attributes": {
      "upstreamId": "BHA1634",
      "status": "READY_FOR_PRESCRIBER",
      "shippingAddress": {
        "name": "The Royal Family",
        "line1": "Buckingham Palace",
        "postcode": "SW1A 1AA"
      },
      "patient": {
        "id": '1223'
      },
      "createdAt": '2011-10-05T14:48:00.000Z',
      "reviewerDetails": { // A reviewer can either be a Pharmacist or a Prescriber
        "name": "Jane",
        "registrationId": "823739",
        "registrationBody": 'GPhC',
        "reviewedAt": null // Only populated when it has been approved (P/POM)
      },
      "items": [{
        "id": "2020-GBP-A1-FRCON-63-MCGY3075",
        "quantity": 3,
        "type": "pom"
      }]
    }
  }
}

This is an example for a dispatched order including the resolved object:

{
  "data": {
    "type": "order",
    "id": "ord_31232",
    "attributes": {
      "status": "DISPATCHED",
      "shippingAddress": {
        "name": "The Royal Family",
        "line1": "Buckingham Palace",
        "postcode": "SW1A 1AA"
      },
      "patient": {
        "id": '1223'
      },
      "createdAt": "2011-10-05T14:48:00.000Z",
      "resolution": {
        "type": "DISPATCHED",
        "resolvedAt": "2011-10-06T08:01:00.000Z"
      },
      "items": [{
        "id": "2020-GBP-A1-FRCON-63-MCGY3075",
        "quantity": 3,
        "type": "pom"
      }]
    }
  }
}
Status Code Meaning
200 Successfully retrieved order

Response properties

Property Type
upstreamId String
status String of Status
shippingAddress Address
patient Patient
createdAt DateTime*
reviewerDetails Reviewer
reviewedAt DateTime* (Only populated when it has been approved (P/POM)
items Array of Items
resolution Resolution

*Datetimes are all ISO 9801 with timezone

Patient Response

Field Name Type Description
id String The patient's ID in our system

Reviewer

A reviewer can either be a Pharmacist or a Prescriber.

Field Name Type
name String
registrationId String
registrationBody String

Items

An array of items.

Field Name Type
id String
quantity number

Resolution

A resolved order may be "Cancelled", "Rejected" or "Dispatched". In all these cases a resolution object is included.

Field Name Type Description
type String The final status of the order (Cancelled, Rejected, Dispatched)
resolvedAt DateTime* The timestamp when order was resolved
reason String Conditional field for additional information (when Cancelled/Rejected)
name String The full name of individual that resolved the order (when Rejected)
registrationBody String The professional body the named individual is registered with (when Rejected)
registrationId String Id of the individual at their professional body (when Rejected)

Cancel an Order

curl --request POST \
  --url https://pharmacy.truepill.co.uk/api/orders/:orderId/cancel \
  -H "Authorization: Bearer eyJ...TEfg" \
  -H "Content-Type: application/vnd.api+json"
return fetch('https://pharmacy.truepill.co.uk/api/orders/:orderId/cancel', {
  method: 'POST'
  headers: {
    'Content-Type': 'application/vnd.api+json',
    'Authorization': 'Bearer eyJ...TEfg',
  }
});

POST https://pharmacy.truepill.co.uk/api/orders/:orderId/cancel

All successful requests will trigger an event to be sent to all subscribed webhooks. Click here to find out about our webhooks. Orders can only be cancelled in certain statuses, to see the applicable statuses, please click here.

Responses

This is an example response:

{
  "data": {
    "type": "order",
    "id": "ord_31232",
    "attributes": {
      "status": "CANCELLED",
      "shippingAddress": {
        "name": "The Royal Family",
        "line1": "Buckingham Palace",
        "postcode": "SW1A 1AA"
      },
      "patient": {
        "id": "1223"
      },
      "createdAt": "2011-10-05T14:48:00.000Z",
      "resolution": {
        "resolvedAt": "2020-04-20T09:00:00Z",
        "type": "CANCELLED",
        "reason": "By request"
      },
      "items": [{
        "id": "2020-GBP-A1-FRCON-63-MCGY3075",
        "quantity": 3,
        "type": "pom"
      }]
    }
  }
}
Status Code Meaning Resolution
200 Successful The order has been successfully cancelled.

Statuses

Status Meaning Event Triggered Cancellable
READY_FOR_PRESCRIBER Waiting for a prescriber to be assigned
READY_FOR_REVIEW Waiting for a pharmacist to be assigned
PRESCRIBING Currently being reviewed by a prescriber
REVIEWING Currently being reviewed by a pharmacist
READY_FOR_DISPENSING Order has been approved and is ready to be dispensed
READY_FOR_DISPATCH Order has been dispensed and is ready to be dispatched
DISPATCHING Currently being handled by a dispatcher
DISPATCHED Order has been dispatched
REJECTED Order was rejected by Truepill
CANCELLED Order has been cancelled

Webhooks

When an order status changes, you might want to send an email update to your customer, or update some status on your server.

In order to see when an order status changes you can enrol a webhook address with us, and we will POST event updates back to you, signed with a secret key.

Create a new Webhook

curl --request POST \
  --url https://pharmacy.truepill.co.uk/api/webhooks \
  -H "Authorization: Bearer eyJ...TEfg" \
  --data '
  {
    "data": {
      "type": "webhook",
      "attributes": {
        "url": "https://mysite.com/callbacks/notify-orders"
      }
    }
  }'
return fetch('https://pharmacy.truepill.co.uk/api/webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/vnd.api+json',
    'Authorization': 'Bearer eyJ...TEfg',
  },
  body: {
    "data": {
      "type": "webhook",
      "attributes": {
        "url": "https://mysite.com/callbacks/notify-orders"
      }
    }
  }
});

Successful responses look like:

{
  "data": {
    "type": "webhook",
    "id": "hook_72392e4a608e",
    "attributes": {
      "secret": "4f4505a8-0907-43e8-bd1d-8470a8a347bd",
      "url": "https://mysite.com/callbacks/notify-orders"
    }
  }
}

POST https://pharmacy.truepill.co.uk/api/webhooks

Content-Type: application/vnd.api+json

Once created, you must save your signing-secret, which you can use to verify that events came directly from TruePill.

HTTP Request

Parameters

Field Type Example Required
url string https://www.managed-client.com/api/webhooks/orders Yes

Responses

Status Code Meaning
200 Created

View All Webhooks

This endpoint allows you to view all of your companies webhooks.

GET https://pharmacy.truepill.co.uk/api/webhooks

curl --request GET \
  --url https://pharmacy.truepill.co.uk/api/webhooks \
  -H "Authorization: Bearer eyJ...TEfg" \
return fetch('https://pharmacy.truepill.co.uk/api/webhooks', {
  headers: {
    'Content-Type': 'application/vnd.api+json',
    'Authorization': 'Bearer eyJ...TEfg',
  }
});

The above request will create a response that looks like this:

{
  "data": [
    {
      "type": "webhook",
      "id": "hook_2e414da9d950",
      "attributes": {
        "secret": "5c10b4d8-7813-4a42-b6bd-f13dcde947cd",
        "url": "https://test.com/test"
      }
    },
    {
      "type": "webhook",
      "id": "hook_72392e4a608e",
      "attributes": {
        "secret": "4f4505a8-0907-36dh-bd1d-8470a8a347ed",
        "url": "https://test.com/status"
      }
    }
  ]
}

Responses

Status Code Meaning
200 Ok

Delete a Webhook

If you not longer require a webhook you can use this endpoint to delete it.

DELETE https://pharmacy.truepill.co.uk/api/webhooks/:webhook_id

curl --request DELETE \
  --url https://pharmacy.truepill.co.uk/api/webhooks/:webhook_id \
  -H "Authorization: Bearer eyJ...TEfg" \
return fetch('https://pharmacy.truepill.co.uk/api/webhooks', {
  method: 'DELETE',
  headers: {
    'Content-Type': 'application/vnd.api+json',
    'Authorization': 'Bearer eyJ...TEfg',
  }
});

Responses

Status Code Meaning
204 Deleted the webhook

Events

When an Order Status changes an Event will be sent to your registered Webhooks. Only certain statuses will trigger an event.

Signing

An example JWS would look like this:

eyJhbGciOiJIUzUxMiJ9.eyJldmVudCI6Ik9SREVSX1VQREFURUQiLCJyZXNvdXJjZSI6eyJpZCI6Im9yZF8xMjM0IiwidHlwZSI6Im9yZGVyIiwibGluayI6Imh0dHBzOi8vcGhhcm1hY3kudHJ1ZXBpbGwuY28udWsvYXBpL29yZGVycy9vcmRfMTIzNCJ9fQ.Ayd3GADKtyng3jop9gUl8HN3L5nHdvFw_s7qSQRCuR-gQkAb0Da1kIZlHKuyyXElY0FwNK9H_mRR2-XO3l6nng

You can use node-jws to quickly verify and decode JWS payloads.

import jws from 'jws';

// ...

const { header, payload } = jws.decode(data);
if (!jws.verify(data, header.alg, storedSecret)) {
  throw new Error('Request did not come from Truepill');
}
const event = JSON.parse(payload);

Events will be sent to your webhook addresses in JWS format. This includes a JSON payload and a signature. The signature uses a 512-bit HMAC algorithm, and the key is the secret returned when you created the webhook. The node-jws package can be used to easily verify the signature and decode the JSON string.

The decoded JSON payload will be the following format:

Event Fields

An example of the JSON payload:

{
  "event": "ORDER_UPDATED",
  "resource": {
    "id": "ord_1234",
    "type" : "order",
    "link": "https://pharmacy.truepill.co.uk/api/orders/ord_1234"
  }
}
Property Meaning Type
resource.type The resource type String
resource.id The ID of the resource String
resource.link The url at which the resource can be viewed String
event The type of Event that occurred String