Capture links
A buyer outside DawaTrack records receiving a consignment, with the one-time token the pharmacy sent it.
Endpoints
Ask for a capture code
https://api.dawatrack.com/partner-api/v1/capture/code/
Email the 6-digit code a receiver enters to confirm a consignment. No account: the token is the credential.
The code goes to the receiver's email on file with its sender (where the link was emailed). Only when there is none, send email: the code goes there, and it becomes the receiver's contact.
A code works once and for 10 minutes; 5 wrong tries lock it; a new code voids the one before; at most 5 codes a link per hour (too_many_codes, 429). DawaTrack stores only a keyed hash of it.
Returns 202 with sent_to, the address masked. 404 for an unknown token, 410 when it was used, withdrawn or expired, 400 email_required when there is no address to send to.
Request body
JSON. Send Content-Type: application/json.
-
tokenstring RequiredThe secret in the link the sender sent you.
-
emailstring OptionalWhere to send the code, only when the sender has no email on file for you.
Example request
curl -X POST https://api.dawatrack.com/partner-api/v1/capture/code/ \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"token": "q3V8m1x0K9cJr2Zt7bWfY4nH6sL5aP0dE8gU1iO3kTw"
}'
import uuid
import requests
response = requests.post(
"https://api.dawatrack.com/partner-api/v1/capture/code/",
headers={"Idempotency-Key": str(uuid.uuid4())},
json={
"token": "q3V8m1x0K9cJr2Zt7bWfY4nH6sL5aP0dE8gU1iO3kTw"
},
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.dawatrack.com/partner-api/v1/capture/code/", {
method: "POST",
headers: {
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"token": "q3V8m1x0K9cJr2Zt7bWfY4nH6sL5aP0dE8gU1iO3kTw"
}),
});
console.log(await response.json());
Response fields
-
sent_tostring
Example response
{
"sent_to": "s•••@upcountry.example"
}
Status codes
202 | Accepted: queued for processing |
400 | The request is invalid, for example a malformed GTIN or a missing field |
409 | Conflict: a request with this Idempotency-Key is in progress, or the record already exists |
429 | Rate limit exceeded |
Record a captured receipt
https://api.dawatrack.com/partner-api/v1/capture/receive/
A buyer outside DawaTrack records receiving one consignment, line by line. No account: the token is the credential.
The token comes in the link DawaTrack emails a receiver outside DawaTrack the moment a consignment to it is recorded, by a pharmacy, a supplier on the portal or a partner here. The same capture can be made on the page at /capture/{token}/. The Shipments and confirmation guide shows the whole flow.
A token is a random secret, shown once to the pharmacy and stored by DawaTrack only as its SHA-256. It works once and lasts 30 days; the pharmacy can withdraw it before use, and making a new one withdraws the open one.
Send lines: each line's batch_number (and gtin, if given), the quantity received of it (damaged units included; 0 for a line that didn't arrive), and optionally damaged (of those received, the units that arrived damaged) and refused (units sent back with the delivery). Every line you send is answered, as in a GS1 RECADV (Receiving Advice): units neither received nor refused are recorded short. Received plus refused is at most what was sent (refused_out_of_range), damaged at most what was received (damaged_out_of_range). A line you leave out stays open. For a one-line consignment, batch_number and quantity (with damaged and refused) alone also work.
scans (optional): the text of what you scanned, a pack's GS1 DataMatrix or Digital Link, or a carton's SSCC. DawaTrack reads each against the lines: a pack matches the line with its GTIN and batch (and its expiry, when the pack carries one), a carton the line shipped under its SSCC. A plain barcode has no batch and matches nothing. Each receipt then records batch_scanned_and_matched, true when a scan matched it, returned on each RECEIVE in events.
delivery_note (optional, multipart): a photo (JPEG, PNG, WebP) or PDF of the delivery note you signed, up to 10 MB, read by its first bytes (delivery_note_unreadable, delivery_note_too_large). It is kept off the ledger, privately, and only the sender can open it.
phone is required: the receiver's business phone, with its country code (+254…; a Kenyan 07… or 01… number works too), else 400 phone_required. It is kept with the receiver's record in E.164, with the verified email, off the ledger: how a recall reaches it.
consent must be true: the record is written to the ledger, can't be changed afterwards, and is visible to the sender, the supplier and regulators tracing or recalling the batch.
code is required: the 6-digit code from POST /capture/code/, which proves the receiver reads the email on file. A wrong, used or expired code is 400 (code_invalid, no_code, code_expired), 429 once locked. The address the code went to is kept with the receiver's record, off the ledger, for recall notices; email is ignored.
Writes a RECEIVE per line received, whose parent is the line's dispatch, with link_confidence INFERRED, and a RECEIPT_ANSWER per line short, damaged or refused: quantity 0 (it moves no stock), parent the dispatch, with expected, received, short, damaged and refused. The sender sees it; refused units stay the sender's to record as a return. Traces don't follow a RECEIPT_ANSWER and EPCIS leaves it out. events lists them line by line, each with its event_type. 404 for an unknown token, 410 when it was used, withdrawn or expired, 409 when the consignment is already recorded, 400 for a line it doesn't have, 429 after 10 attempts from one IP in an hour.
Request body
JSON. Send Content-Type: application/json.
-
tokenstring RequiredThe secret in the link the sender sent you.
-
codestring RequiredThe 6-digit code emailed to you (POST /capture/code/), valid 10 minutes, once.
-
company_namestring Required -
ppb_licencestring Optional -
attester_namestring Required -
emailstring OptionalIgnored: the email the code was sent to is kept, off the ledger, for recall notices.
-
phonestring RequiredYour business phone, with its country code (+254...; a Kenyan 07... or 01... number works too). Kept with your record in E.164, off the ledger, for recall notices.
-
gtinstring OptionalThe product's GS1 GTIN, as 14 digits.
-
batch_numberstring OptionalA one-line consignment: the line's batch. Or send lines.
-
quantityinteger OptionalA one-line consignment: units received, damaged ones included.
-
damagedinteger OptionalA one-line consignment: units received damaged.
Default:
0 -
refusedinteger OptionalA one-line consignment: units refused.
Default:
0 -
linesarray of objects OptionalEach line of the consignment (GTIN, batch): the units received, damaged and refused. Every line given is answered; units neither received nor refused are recorded short.
-
gtinstring OptionalThe product's GS1 GTIN, as 14 digits.
-
batch_numberstring RequiredThe batch or lot number.
-
quantityinteger RequiredUnits received of this line, damaged ones included; 0 for a line that didn't arrive.
-
damagedinteger OptionalOf the units received, those that arrived damaged.
Default:
0 -
refusedinteger OptionalUnits refused and sent back with the delivery.
Default:
0
-
-
captured_atdatetime RequiredWhen the stock was received.
-
consentboolean RequiredTrue: the submitter has read and accepts how the record is kept.
-
scansarray of strings OptionalWhat you scanned: packs' GS1 DataMatrix or Digital Link text, or cartons' SSCC. Each line's receipt records whether a scan matched its GTIN and batch (batch_scanned_and_matched).
-
delivery_noteURL or null OptionalA photo (JPEG, PNG, WebP) or PDF of the signed delivery note, up to 10 MB; multipart only. Kept off the ledger, shown only to the sender.
Example request
curl -X POST https://api.dawatrack.com/partner-api/v1/capture/receive/ \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"token": "q3V8m1x0K9cJr2Zt7bWfY4nH6sL5aP0dE8gU1iO3kTw",
"code": "482915",
"company_name": "Upcountry Chemist",
"ppb_licence": "PPB/R/7781",
"attester_name": "Wanjiru Kamau",
"phone": "+254712345678",
"lines": [
{
"gtin": "06164004321562",
"batch_number": "BTH-2026-041",
"quantity": 18
},
{
"gtin": "06164004321562",
"batch_number": "BTH-2026-052",
"quantity": 10,
"damaged": 2,
"refused": 4
}
],
"captured_at": "2026-10-22T10:30:00+03:00",
"consent": true,
"scans": [
"]d201061640043215621727113010BTH-2026-041\u001d21SN0007731"
]
}'
import uuid
import requests
response = requests.post(
"https://api.dawatrack.com/partner-api/v1/capture/receive/",
headers={"Idempotency-Key": str(uuid.uuid4())},
json={
"token": "q3V8m1x0K9cJr2Zt7bWfY4nH6sL5aP0dE8gU1iO3kTw",
"code": "482915",
"company_name": "Upcountry Chemist",
"ppb_licence": "PPB/R/7781",
"attester_name": "Wanjiru Kamau",
"phone": "+254712345678",
"lines": [
{
"gtin": "06164004321562",
"batch_number": "BTH-2026-041",
"quantity": 18
},
{
"gtin": "06164004321562",
"batch_number": "BTH-2026-052",
"quantity": 10,
"damaged": 2,
"refused": 4
}
],
"captured_at": "2026-10-22T10:30:00+03:00",
"consent": True,
"scans": [
"]d201061640043215621727113010BTH-2026-041\u001d21SN0007731"
]
},
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.dawatrack.com/partner-api/v1/capture/receive/", {
method: "POST",
headers: {
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"token": "q3V8m1x0K9cJr2Zt7bWfY4nH6sL5aP0dE8gU1iO3kTw",
"code": "482915",
"company_name": "Upcountry Chemist",
"ppb_licence": "PPB/R/7781",
"attester_name": "Wanjiru Kamau",
"phone": "+254712345678",
"lines": [
{
"gtin": "06164004321562",
"batch_number": "BTH-2026-041",
"quantity": 18
},
{
"gtin": "06164004321562",
"batch_number": "BTH-2026-052",
"quantity": 10,
"damaged": 2,
"refused": 4
}
],
"captured_at": "2026-10-22T10:30:00+03:00",
"consent": true,
"scans": [
"]d201061640043215621727113010BTH-2026-041\u001d21SN0007731"
]
}),
});
console.log(await response.json());
Response fields
-
event_idinteger -
parent_event_idinteger -
link_confidencestring -
batch_numberstringThe batch or lot number.
-
quantityintegerThe number of units.
-
event_typestringThe kind of event.
-
eventsarray of objects
Example response
{
"event_id": 4512,
"parent_event_id": 4488,
"link_confidence": "INFERRED",
"batch_number": "BTH-2026-041",
"quantity": 18,
"event_type": "RECEIVE",
"events": [
{
"event_id": 4512,
"parent_event_id": 4488,
"link_confidence": "INFERRED",
"batch_number": "BTH-2026-041",
"quantity": 18,
"event_type": "RECEIVE",
"batch_scanned_and_matched": true
},
{
"event_id": 4513,
"parent_event_id": 4489,
"link_confidence": "INFERRED",
"batch_number": "BTH-2026-052",
"quantity": 10,
"event_type": "RECEIVE",
"batch_scanned_and_matched": false
},
{
"event_id": 4514,
"parent_event_id": 4489,
"link_confidence": "INFERRED",
"batch_number": "BTH-2026-052",
"quantity": 0,
"event_type": "RECEIPT_ANSWER",
"expected": 16,
"received": 10,
"short": 2,
"damaged": 2,
"refused": 4
}
]
}
Status codes
200 | OK |
400 | The request is invalid, for example a malformed GTIN or a missing field |
409 | Conflict: a request with this Idempotency-Key is in progress, or the record already exists |
429 | Rate limit exceeded |
Partner API v1. The API reference is generated from the API's own code.