> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rxscale.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Review Requests

> Ask a doctor to review a patient without an order for prescription products, and follow the outcome

# Review Requests

A review request asks one of your organisation's doctors to look at a patient again, without
the patient ordering prescription products first. The typical case is a repurchase: the patient
fills in a short follow-up questionnaire and pays a prescription fee in your shop, and a doctor
decides what happens next.

You send the patient's new anamnesis with the request. The doctor finds it in a separate
**Review requests** list in the Anamnesis Center and answers in one of three ways:

* **Create the treatment.** The doctor creates a draft order in your shop. The patient pays it,
  and the prescription follows the normal review and signing flow.
* **Request a video appointment.** The doctor sends the patient a booking link for an
  appointment with them.
* **Decline**, with a reason.

You follow the outcome through the [`review_request_updated`](https://docs.rxscale.com/webhooks/events#review-request-updated)
webhook, and can read the current state at any time.

All endpoints are scoped to the organisation that owns the API key. A review request, shop,
patient or anamnesis of another organisation is treated as not found.

## How it fits together

**1. The patient submits the questionnaire**

The anamnesis must reach RxScale before you request the review. Submit it through the
[Anamnesis API](https://docs.rxscale.com/api-reference/public/anamnesis-v4.md#submit-anamnesis) and keep the anamnesis
UID RxScale returns: the review request addresses the anamnesis by this UID only.

**2. The patient pays the prescription fee**

For example in your Shopify shop.

**3. You create the review request**

Call `POST /v1/management/review-requests`, for example from Shopify Flow once the
fee order is paid.

**4. The doctor decides**

You receive `review_request_updated` with the doctor's decision.

**5. You act on the decision**

Send the patient the booking link (`video_requested`), send the invoice of the draft order
(`draft_created`), or refund the fee if your terms say so (`declined`).

**6. The patient pays the draft order**

RxScale links the paid order to the request and you receive `completed`.

> [!NOTE]
>
> **RxScale bills nothing for review requests on your shops.** A booking link the doctor creates
> for one of your shops carries no price in RxScale: you decide whether and how the patient pays
> for the video appointment. If the doctor declines, RxScale only reports `declined`. Whether you
> refund the prescription fee is your decision, and you make the refund in your shop.

> [!NOTE]
>
> Make sure the prescription-fee order does not reach a pharmacy. Mark the fee product's SKU with
> `skip_for_pharmacy_orders: true` (see [SKUs](https://docs.rxscale.com/api-reference/management/skus.md)).

### Who receives the request

The request is assigned to the doctor who signed the patient's most recent prescription in your
organisation. If the patient has no signed prescription, or that doctor's account is archived,
the request arrives unassigned and any doctor of your organisation can take it.

The doctor can forward the request to a colleague, and your admins can assign or reassign it
in the admin portal. Every change of doctor sends `assigned`. A request stays with its doctor
until they act on it: nothing releases it after a time limit.

### Whether an asynchronous review is allowed

RxScale does not decide whether a patient may be reviewed without a consultation, for example
whether a video consultation is due first or whether the last one was within a year. You
decide that before you send the request. The doctor sees the date of the patient's last video
appointment and can request a video appointment instead of creating the treatment.

## Permissions

* `review_request:write` -- required to create and to cancel a review request.
* `review_request:read` -- required to read a review request.

A key with `review_request:write` is not automatically granted `review_request:read`; add both
if you need to write and read. To receive the webhook, you also need the
`notification_subscription` permissions (see [Webhooks](https://docs.rxscale.com/api-reference/management/webhooks.md)).
Contact your RxScale account manager to adjust permissions.

All requests authenticate with the `X-API-Key` header. See
[Authentication](https://docs.rxscale.com/authentication.md) for details.

## Create a Review Request

```bash theme={null}
POST /v1/management/review-requests
```

Asks a doctor to review the patient's anamnesis.

**Required permission:** `review_request:write`

### Request Body

**external\_id** `string` *required*

Your ID for this request, 1 to 255 characters. It must be unique within your organisation and
makes the call safe to repeat: see [Repeating a request](#repeating-a-request).

**type** `string` *required*

The kind of review. Currently only `repurchase`.

**shop\_identifier** `string` *required*

Identifier of the shop the patient is a customer of. The shop must belong to your organisation.

**shop\_customer\_id** `string` *required*

Your shop's ID for the customer.

**anamnesis\_uid** `string` *required*

UID of the anamnesis in RxScale, as RxScale returned it when the anamnesis was submitted. The
only way to address the anamnesis (see below).

**fee\_shop\_order\_external\_id** `string`

Your shop's ID of the prescription-fee order, 1 to 255 characters. Use the same ID as the
`shop_order_external_id` filter of [Orders](https://docs.rxscale.com/api-reference/management/orders.md), for Shopify the
order ID. Stored as you send it, for traceability, and returned in reads and webhooks. RxScale
does not need to know the order yet: Shopify Flow can be faster than the order reaching
RxScale.

Fields that are not listed above are refused with `400`.

### Identifying the patient and the anamnesis

* The patient by `shop_identifier` and `shop_customer_id`.
* The anamnesis by `anamnesis_uid`, its UID in RxScale. The anamnesis has to be submitted to
  RxScale first, for example with
  [Submit Anamnesis](https://docs.rxscale.com/api-reference/public/anamnesis-v4.md#submit-anamnesis), whose response
  carries this UID.

Unlike [Connect an Anamnesis to a Patient](https://docs.rxscale.com/api-reference/management/anamnesis.md#connect-an-anamnesis-to-a-patient),
a review request does not accept an external submission: `external_submission_uid`,
`external_identifier` and `provider_identifier` are refused with `400` as unknown fields. The UID
that [Submit External Anamnesis](https://docs.rxscale.com/api-reference/public/anamnesis-v4.md#submit-external-anamnesis)
returns is the submission's UID, not an anamnesis UID, and is answered with `404`.

If the anamnesis is not yet connected to a patient, the review request connects it to this
patient, just as the patient-connection endpoint does: the answers fill the patient's profile
fields, and the anamnesis appears on the patient in RxScale. If the anamnesis is already
connected to a **different** patient, the request is refused with `409` and nothing is created.

### Example Request

```bash theme={null}
curl -X POST "https://api.rxscale.com/v1/management/review-requests" \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "htv-rr-8f2c",
    "type": "repurchase",
    "shop_identifier": "menopause-de",
    "shop_customer_id": "7321456789012",
    "anamnesis_uid": "an-abc123",
    "fee_shop_order_external_id": "6012345678901"
  }'
```

### Response (201 Created)

```json theme={null}
{
  "uid": "rr-abc123",
  "external_id": "htv-rr-8f2c",
  "type": "repurchase",
  "status": "open",
  "created_at": 1794045600
}
```

| Field | Type | Description |
| - | - | - |
| `uid` | string | RxScale's ID of the review request. Use it to read or cancel the request |
| `external_id` | string | Your ID, as sent |
| `type` | string | The kind of review, for example `repurchase` |
| `status` | string | The current status. A new request is `open`. See [Status lifecycle](#status-lifecycle) |
| `created_at` | integer | Unix timestamp (seconds) when the request was created |

The response does not name the doctor. Read the request to see `doctor_uid`.

### Repeating a request

> [!NOTE]
>
> A call with an `external_id` you have already used creates nothing. It answers `200 OK` instead
> of `201` and returns the existing request in its current state, even when the rest of the body
> differs. A Shopify Flow retry, or a call whose response you did not receive, is therefore
> harmless.
>
> To ask for a new review, for example after a request was declined or cancelled, send a new
> `external_id`.

### Error Responses

| Code | Meaning |
| - | - |
| `400` | Validation error: a required field is missing (including `anamnesis_uid`), a value is invalid, or a field is not known (including the external-submission fields) |
| `401` | Missing or invalid API key |
| `403` | The API key lacks `review_request:write` |
| `404` | Shop, patient or anamnesis not found, or not part of your organisation |
| `409` | The anamnesis is already connected to a different patient |

Nothing is stored when a request is refused.

A `400` names the fields at fault:

```json theme={null}
{
  "error": {
    "external_id": ["Missing data for required field."],
    "type": ["Must be one of: repurchase."]
  }
}
```

A `404` carries one fixed message per resource. The message is the same whether the record does
not exist or belongs to another organisation, so it never confirms that a record is real. The
shop is checked first, then the anamnesis, then the patient.

| Message | Meaning |
| - | - |
| `Shop not found.` | No shop with this `shop_identifier` in your organisation |
| `Anamnesis not found.` | No anamnesis with this `anamnesis_uid` in your organisation |
| `Patient not found.` | No customer with this `shop_customer_id` in the shop |

```json theme={null}
{
  "error": "Patient not found."
}
```

A `409`:

```json theme={null}
{
  "error": "This anamnesis is already connected to a different patient."
}
```

## Get a Review Request

```bash theme={null}
GET /v1/management/review-requests/{review_request_uid}
```

Returns the current state of a review request. The body carries the same fields as the
`review_request_updated` webhook, plus the timestamps, so you can use it whenever you missed a
webhook.

**Required permission:** `review_request:read`

**review\_request\_uid** `string` *required*

The `uid` returned when you created the request

### Example Request

```bash theme={null}
curl -X GET "https://api.rxscale.com/v1/management/review-requests/rr-abc123" \
  -H "X-API-Key: your-api-key-here"
```

### Response (200 OK)

```json theme={null}
{
  "uid": "rr-abc123",
  "external_id": "htv-rr-8f2c",
  "type": "repurchase",
  "status": "draft_created",
  "created_at": 1794045600,
  "closed_at": null,
  "shop_identifier": "menopause-de",
  "shop_customer_id": "7321456789012",
  "anamnesis_uid": "an-abc123",
  "doctor_uid": "doc-abc123",
  "decline_reason": null,
  "booking": {
    "launch_url": "https://meet.rxscale.com/booking?launch=blc_Hq4...",
    "expires_at": 1795271400,
    "appointment_type_uid": "at-abc123"
  },
  "appointment": {
    "meeting_uid": "m-abc123",
    "status": "completed",
    "start_date": 1794301200
  },
  "draft_order": {
    "id": "gid://shopify/DraftOrder/1122334455",
    "name": "#D1042",
    "invoice_url": "https://menopause-de.myshopify.com/12345678/invoices/abc123"
  },
  "fee_shop_order_external_id": "6012345678901",
  "shop_order_external_id": null
}
```

| Field | Type | Description |
| - | - | - |
| `uid` | string | RxScale's ID of the review request |
| `external_id` | string | Your ID, as sent at creation |
| `type` | string | The kind of review, for example `repurchase` |
| `status` | string | The current status. See [Status lifecycle](#status-lifecycle) |
| `created_at` | integer | Unix timestamp when the request was created |
| `closed_at` | integer or null | Unix timestamp when the request reached a final status (`completed`, `declined` or `cancelled`), otherwise `null` |
| `shop_identifier` | string | The shop the request was sent for |
| `shop_customer_id` | string or null | Your shop's ID for the patient |
| `anamnesis_uid` | string | UID of the anamnesis in RxScale. For an external submission, this is the anamnesis RxScale created from it |
| `doctor_uid` | string or null | UID of the doctor who has the request, or `null` while it is unassigned. A doctor who declines an unassigned request is named here |
| `decline_reason` | string or null | The reason the doctor or admin gave when declining, otherwise `null` |
| `booking` | object or null | The current booking link, if the doctor requested a video appointment, otherwise `null` |
| `booking.launch_url` | string | The link the patient opens to book |
| `booking.expires_at` | integer | Unix timestamp until which the patient can book through the link. If the request was cancelled, declined or completed before the patient used the link, the time the link stopped working |
| `booking.appointment_type_uid` | string | The appointment type the doctor chose |
| `appointment` | object or null | The appointment the patient booked through the current booking link, otherwise `null` |
| `appointment.meeting_uid` | string | The appointment's ID. Use it to cancel the appointment with [`POST /v1/management/scheduling/appointments/{meeting_uid}/cancel`](https://docs.rxscale.com/api-reference/management/scheduling#cancel-an-appointment) |
| `appointment.status` | string | The appointment's status: `held`, `confirmed`, `cancelled`, `expired`, `completed` or `no_show` |
| `appointment.start_date` | integer | Unix timestamp of the appointment's start |
| `draft_order` | object or null | The draft order the doctor created in your shop, otherwise `null` |
| `draft_order.id` | string | Shopify's ID of the draft order |
| `draft_order.name` | string | The draft order's name, for example `#D1042` |
| `draft_order.invoice_url` | string | The draft order's invoice link, which the patient pays through |
| `fee_shop_order_external_id` | string or null | Your shop's ID of the prescription-fee order, as you sent it at creation, otherwise `null` |
| `shop_order_external_id` | string or null | Your shop's ID of the paid treatment order, once RxScale has linked it (for Shopify the order ID, for example `6099887766554`), otherwise `null` |

> [!NOTE]
>
> Unlike the webhook, which carries `booking` only with `video_requested`, this response always
> includes the current booking link when there is one. If the doctor created a new link, it is the
> newest one.

> [!NOTE]
>
> When the patient books through the link, you receive `review_request_updated` with
> `change: "appointment_booked"` and the appointment's `meeting_uid` in `appointment`. From then on,
> `appointment` is filled in this response and in every later `review_request_updated`. The booking
> is also announced by the [`patient_doctor_meeting_updated`](https://docs.rxscale.com/webhooks/events#patient_doctor_meeting_updated)
> webhook, which reports every later change to the appointment.

### Error Responses

| Code | Meaning |
| - | - |
| `401` | Missing or invalid API key |
| `403` | The API key lacks `review_request:read` |
| `404` | Review request not found, or not part of your organisation |

```json theme={null}
{
  "error": "Resource not found"
}
```

## Cancel a Review Request

```bash theme={null}
POST /v1/management/review-requests/{review_request_uid}/cancel
```

Cancels a review request that is still open, for example because the patient withdrew or you
refunded the fee.

**Required permission:** `review_request:write`

**review\_request\_uid** `string` *required*

The `uid` returned when you created the request

**reason** `string`

Why the request is cancelled, at most 2000 characters. Recorded in the request's history,
which your admins see in the admin portal.

### What cancelling does

* The status becomes `cancelled`, `closed_at` is set, and you receive `review_request_updated`
  with `change: "cancelled"`.
* The request leaves the doctor's list. A doctor who still has it open is told, on their next
  action, that the shop cancelled it.
* A booking link the patient has not used yet stops working at once.
* An appointment the patient **already booked** stays in place. Take its `meeting_uid` from the
  `appointment_booked` webhook or from `appointment.meeting_uid` when you read the request, then
  cancel it with
  [`POST /v1/management/scheduling/appointments/{meeting_uid}/cancel`](https://docs.rxscale.com/api-reference/management/scheduling#cancel-an-appointment),
  which applies that endpoint's refund rules.
* A draft order the doctor **already created** stays in your shop. Delete it in Shopify. If the
  patient pays it anyway, RxScale processes the order as usual, and the request stays
  `cancelled`.

Cancelling a request that is already final (`completed`, `declined` or `cancelled`) changes
nothing and sends no webhook: it returns the request unchanged with `200 OK`. A repeated cancel
is therefore harmless.

### Example Request

```bash theme={null}
curl -X POST "https://api.rxscale.com/v1/management/review-requests/rr-abc123/cancel" \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Patient withdrew"
  }'
```

### Response (200 OK)

```json theme={null}
{
  "uid": "rr-abc123",
  "external_id": "htv-rr-8f2c",
  "type": "repurchase",
  "status": "cancelled",
  "created_at": 1794045600
}
```

The fields are the same as in the create response. `status` is `cancelled`, or the final status
the request already had.

### Error Responses

| Code | Meaning |
| - | - |
| `400` | Validation error, for example a `reason` longer than 2000 characters |
| `401` | Missing or invalid API key |
| `403` | The API key lacks `review_request:write` |
| `404` | Review request not found, or not part of your organisation |

## Status Lifecycle

```
open ──create treatment──▶ draft_created ──order paid──▶ completed
  │
  ├──request video──▶ video_requested ──create treatment──▶ draft_created
  │
  └──decline──▶ declined

any open status ──cancel──▶ cancelled
```

| Status | Open or final | Meaning |
| - | - | - |
| `open` | Open | Waiting for the doctor |
| `video_requested` | Open | The doctor sent the patient a booking link. The request stays here until the doctor creates the treatment or declines, usually after the appointment |
| `draft_created` | Open | The doctor created the treatment as a draft order in your shop. Waiting for the patient to pay it |
| `completed` | Final | The patient paid the draft order and RxScale linked it to the request |
| `declined` | Final | A doctor or an admin declined the request |
| `cancelled` | Final | You or an admin cancelled the request |

* A final status never changes again.
* An open request can be declined or cancelled from any open status. Declining also ends an
  unused booking link.
* Taking or forwarding a request changes its doctor, not its status.
* When the patient books through the link, the status does not change either. You receive
  `appointment_booked` with the appointment.
* The doctor can request a video appointment after creating the draft order. The status then
  stays `draft_created`, and you receive `video_requested` with the link.
* A request has at most one booking link that can be used. The doctor can request a new link
  only once the current one expired unused, or once the appointment booked through it no longer
  stands (cancelled, expired, a no-show, or completed). You then receive another
  `video_requested` with the new link, which replaces the old one. The status does not change.

Your admins see your organisation's review requests in the admin portal. They can assign or
reassign the doctor, decline and cancel. Creating the treatment and requesting a video
appointment stay with the doctor.

## Completing the Request with the Paid Order

When the doctor creates the treatment, the draft order in your shop carries the order attribute
`_rxscale_review_request_uid` with the request's `uid`. When the patient pays the draft order and
the order reaches RxScale, RxScale links it to the request, sets `completed`, and sends
`review_request_updated` with `change: "completed"` and the order's ID in
`shop_order_external_id`. A booking link the patient has not used stops working. The
prescription then follows the normal review and signing flow.

A request is completed by one order. If a further paid order carries the same
`_rxscale_review_request_uid`, RxScale processes that order as usual, but the request keeps the
first order and no webhook is sent.

> [!WARNING]
>
> Keep the `_rxscale_review_request_uid` attribute on the order. Do not remove or overwrite it, for
> example in Shopify Flow, in an app, or when you edit the draft. Without it, RxScale cannot link
> the paid order: the order is processed as usual, but the request stays `draft_created`.

RxScale only accepts the attribute for a request of your organisation and of the same patient.
Any other value is ignored, and the order is processed without the link.

## Related Topics

* [`review_request_updated` webhook](https://docs.rxscale.com/webhooks/events#review-request-updated) -- The payload and
  every `change` value.
* [Anamnesis](https://docs.rxscale.com/api-reference/management/anamnesis.md) -- The patient-connection endpoint and its
  addressing modes.
* [Scheduling](https://docs.rxscale.com/api-reference/management/scheduling.md) -- Cancel an appointment the patient booked.
* [Review Requests for doctors](https://docs.rxscale.com/for-doctors/review-requests.md) -- What the doctor sees and does.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.