Learn · RxPhoto
Recipes — RxPhoto
The reference documents one endpoint at a time. Real features chain several together. Here are end-to-end workflows for the RxPhoto API — showing how one call's output feeds the next.
Request/response fields below are examples to show the flow. Confirm the exact required fields for each resource in the RxPhoto reference before building against it.
The setup these recipes share
Every snippet below assumes you've already built an authenticated client, exactly as
in the RxPhoto quickstart — so we don't repeat auth
in each step. It's a client with your token set as the
Authorization header on every call:
# Each curl below omits this for brevity — add it to every request:
# -H "Authorization: Token YOUR_TOKEN"
# Base URL for the paths below: https://api.rxphoto.com/api/v3const base = "https://api.rxphoto.com/api/v3";
const authHeader = { Authorization: "Token YOUR_TOKEN" };
const json = { ...authHeader, "Content-Type": "application/json" };import requests
base = "https://api.rxphoto.com/api/v3"
s = requests.Session()
s.headers.update({"Authorization": "Token YOUR_TOKEN"}) # sent on every requestusing System;
using System.Net.Http;
var http = new HttpClient {
BaseAddress = new Uri("https://api.rxphoto.com/api/v3/")
};
http.DefaultRequestHeaders.Add("Authorization", "Token YOUR_TOKEN");1 · Find or create a patient
Most workflows start from a patient. You can search first and reuse a match, but you
don't have to guard against duplicates yourself: the create endpoint returns a
validation error when first_name, last_name and
birth_date all match an existing record. If you intend to create the
record anyway, resend the request with force_create=true.
# 1. Look for an existing patient by name
curl "https://api.rxphoto.com/api/v3/patient/?last_name__iexact=Smith&first_name__istartswith=Jane&page_size=5" \
-H "Authorization: Token YOUR_TOKEN"
# → { "count": 1, "results": [ { "id": 12345, "first_name": "Jane", ... } ] }
# 2. Create one only if nothing matched
curl -X POST "https://api.rxphoto.com/api/v3/patient/" \
-H "Authorization: Token YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{"first_name": "Jane", "last_name": "Smith", "birth_date": "1985-03-15", "email": "jane.smith@example.com"}'
# → { "id": 12345, ... } ← reuse this integer id below// 1. Look for an existing patient by name
const qs = new URLSearchParams({ last_name__iexact: "Smith", first_name__istartswith: "Jane", page_size: "5" });
const searchRes = await fetch(`${base}/patient/?${qs}`, { headers: authHeader });
const search = await searchRes.json();
// 2. Create one only if nothing matched
let patient = search.results[0];
if (!patient) {
const createRes = await fetch(`${base}/patient/`, {
method: "POST",
headers: json,
body: JSON.stringify({ first_name: "Jane", last_name: "Smith", birth_date: "1985-03-15", email: "jane.smith@example.com" }),
});
patient = await createRes.json();
}
const patientId = patient.id; // integer id, reused below# 1. Look for an existing patient by name
res = s.get(f"{base}/patient/", params={
"last_name__iexact": "Smith",
"first_name__istartswith": "Jane",
"page_size": 5,
})
matches = res.json()["results"]
# 2. Create one only if nothing matched
if matches:
patient = matches[0]
else:
patient = s.post(f"{base}/patient/", json={
"first_name": "Jane",
"last_name": "Smith",
"birth_date": "1985-03-15",
"email": "jane.smith@example.com",
}).json()
patient_id = patient["id"] # integer id, reused belowusing System.Net.Http.Json;
// 1. Look for an existing patient by name
var found = await http.GetFromJsonAsync<JsonElement>(
"patient/?last_name__iexact=Smith&first_name__istartswith=Jane&page_size=5");
var results = found.GetProperty("results");
// 2. Create one only if nothing matched
JsonElement patient;
if (results.GetArrayLength() > 0) {
patient = results[0];
} else {
var created = await http.PostAsJsonAsync("patient/", new {
first_name = "Jane", last_name = "Smith", birth_date = "1985-03-15", email = "jane.smith@example.com",
});
patient = await created.Content.ReadFromJsonAsync<JsonElement>();
}
var patientId = patient.GetProperty("id").GetInt32(); // integer id, reused belowAn explicit search isn't required just to avoid duplicates — the create endpoint
rejects a patient whose first_name, last_name and
birth_date exactly match an existing one, so you learn about the clash
from the 400 response. Pass force_create=true to create
the record despite the match. The __iexact / __istartswith
filters (see Filtering) are still useful
when you want to find and reuse an existing record rather than add one.
2 · Build a before/after gallery
RxPhoto's signature feature is the before/after comparison. The pieces:
images belong to a patient, a gallery holds a set
of comparisons, and each image pair links a "before" and an "after"
image inside a gallery. So you create the gallery first, then add pairs to it. The
add_pair action requires three query parameters — the two image IDs
(before_id, after_id) and a procedure_details
label — and a JSON body (the GalleryCreateUpdate shape,
whose only required field is name).
To read the images, videos, notes, forms and so on that belong to a patient, the suggested approach is the shortcut endpoints nested under the patient rather than filtering the top-level collections:
GET /api/v3/patient/{id}/images/GET /api/v3/patient/{id}/videos/
Unlike the paginated top-level collections, these shortcuts return a
bare JSON array (no { count, next, previous, results }
envelope) — iterate the decoded array directly. The only documented query
parameter is expand. See the
reference for the full set of patient shortcuts.
# 1. List the patient's images to pick a before/after set.
# The patient shortcut endpoint is the preferred way to fetch a
# patient's media (images, videos, notes, forms, …).
curl "https://api.rxphoto.com/api/v3/patient/PATIENT_ID/images/" \
-H "Authorization: Token YOUR_TOKEN"
# 2. Create a gallery (only "name" is required)
curl -X POST "https://api.rxphoto.com/api/v3/gallery/" \
-H "Authorization: Token YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "Consult - session 1"}'
# 3. Add a before/after pair to the gallery. before_id, after_id and
# procedure_details are required query params; a JSON body (name required) is
# also required.
curl -X POST "https://api.rxphoto.com/api/v3/gallery/GALLERY_ID/add_pair/?before_id=BEFORE_ID&after_id=AFTER_ID&procedure_details=Rhinoplasty" \
-H "Authorization: Token YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "Consult - session 1"}'// 1. List the patient's images to pick a before/after set.
// The patient shortcut endpoint is the preferred way to fetch a
// patient's media (images, videos, notes, forms, …).
const imgRes = await fetch(`${base}/patient/${patientId}/images/`, { headers: authHeader });
const imgs = await imgRes.json(); // bare array, not a {results} envelope
const beforeId = imgs[0].id;
const afterId = imgs[imgs.length - 1].id;
// 2. Create a gallery (only "name" is required)
const galleryRes = await fetch(`${base}/gallery/`, {
method: "POST",
headers: json,
body: JSON.stringify({ name: "Consult — session 1" }),
});
const gallery = await galleryRes.json();
// 3. Add a before/after pair to the gallery. before_id, after_id and
// procedure_details are required query params; a JSON body (name required)
// is also required.
const addQs = new URLSearchParams({
before_id: beforeId,
after_id: afterId,
procedure_details: "Rhinoplasty",
});
await fetch(`${base}/gallery/${gallery.id}/add_pair/?${addQs}`, {
method: "POST",
headers: json,
body: JSON.stringify({ name: "Consult — session 1" }),
});# 1. List the patient's images to pick a before/after set.
# The patient shortcut endpoint is the preferred way to fetch a
# patient's media (images, videos, notes, forms, …).
imgs = s.get(f"{base}/patient/{patient_id}/images/").json() # bare array, not a {results} envelope
before_id, after_id = imgs[0]["id"], imgs[-1]["id"]
# 2. Create a gallery (only "name" is required)
gallery = s.post(f"{base}/gallery/", json={
"name": "Consult — session 1",
}).json()
# 3. Add a before/after pair to the gallery. before_id, after_id and
# procedure_details are required query params; a JSON body (name required)
# is also required.
s.post(
f"{base}/gallery/{gallery['id']}/add_pair/",
params={
"before_id": before_id,
"after_id": after_id,
"procedure_details": "Rhinoplasty",
},
json={"name": "Consult — session 1"},
)// 1. List the patient's images to pick a before/after set.
// The patient shortcut endpoint is the preferred way to fetch a
// patient's media (images, videos, notes, forms, …).
// bare array, not a {results} envelope — use the decoded array directly
var imgs = await http.GetFromJsonAsync<JsonElement>($"patient/{patientId}/images/");
var beforeId = imgs[0].GetProperty("id").GetInt32();
var afterId = imgs[imgs.GetArrayLength() - 1].GetProperty("id").GetInt32();
// 2. Create a gallery (only "name" is required)
var galRes = await http.PostAsJsonAsync("gallery/", new { name = "Consult — session 1" });
var gallery = await galRes.Content.ReadFromJsonAsync<JsonElement>();
var galleryId = gallery.GetProperty("id").GetInt32();
// 3. Add a before/after pair to the gallery. before_id, after_id and
// procedure_details are required query params; a JSON body (name required)
// is also required.
await http.PostAsJsonAsync(
$"gallery/{galleryId}/add_pair/?before_id={beforeId}&after_id={afterId}&procedure_details=Rhinoplasty",
new { name = "Consult — session 1" });Galleries expose action endpoints beyond plain CRUD — for example
/gallery/{id}/pairs/ to list its pairs and
/gallery/{id}/set_procedure/ to tag it. See the
reference for the full set and each body.
3 · Share a gallery
Once a gallery is assembled you can share it or export it. RxPhoto offers both
app/web sharing and file exports. The file exports require a
template query parameter (the integer ID of the export template to
render with).
# Share on the patient-facing web viewer (share_on_web is a GET)
curl "https://api.rxphoto.com/api/v3/gallery/GALLERY_ID/share_on_web/" \
-H "Authorization: Token YOUR_TOKEN"
# ...or export the gallery as a file (template is a required query param)
curl "https://api.rxphoto.com/api/v3/gallery/GALLERY_ID/download_pdf/?template=TEMPLATE_ID" \
-H "Authorization: Token YOUR_TOKEN" -o gallery.pdf # application/pdf
curl "https://api.rxphoto.com/api/v3/gallery/GALLERY_ID/download_zip/?template=TEMPLATE_ID" \
-H "Authorization: Token YOUR_TOKEN" -o gallery.zip # image archiveconst galleryId = gallery.id;
// Share on the patient-facing web viewer (share_on_web is a GET)
await fetch(`${base}/gallery/${galleryId}/share_on_web/`, { headers: authHeader });
// ...or export the gallery as a file (template is a required query param)
const pdf = await fetch(`${base}/gallery/${galleryId}/download_pdf/?template=TEMPLATE_ID`, { headers: authHeader }); // application/pdf
const zip = await fetch(`${base}/gallery/${galleryId}/download_zip/?template=TEMPLATE_ID`, { headers: authHeader }); // image archivegallery_id = gallery["id"]
# Share on the patient-facing web viewer (share_on_web is a GET)
s.get(f"{base}/gallery/{gallery_id}/share_on_web/")
# ...or export the gallery as a file (template is a required query param)
pdf = s.get(f"{base}/gallery/{gallery_id}/download_pdf/", params={"template": TEMPLATE_ID}) # application/pdf
zip_ = s.get(f"{base}/gallery/{gallery_id}/download_zip/", params={"template": TEMPLATE_ID}) # image archive// Share on the patient-facing web viewer (share_on_web is a GET)
await http.GetAsync($"gallery/{galleryId}/share_on_web/");
// ...or export the gallery as a file (template is a required query param)
var pdf = await http.GetByteArrayAsync($"gallery/{galleryId}/download_pdf/?template=TEMPLATE_ID"); // application/pdf
var zip = await http.GetByteArrayAsync($"gallery/{galleryId}/download_zip/?template=TEMPLATE_ID"); // image archiveBulk and archive exports may be prepared in the background. Endpoints such as
/gallery/download_status/ let you poll for readiness before you
fetch the file. Check each endpoint's response in the
reference.