Recalls
Open, follow, acknowledge and report on recalls.
Endpoints
-
GET
/recalls/List recalls -
POST
/recalls/Open a recall -
POST
/recalls/drill/Run a recall drill -
GET
/recalls/{id}/Fetch a recall -
GET
/recalls/{id}/tree/Get a recall's distribution tree -
GET
/recalls/{id}/affected/List affected facilities -
GET
/recalls/{id}/affected/{facility_schema}/units/List affected units at a facility -
POST
/recalls/{id}/acknowledge/Acknowledge a recall -
GET
/recalls/{id}/report/Get a recall report
List recalls
https://api.dawatrack.com/partner-api/v1/recalls/
Recalls you opened as a manufacturer, or that affect a facility you're linked to.
recalls:read
Limit 5,000 per day
Query parameters
-
orderingstring OptionalWhich field to use when ordering the results.
-
pageinteger OptionalA page number within the paginated result set.
-
page_sizeinteger OptionalNumber of results to return per page.
-
searchstring OptionalA search term.
Example request
curl -X GET https://api.dawatrack.com/partner-api/v1/recalls/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN"
import requests
token = "..." # from POST /oauth/token/
response = requests.get(
"https://api.dawatrack.com/partner-api/v1/recalls/",
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(await response.json());
Response fields
A page of results. These are the fields of each item in results. See Pagination.
-
idinteger Read-onlyThe record's id.
-
medicine_namestringThe product's name.
-
gtinstringThe product's GS1 GTIN, as 14 digits.
-
batch_numberstringThe batch or lot number.
-
additional_batch_numbersarray of stringsExtra batch numbers this recall also covers, beyond batch_number.
-
ssccstring Read-onlySet when the recall is of one container and what it holds; blank for a batch.
-
all_batch_numbersarray of strings Read-onlybatch_number plus additional_batch_numbers, deduped - the full set this recall covers.
-
reasonenumPossible values:QUALITY_DEFECTCONTAMINATIONMISLABELINGADVERSE_REACTIONREGULATORY_WITHDRAWALOTHER -
reason_displaystring Read-onlyThe recall reason, in words.
-
severity_classenumPossible values:CLASS_ICLASS_IICLASS_IIICAUTION_IN_USE -
severity_class_displaystring Read-onlyThe severity class, in words.
-
trigger_typestringVOLUNTARY or STATUTORY. Recalls opened through the API are always VOLUNTARY.
-
ppb_referencestringPPB's reference for the recall, once assigned.
-
instructionsstringWhat facilities should do.
-
recall_levelstringHow far into the supply chain the recall reaches, in PPB's own published wording.
-
geographic_scopestringGeographic reach PPB publishes on the notice.
-
patient_instructionsstringWhat patients/caregivers who may have this product should do, if applicable.
-
manufacturerstring Read-onlyThe marketing authorisation holder or manufacturer, from the product's catalogue record.
-
generic_namestring Read-onlyThe active ingredient, from the product's catalogue record.
-
local_technical_representativestring Read-onlyPPB's registered local technical representative for the recalled product.
-
statusstring -
initiated_atdatetimeWhen the recall was opened.
-
closed_atdatetime or nullWhen the recall was closed, if it is closed.
-
defect_discovered_atdatetime or nullPPB Guidelines §2.10.3b: when this defect was first discovered.
-
ppb_notification_due_atdatetime or nullPPB Guidelines §2.10.3b: 24h after defect_discovered_at.
-
ppb_notified_atdatetime or null Read-onlyWhen the supplier recorded that PPB was notified. Set in the supplier portal, not through the API.
-
initial_report_due_atdatetime or nullPPB Guidelines §2.13.1a: an initial report is due within 1 week.
-
progress_report_due_atdatetime or null -
reconciliation_due_atdatetime or null -
facilities_acknowledgedinteger Read-only -
facilities_totalinteger Read-only -
is_drillboolean Read-only -
drill_facilities_reachedinteger Read-only -
drill_facilities_by_hopobject Read-only
Example response
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 42,
"medicine_name": "Amoxicillin 500 mg capsules",
"gtin": "06164004321562",
"batch_number": "BTH-2026-041",
"additional_batch_numbers": [],
"sscc": "106164004000007317",
"all_batch_numbers": [
"BTH-2026-041"
],
"reason": "CONTAMINATION",
"reason_display": "Contamination",
"severity_class": "CLASS_II",
"severity_class_display": "Class II",
"trigger_type": "VOLUNTARY",
"ppb_reference": "",
"instructions": "Quarantine the batch and await collection instructions.",
"recall_level": "Retail level",
"geographic_scope": "Kenya",
"patient_instructions": "",
"manufacturer": "Example Pharmaceuticals Ltd",
"generic_name": "Amoxicillin",
"local_technical_representative": "",
"status": "OPEN",
"initiated_at": "2026-10-10T09:00:00Z",
"closed_at": "2026-10-10T09:00:00Z",
"defect_discovered_at": "2026-10-10T09:00:00Z",
"ppb_notification_due_at": "2026-10-10T09:00:00Z",
"ppb_notified_at": "2026-10-10T09:00:00Z",
"initial_report_due_at": "2026-10-10T09:00:00Z",
"progress_report_due_at": "2026-10-10T09:00:00Z",
"reconciliation_due_at": "2026-10-10T09:00:00Z",
"facilities_acknowledged": 4,
"facilities_total": 6,
"is_drill": true,
"drill_facilities_reached": 6,
"drill_facilities_by_hop": {
"1": 4,
"2": 2
}
}
]
}
Status codes
200 | OK |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
429 | Rate limit exceeded |
Open a recall
https://api.dawatrack.com/partner-api/v1/recalls/
Open a voluntary recall for a batch you have shipped.
recalls:write
Manufacturers only
Production credentials only
Limit 5,000 per day
Limit 20 per day
Every affected facility is notified before the call returns, including facilities that received the batch through wholesale resale. Unless the class is CAUTION_IN_USE, the batch is quarantined at each facility.
Re-notification of facilities that haven't acknowledged follows the severity class. Recalls opened through the API are always VOLUNTARY.
To try this without notifying anyone, use the recall drill.
A batch has one open recall at a time: when one already covers it (the whole batch, or the same sscc), a new one is 409, with that recall's recall_id. Escalate or update it instead. A recall of the whole batch while one of its containers is recalled is allowed.
A manufacturer with no supplier account on DawaTrack names the gtin too, and may recall only a product it is the registered manufacturer of (otherwise 403). It owns the recall, which reaches every facility that received the batch, whoever shipped it, and every supplier that shipped it is told. A batch number with no event of that GTIN on DawaTrack is 404; a recall by sscc is a supplier account's (400). It follows the recall with the same endpoints, and gets its PPB report.
Request body
JSON. Send Content-Type: application/json.
-
batch_numberstring OptionalThe batch to recall. Optional with sscc: then the batch of what it holds.
-
additional_batch_numbersarray of strings OptionalExtra batch numbers of the same product/defect this recall should also cover. Each must be one your supplier account shipped.
-
ssccstring OptionalRecall one container (GS1 SSCC) and what it holds, through splits and repacks, instead of the whole batch. It must be one your supplier account packed or shipped.
-
reasonenum RequiredWhy the batch is being recalled.
Possible values:QUALITY_DEFECTCONTAMINATIONMISLABELINGADVERSE_REACTIONREGULATORY_WITHDRAWALOTHER -
severity_classenum RequiredThe PPB severity class. CAUTION_IN_USE alerts facilities without quarantining the batch.
Possible values:CLASS_ICLASS_IICLASS_IIICAUTION_IN_USE -
instructionsstring RequiredWhat facilities should do.
-
defect_discovered_atdatetime OptionalPPB Guidelines §2.10.3b: when this defect was first discovered - you must notify PPB within 24h of this. Defaults to now if omitted.
Example request
curl -X POST https://api.dawatrack.com/partner-api/v1/recalls/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"batch_number": "BTH-2026-041",
"reason": "CONTAMINATION",
"severity_class": "CLASS_II",
"instructions": "Quarantine the batch and await collection instructions."
}'
import uuid
import requests
token = "..." # from POST /oauth/token/
response = requests.post(
"https://api.dawatrack.com/partner-api/v1/recalls/",
headers={"Authorization": f"Bearer {token}", "Idempotency-Key": str(uuid.uuid4())},
json={
"batch_number": "BTH-2026-041",
"reason": "CONTAMINATION",
"severity_class": "CLASS_II",
"instructions": "Quarantine the batch and await collection instructions."
},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"batch_number": "BTH-2026-041",
"reason": "CONTAMINATION",
"severity_class": "CLASS_II",
"instructions": "Quarantine the batch and await collection instructions."
}),
});
console.log(await response.json());
Response fields
-
idinteger Read-onlyThe record's id.
-
medicine_namestringThe product's name.
-
gtinstringThe product's GS1 GTIN, as 14 digits.
-
batch_numberstringThe batch or lot number.
-
additional_batch_numbersarray of stringsExtra batch numbers this recall also covers, beyond batch_number.
-
ssccstring Read-onlySet when the recall is of one container and what it holds; blank for a batch.
-
all_batch_numbersarray of strings Read-onlybatch_number plus additional_batch_numbers, deduped - the full set this recall covers.
-
reasonenumPossible values:QUALITY_DEFECTCONTAMINATIONMISLABELINGADVERSE_REACTIONREGULATORY_WITHDRAWALOTHER -
reason_displaystring Read-onlyThe recall reason, in words.
-
severity_classenumPossible values:CLASS_ICLASS_IICLASS_IIICAUTION_IN_USE -
severity_class_displaystring Read-onlyThe severity class, in words.
-
trigger_typestringVOLUNTARY or STATUTORY. Recalls opened through the API are always VOLUNTARY.
-
ppb_referencestringPPB's reference for the recall, once assigned.
-
instructionsstringWhat facilities should do.
-
recall_levelstringHow far into the supply chain the recall reaches, in PPB's own published wording.
-
geographic_scopestringGeographic reach PPB publishes on the notice.
-
patient_instructionsstringWhat patients/caregivers who may have this product should do, if applicable.
-
manufacturerstring Read-onlyThe marketing authorisation holder or manufacturer, from the product's catalogue record.
-
generic_namestring Read-onlyThe active ingredient, from the product's catalogue record.
-
local_technical_representativestring Read-onlyPPB's registered local technical representative for the recalled product.
-
statusstring -
initiated_atdatetimeWhen the recall was opened.
-
closed_atdatetime or nullWhen the recall was closed, if it is closed.
-
defect_discovered_atdatetime or nullPPB Guidelines §2.10.3b: when this defect was first discovered.
-
ppb_notification_due_atdatetime or nullPPB Guidelines §2.10.3b: 24h after defect_discovered_at.
-
ppb_notified_atdatetime or null Read-onlyWhen the supplier recorded that PPB was notified. Set in the supplier portal, not through the API.
-
initial_report_due_atdatetime or nullPPB Guidelines §2.13.1a: an initial report is due within 1 week.
-
progress_report_due_atdatetime or null -
reconciliation_due_atdatetime or null -
facilities_acknowledgedinteger Read-only -
facilities_totalinteger Read-only -
is_drillboolean Read-only -
drill_facilities_reachedinteger Read-only -
drill_facilities_by_hopobject Read-only
Example response
{
"id": 42,
"medicine_name": "Amoxicillin 500 mg capsules",
"gtin": "06164004321562",
"batch_number": "BTH-2026-041",
"additional_batch_numbers": [],
"sscc": "106164004000007317",
"all_batch_numbers": [
"BTH-2026-041"
],
"reason": "CONTAMINATION",
"reason_display": "Contamination",
"severity_class": "CLASS_II",
"severity_class_display": "Class II",
"trigger_type": "VOLUNTARY",
"ppb_reference": "",
"instructions": "Quarantine the batch and await collection instructions.",
"recall_level": "Retail level",
"geographic_scope": "Kenya",
"patient_instructions": "",
"manufacturer": "Example Pharmaceuticals Ltd",
"generic_name": "Amoxicillin",
"local_technical_representative": "",
"status": "OPEN",
"initiated_at": "2026-10-10T09:00:00Z",
"closed_at": "2026-10-10T09:00:00Z",
"defect_discovered_at": "2026-10-10T09:00:00Z",
"ppb_notification_due_at": "2026-10-10T09:00:00Z",
"ppb_notified_at": "2026-10-10T09:00:00Z",
"initial_report_due_at": "2026-10-10T09:00:00Z",
"progress_report_due_at": "2026-10-10T09:00:00Z",
"reconciliation_due_at": "2026-10-10T09:00:00Z",
"facilities_acknowledged": 4,
"facilities_total": 6,
"is_drill": true,
"drill_facilities_reached": 6,
"drill_facilities_by_hop": {
"1": 4,
"2": 2
}
}
Status codes
201 | Created |
400 | The request is invalid, for example a malformed GTIN or a missing field |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
404 | Not found, or not visible to your credential |
409 | Conflict: a request with this Idempotency-Key is in progress, or the record already exists |
429 | Rate limit exceeded |
Run a recall drill
https://api.dawatrack.com/partner-api/v1/recalls/drill/
Rehearse a recall without notifying anyone, for PPB's annual recall-system challenge.
recalls:write
Manufacturers only
Limit 5,000 per day
Takes the same body as opening a recall. It counts how many facilities a real recall would reach (drill_facilities_reached, by hop in drill_facilities_by_hop) but sends nothing and quarantines nothing.
The returned record has is_drill: true and is your audit evidence. Drills never appear in the public recall lookup.
Request body
JSON. Send Content-Type: application/json.
-
batch_numberstring OptionalThe batch to recall. Optional with sscc: then the batch of what it holds.
-
additional_batch_numbersarray of strings OptionalExtra batch numbers of the same product/defect this recall should also cover. Each must be one your supplier account shipped.
-
ssccstring OptionalRecall one container (GS1 SSCC) and what it holds, through splits and repacks, instead of the whole batch. It must be one your supplier account packed or shipped.
-
reasonenum RequiredWhy the batch is being recalled.
Possible values:QUALITY_DEFECTCONTAMINATIONMISLABELINGADVERSE_REACTIONREGULATORY_WITHDRAWALOTHER -
severity_classenum RequiredThe PPB severity class. CAUTION_IN_USE alerts facilities without quarantining the batch.
Possible values:CLASS_ICLASS_IICLASS_IIICAUTION_IN_USE -
instructionsstring RequiredWhat facilities should do.
-
defect_discovered_atdatetime OptionalPPB Guidelines §2.10.3b: when this defect was first discovered - you must notify PPB within 24h of this. Defaults to now if omitted.
Example request
curl -X POST https://api.dawatrack.com/partner-api/v1/recalls/drill/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"batch_number": "BTH-2026-041",
"reason": "CONTAMINATION",
"severity_class": "CLASS_II",
"instructions": "DRILL - no action required."
}'
import uuid
import requests
token = "..." # from POST /oauth/token/
response = requests.post(
"https://api.dawatrack.com/partner-api/v1/recalls/drill/",
headers={"Authorization": f"Bearer {token}", "Idempotency-Key": str(uuid.uuid4())},
json={
"batch_number": "BTH-2026-041",
"reason": "CONTAMINATION",
"severity_class": "CLASS_II",
"instructions": "DRILL - no action required."
},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/drill/", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"batch_number": "BTH-2026-041",
"reason": "CONTAMINATION",
"severity_class": "CLASS_II",
"instructions": "DRILL - no action required."
}),
});
console.log(await response.json());
Response fields
-
idinteger Read-onlyThe record's id.
-
medicine_namestringThe product's name.
-
gtinstringThe product's GS1 GTIN, as 14 digits.
-
batch_numberstringThe batch or lot number.
-
additional_batch_numbersarray of stringsExtra batch numbers this recall also covers, beyond batch_number.
-
ssccstring Read-onlySet when the recall is of one container and what it holds; blank for a batch.
-
all_batch_numbersarray of strings Read-onlybatch_number plus additional_batch_numbers, deduped - the full set this recall covers.
-
reasonenumPossible values:QUALITY_DEFECTCONTAMINATIONMISLABELINGADVERSE_REACTIONREGULATORY_WITHDRAWALOTHER -
reason_displaystring Read-onlyThe recall reason, in words.
-
severity_classenumPossible values:CLASS_ICLASS_IICLASS_IIICAUTION_IN_USE -
severity_class_displaystring Read-onlyThe severity class, in words.
-
trigger_typestringVOLUNTARY or STATUTORY. Recalls opened through the API are always VOLUNTARY.
-
ppb_referencestringPPB's reference for the recall, once assigned.
-
instructionsstringWhat facilities should do.
-
recall_levelstringHow far into the supply chain the recall reaches, in PPB's own published wording.
-
geographic_scopestringGeographic reach PPB publishes on the notice.
-
patient_instructionsstringWhat patients/caregivers who may have this product should do, if applicable.
-
manufacturerstring Read-onlyThe marketing authorisation holder or manufacturer, from the product's catalogue record.
-
generic_namestring Read-onlyThe active ingredient, from the product's catalogue record.
-
local_technical_representativestring Read-onlyPPB's registered local technical representative for the recalled product.
-
statusstring -
initiated_atdatetimeWhen the recall was opened.
-
closed_atdatetime or nullWhen the recall was closed, if it is closed.
-
defect_discovered_atdatetime or nullPPB Guidelines §2.10.3b: when this defect was first discovered.
-
ppb_notification_due_atdatetime or nullPPB Guidelines §2.10.3b: 24h after defect_discovered_at.
-
ppb_notified_atdatetime or null Read-onlyWhen the supplier recorded that PPB was notified. Set in the supplier portal, not through the API.
-
initial_report_due_atdatetime or nullPPB Guidelines §2.13.1a: an initial report is due within 1 week.
-
progress_report_due_atdatetime or null -
reconciliation_due_atdatetime or null -
facilities_acknowledgedinteger Read-only -
facilities_totalinteger Read-only -
is_drillboolean Read-only -
drill_facilities_reachedinteger Read-only -
drill_facilities_by_hopobject Read-only
Example response
{
"id": 42,
"medicine_name": "Amoxicillin 500 mg capsules",
"gtin": "06164004321562",
"batch_number": "BTH-2026-041",
"additional_batch_numbers": [],
"sscc": "106164004000007317",
"all_batch_numbers": [
"BTH-2026-041"
],
"reason": "CONTAMINATION",
"reason_display": "Contamination",
"severity_class": "CLASS_II",
"severity_class_display": "Class II",
"trigger_type": "VOLUNTARY",
"ppb_reference": "",
"instructions": "Quarantine the batch and await collection instructions.",
"recall_level": "Retail level",
"geographic_scope": "Kenya",
"patient_instructions": "",
"manufacturer": "Example Pharmaceuticals Ltd",
"generic_name": "Amoxicillin",
"local_technical_representative": "",
"status": "OPEN",
"initiated_at": "2026-10-10T09:00:00Z",
"closed_at": "2026-10-10T09:00:00Z",
"defect_discovered_at": "2026-10-10T09:00:00Z",
"ppb_notification_due_at": "2026-10-10T09:00:00Z",
"ppb_notified_at": "2026-10-10T09:00:00Z",
"initial_report_due_at": "2026-10-10T09:00:00Z",
"progress_report_due_at": "2026-10-10T09:00:00Z",
"reconciliation_due_at": "2026-10-10T09:00:00Z",
"facilities_acknowledged": 4,
"facilities_total": 6,
"is_drill": true,
"drill_facilities_reached": 6,
"drill_facilities_by_hop": {
"1": 4,
"2": 2
}
}
Status codes
201 | Created |
400 | The request is invalid, for example a malformed GTIN or a missing field |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
404 | Not found, or not visible to your credential |
409 | Conflict: a request with this Idempotency-Key is in progress, or the record already exists |
429 | Rate limit exceeded |
Fetch a recall
https://api.dawatrack.com/partner-api/v1/recalls/{id}/
One recall in full: its PPB deadlines, acknowledgement progress and product details.
recalls:read
Limit 5,000 per day
Path parameters
-
idinteger Required
Example request
curl -X GET https://api.dawatrack.com/partner-api/v1/recalls/42/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN"
import requests
token = "..." # from POST /oauth/token/
response = requests.get(
"https://api.dawatrack.com/partner-api/v1/recalls/42/",
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/42/", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(await response.json());
Response fields
-
idinteger Read-onlyThe record's id.
-
medicine_namestringThe product's name.
-
gtinstringThe product's GS1 GTIN, as 14 digits.
-
batch_numberstringThe batch or lot number.
-
additional_batch_numbersarray of stringsExtra batch numbers this recall also covers, beyond batch_number.
-
ssccstring Read-onlySet when the recall is of one container and what it holds; blank for a batch.
-
all_batch_numbersarray of strings Read-onlybatch_number plus additional_batch_numbers, deduped - the full set this recall covers.
-
reasonenumPossible values:QUALITY_DEFECTCONTAMINATIONMISLABELINGADVERSE_REACTIONREGULATORY_WITHDRAWALOTHER -
reason_displaystring Read-onlyThe recall reason, in words.
-
severity_classenumPossible values:CLASS_ICLASS_IICLASS_IIICAUTION_IN_USE -
severity_class_displaystring Read-onlyThe severity class, in words.
-
trigger_typestringVOLUNTARY or STATUTORY. Recalls opened through the API are always VOLUNTARY.
-
ppb_referencestringPPB's reference for the recall, once assigned.
-
instructionsstringWhat facilities should do.
-
recall_levelstringHow far into the supply chain the recall reaches, in PPB's own published wording.
-
geographic_scopestringGeographic reach PPB publishes on the notice.
-
patient_instructionsstringWhat patients/caregivers who may have this product should do, if applicable.
-
manufacturerstring Read-onlyThe marketing authorisation holder or manufacturer, from the product's catalogue record.
-
generic_namestring Read-onlyThe active ingredient, from the product's catalogue record.
-
local_technical_representativestring Read-onlyPPB's registered local technical representative for the recalled product.
-
statusstring -
initiated_atdatetimeWhen the recall was opened.
-
closed_atdatetime or nullWhen the recall was closed, if it is closed.
-
defect_discovered_atdatetime or nullPPB Guidelines §2.10.3b: when this defect was first discovered.
-
ppb_notification_due_atdatetime or nullPPB Guidelines §2.10.3b: 24h after defect_discovered_at.
-
ppb_notified_atdatetime or null Read-onlyWhen the supplier recorded that PPB was notified. Set in the supplier portal, not through the API.
-
initial_report_due_atdatetime or nullPPB Guidelines §2.13.1a: an initial report is due within 1 week.
-
progress_report_due_atdatetime or null -
reconciliation_due_atdatetime or null -
facilities_acknowledgedinteger Read-only -
facilities_totalinteger Read-only -
is_drillboolean Read-only -
drill_facilities_reachedinteger Read-only -
drill_facilities_by_hopobject Read-only
Example response
{
"id": 42,
"medicine_name": "Amoxicillin 500 mg capsules",
"gtin": "06164004321562",
"batch_number": "BTH-2026-041",
"additional_batch_numbers": [],
"sscc": "106164004000007317",
"all_batch_numbers": [
"BTH-2026-041"
],
"reason": "CONTAMINATION",
"reason_display": "Contamination",
"severity_class": "CLASS_II",
"severity_class_display": "Class II",
"trigger_type": "VOLUNTARY",
"ppb_reference": "",
"instructions": "Quarantine the batch and await collection instructions.",
"recall_level": "Retail level",
"geographic_scope": "Kenya",
"patient_instructions": "",
"manufacturer": "Example Pharmaceuticals Ltd",
"generic_name": "Amoxicillin",
"local_technical_representative": "",
"status": "OPEN",
"initiated_at": "2026-10-10T09:00:00Z",
"closed_at": "2026-10-10T09:00:00Z",
"defect_discovered_at": "2026-10-10T09:00:00Z",
"ppb_notification_due_at": "2026-10-10T09:00:00Z",
"ppb_notified_at": "2026-10-10T09:00:00Z",
"initial_report_due_at": "2026-10-10T09:00:00Z",
"progress_report_due_at": "2026-10-10T09:00:00Z",
"reconciliation_due_at": "2026-10-10T09:00:00Z",
"facilities_acknowledged": 4,
"facilities_total": 6,
"is_drill": true,
"drill_facilities_reached": 6,
"drill_facilities_by_hop": {
"1": 4,
"2": 2
}
}
Status codes
200 | OK |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
404 | Not found, or not visible to your credential |
429 | Rate limit exceeded |
Get a recall's distribution tree
https://api.dawatrack.com/partner-api/v1/recalls/{id}/tree/
Every facility the recall reached, nested under whoever resold to whom.
recalls:read
Limit 5,000 per day
A manufacturer gets the whole tree. A distributor or pharmacy gets one root per linked facility, with only that facility's downstream reach.
Path parameters
-
idinteger Required
Example request
curl -X GET https://api.dawatrack.com/partner-api/v1/recalls/42/tree/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN"
import requests
token = "..." # from POST /oauth/token/
response = requests.get(
"https://api.dawatrack.com/partner-api/v1/recalls/42/tree/",
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/42/tree/", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(await response.json());
Response fields
-
noticeobjectThis facility's entry.
-
idinteger Read-onlyThe record's id.
-
hop_depthinteger Read-only1 for a direct delivery, 2 or more for each wholesale hop further on.
-
pharmacy_namestringThe facility's name.
-
branch_namestringThe facility branch.
-
schema_namestringThe facility's identifier on DawaTrack.
-
quantity_on_hand_at_recallinteger Read-only -
quarantined_atdatetime or nullWhen the batch was quarantined at this facility.
-
acknowledged_atdatetime or nullWhen the facility acknowledged the recall, if it has.
-
acknowledged_by_namestringWho acknowledged it.
-
is_overdueboolean Read-onlyWhether acknowledgement is past its deadline.
-
in_transitboolean Read-onlyStock dispatched to this facility that it hasn't received yet (no lot yet).
-
pendingboolean Read-onlyReached since the recall's last fan-out: its notice goes out within 15 minutes (id is null).
Default:
False
-
-
childrenarray of objects Read-onlyThe facilities it passed the batch on to, in the same shape.
Example response
[
{
"notice": {
"id": 501,
"hop_depth": 1,
"pharmacy_name": "Pharmacy C",
"branch_name": "Main branch",
"schema_name": "pharmacy_c",
"quantity_on_hand_at_recall": 400,
"quarantined_at": "2026-10-05T13:19:57Z",
"acknowledged_at": null,
"acknowledged_by_name": "",
"is_overdue": false
},
"children": []
}
]
Status codes
200 | OK |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
404 | Not found, or not visible to your credential |
429 | Rate limit exceeded |
List affected facilities
https://api.dawatrack.com/partner-api/v1/recalls/{id}/affected/
The facilities a recall affects, with quantities, SSCCs and units, plus a summary across all of them.
recalls:read
Limit 5,000 per day
The response adds a summary object: total_facilities, total_ssccs, total_units, total_quantity_affected and total_quantity_recovered. It always covers every facility you can see, even when filtered.
Path parameters
-
idinteger Required
Query parameters
-
pageinteger OptionalA page number within the paginated result set.
-
page_sizeinteger OptionalNumber of results to return per page.
-
schema_namestring OptionalOnly this facility schema.
Example request
curl -X GET https://api.dawatrack.com/partner-api/v1/recalls/42/affected/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN"
import requests
token = "..." # from POST /oauth/token/
response = requests.get(
"https://api.dawatrack.com/partner-api/v1/recalls/42/affected/",
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/42/affected/", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(await response.json());
Response fields
A page of results. These are the fields of each item in results. See Pagination.
-
facility_schemastringThe facility's identifier on DawaTrack.
-
facility_namestringThe facility's name.
-
facility_license_numberstring -
facility_typestring -
facility_countystring -
quantity_affectedinteger -
quantity_recoveredinteger -
acknowledged_atdatetime or nullWhen the facility acknowledged the recall, if it has.
-
ssccsarray of strings -
unitsarray of objects
Example response
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"facility_schema": "pharmacy_a",
"facility_name": "Pharmacy A",
"facility_license_number": "PPB/R/2026/0001",
"facility_type": "RETAIL",
"facility_county": "Nairobi",
"quantity_affected": 1284,
"quantity_recovered": 1284,
"acknowledged_at": "2026-10-10T09:00:00Z",
"ssccs": [
"string"
],
"units": [
{}
]
}
]
}
Status codes
200 | OK |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
404 | Not found, or not visible to your credential |
429 | Rate limit exceeded |
List affected units at a facility
https://api.dawatrack.com/partner-api/v1/recalls/{id}/affected/{facility_schema}/units/
The serialized units of the recalled batches held at one facility.
recalls:read
Limit 5,000 per day
Returns 404 if you can't see that facility on this recall.
Path parameters
-
facility_schemastring Required -
idinteger Required
Query parameters
-
orderingstring OptionalWhich field to use when ordering the results.
-
pageinteger OptionalA page number within the paginated result set.
-
page_sizeinteger OptionalNumber of results to return per page.
-
searchstring OptionalA search term.
Example request
curl -X GET https://api.dawatrack.com/partner-api/v1/recalls/42/affected/pharmacy_a/units/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN"
import requests
token = "..." # from POST /oauth/token/
response = requests.get(
"https://api.dawatrack.com/partner-api/v1/recalls/42/affected/pharmacy_a/units/",
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/42/affected/pharmacy_a/units/", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(await response.json());
Response fields
A page of results. These are the fields of each item in results. See Pagination.
-
gtinstringThe product's GS1 GTIN, as 14 digits.
-
serial_numberstringThe unit's serial number.
-
batch_numberstringThe batch or lot number.
-
current_ssccstring or null Read-onlyThe SSCC the unit is currently packed in, if any.
-
current_statusstringThe type of the unit's latest event.
-
facility_schemastringThe facility's identifier on DawaTrack.
-
last_event_atdatetimeWhen the unit's latest event happened.
Example response
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"gtin": "06164004321562",
"serial_number": "SN-0007731",
"batch_number": "BTH-2026-041",
"current_sscc": "106164004000007317",
"current_status": "SHIP",
"facility_schema": "pharmacy_a",
"last_event_at": "2026-10-10T09:00:00Z"
}
]
}
Status codes
200 | OK |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
429 | Rate limit exceeded |
Acknowledge a recall
https://api.dawatrack.com/partner-api/v1/recalls/{id}/acknowledge/
Confirm receipt of a recall notice for a facility you're linked to.
recalls:acknowledge
Limit 5,000 per day
schema_name can be left out if your credential is linked to exactly one facility.
Path parameters
-
idinteger Required
Request body
JSON. Send Content-Type: application/json.
-
schema_namestring OptionalRequired unless this partner is linked to exactly one facility.
-
notesstring Optional
Example request
curl -X POST https://api.dawatrack.com/partner-api/v1/recalls/42/acknowledge/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"schema_name": "pharmacy_a",
"notes": "Stock pulled from the shelf."
}'
import uuid
import requests
token = "..." # from POST /oauth/token/
response = requests.post(
"https://api.dawatrack.com/partner-api/v1/recalls/42/acknowledge/",
headers={"Authorization": f"Bearer {token}", "Idempotency-Key": str(uuid.uuid4())},
json={
"schema_name": "pharmacy_a",
"notes": "Stock pulled from the shelf."
},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/42/acknowledge/", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"schema_name": "pharmacy_a",
"notes": "Stock pulled from the shelf."
}),
});
console.log(await response.json());
Response fields
-
idinteger Read-onlyThe record's id.
-
hop_depthinteger Read-only1 for a direct delivery, 2 or more for each wholesale hop further on.
-
pharmacy_namestringThe facility's name.
-
branch_namestringThe facility branch.
-
schema_namestringThe facility's identifier on DawaTrack.
-
quantity_on_hand_at_recallinteger Read-only -
quarantined_atdatetime or nullWhen the batch was quarantined at this facility.
-
acknowledged_atdatetime or nullWhen the facility acknowledged the recall, if it has.
-
acknowledged_by_namestringWho acknowledged it.
-
is_overdueboolean Read-onlyWhether acknowledgement is past its deadline.
-
in_transitboolean Read-onlyStock dispatched to this facility that it hasn't received yet (no lot yet).
-
pendingboolean Read-onlyReached since the recall's last fan-out: its notice goes out within 15 minutes (id is null).
Default:
False
Example response
{
"id": 42,
"hop_depth": 1,
"pharmacy_name": "Pharmacy A",
"branch_name": "Main branch",
"schema_name": "pharmacy_a",
"quantity_on_hand_at_recall": 1284,
"quarantined_at": "2026-10-10T09:00:00Z",
"acknowledged_at": "2026-10-10T09:00:00Z",
"acknowledged_by_name": "acknowledged_by_name",
"is_overdue": true,
"in_transit": false,
"pending": false
}
Status codes
200 | OK |
400 | The request is invalid, for example a malformed GTIN or a missing field |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
404 | Not found, or not visible to your credential |
409 | Conflict: a request with this Idempotency-Key is in progress, or the record already exists |
429 | Rate limit exceeded |
Get a recall report
https://api.dawatrack.com/partner-api/v1/recalls/{id}/report/
One of the three reports PPB requires per recall: initial, follow-up and final.
recalls:read
Manufacturers only
Limit 5,000 per day
Choose the report with ?stage=initial, follow_up or final. It reports acknowledgements and quantity-on-hand snapshots only. It doesn't estimate returned or destroyed quantities that weren't recorded.
facilities_* count pharmacies on DawaTrack. outside_parties lists the parties outside DawaTrack the recall reaches: each one's status (notified, not-acknowledged after 24 hours with a second notice, acknowledged, unreachable, phone-only, no-contact), when it was notified, its answer (quantity_held, action) and who it received the lot from. Never its email or phone.
Path parameters
-
idinteger Required
Query parameters
-
stageenum RequiredWhich report to generate.
Possible values:initialfollow_upfinal
Example request
curl -X GET https://api.dawatrack.com/partner-api/v1/recalls/42/report/ \
-H "Authorization: Bearer $DAWATRACK_TOKEN"
import requests
token = "..." # from POST /oauth/token/
response = requests.get(
"https://api.dawatrack.com/partner-api/v1/recalls/42/report/",
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
print(response.json())
const token = "..."; // from POST /oauth/token/
const response = await fetch("https://api.dawatrack.com/partner-api/v1/recalls/42/report/", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(await response.json());
Response fields
-
recall_idinteger -
stagestring -
generated_atdatetime -
batch_numberstringThe batch or lot number.
-
medicine_namestringThe product's name.
-
trigger_typestringVOLUNTARY or STATUTORY. Recalls opened through the API are always VOLUNTARY.
-
severity_classstring -
statusstring -
ppb_referencestringPPB's reference for the recall, once assigned.
-
initiated_atdatetimeWhen the recall was opened.
-
due_atdatetime or nullThis stage's deadline: the initial, progress or reconciliation report due date.
-
filed_atdatetime or nullThis stage's filed timestamp, if filed.
-
initial_report_due_atdatetime or null -
progress_report_due_atdatetime or null -
reconciliation_due_atdatetime or null -
facilities_totalinteger -
facilities_acknowledgedinteger -
facilities_pendinginteger -
percentage_acknowledgednumber -
total_units_on_hand_at_recallinteger -
unrecorded_sourcesintegerUnits of the recalled lots that a facility sent on from a receipt DawaTrack didn't see (stock bought outside the platform): the {"event": null} entries of the shipments' sources. The shipments are recorded; the graph can't trace these units back to a delivery.
-
noticesarray of objects-
idinteger Read-onlyThe record's id.
-
hop_depthinteger Read-only1 for a direct delivery, 2 or more for each wholesale hop further on.
-
pharmacy_namestringThe facility's name.
-
branch_namestringThe facility branch.
-
schema_namestringThe facility's identifier on DawaTrack.
-
quantity_on_hand_at_recallinteger Read-only -
quarantined_atdatetime or nullWhen the batch was quarantined at this facility.
-
acknowledged_atdatetime or nullWhen the facility acknowledged the recall, if it has.
-
acknowledged_by_namestringWho acknowledged it.
-
is_overdueboolean Read-onlyWhether acknowledgement is past its deadline.
-
in_transitboolean Read-onlyStock dispatched to this facility that it hasn't received yet (no lot yet).
-
pendingboolean Read-onlyReached since the recall's last fan-out: its notice goes out within 15 minutes (id is null).
Default:
False
-
-
outside_partiesarray of objectsParties outside DawaTrack the recall reaches (they have no recall console): each notice and answer. The facilities_* figures count DawaTrack facilities only.
-
party_idinteger or nullIts record on GET /parties/, when it has one.
-
namestring -
identifierstringppb:<licence>, gln:<GLN> or name:<name>.
-
statusenumacknowledged; unreachable (the recalling firm marked it so); not-acknowledged (notified, no answer 24 hours on: a second notice was sent); notified; failed (the mail server refused the notice); queued; phone-only; no-contact.
Possible values:acknowledgedunreachablenot-acknowledgednotifiedfailedqueuedphone-onlyno-contact -
reachablebooleanAn email on file a notice can go to.
-
notified_atdatetime or null -
second_notice_atdatetime or nullThe automatic notice 24 hours on.
-
remindersintegerReminders the recalling firm sent by hand.
-
acknowledged_atdatetime or nullWhen the facility acknowledged the recall, if it has.
-
quantity_heldinteger or nullUnits it said it holds, when it answered.
-
actionstringWhat it said it did with the stock.
-
unreachable_notestringHow the firm tried, when unreachable.
-
received_fromarray of stringsWho it received the lot from, by name (incl. a declaring party).
-
supplied_toarray of stringsWho received the lot from it.
-
-
outside_parties_acknowledgedinteger
Example response
{
"recall_id": 42,
"stage": "initial",
"generated_at": "2026-10-10T09:00:00Z",
"batch_number": "BTH-2026-041",
"medicine_name": "Amoxicillin 500 mg capsules",
"trigger_type": "VOLUNTARY",
"severity_class": "CLASS_II",
"status": "OPEN",
"ppb_reference": "",
"initiated_at": "2026-10-10T09:00:00Z",
"due_at": "2026-10-10T09:00:00Z",
"filed_at": "2026-10-10T09:00:00Z",
"initial_report_due_at": "2026-10-10T09:00:00Z",
"progress_report_due_at": "2026-10-10T09:00:00Z",
"reconciliation_due_at": "2026-10-10T09:00:00Z",
"facilities_total": 6,
"facilities_acknowledged": 4,
"facilities_pending": 2,
"percentage_acknowledged": 66.7,
"total_units_on_hand_at_recall": 7354,
"unrecorded_sources": 120,
"notices": [
{
"id": 42,
"hop_depth": 1,
"pharmacy_name": "Pharmacy A",
"branch_name": "Main branch",
"schema_name": "pharmacy_a",
"quantity_on_hand_at_recall": 1284,
"quarantined_at": "2026-10-10T09:00:00Z",
"acknowledged_at": "2026-10-10T09:00:00Z",
"acknowledged_by_name": "acknowledged_by_name",
"is_overdue": true,
"in_transit": false,
"pending": false
}
],
"outside_parties": [
{
"party_id": 1,
"name": "name",
"identifier": "identifier",
"status": "OPEN",
"reachable": false,
"notified_at": "2026-10-10T09:00:00Z",
"second_notice_at": "2026-10-10T09:00:00Z",
"reminders": 1,
"acknowledged_at": "2026-10-10T09:00:00Z",
"quantity_held": 1,
"action": "action",
"unreachable_note": "unreachable_note",
"received_from": [
"string"
],
"supplied_to": [
"string"
]
}
],
"outside_parties_acknowledged": 1
}
Status codes
200 | OK |
400 | The request is invalid, for example a malformed GTIN or a missing field |
401 | No valid access token |
403 | Your credential lacks the scope, partner type or environment this needs |
404 | Not found, or not visible to your credential |
429 | Rate limit exceeded |
Partner API v1. The API reference is generated from the API's own code.