Reporting What You Sent
Note
Part of the Wholesaler Integrations guide. This step assumes you have already completed Registering an Available Wholesaler: receipts are submitted against a clinic’s wholesaler, which only exists once that clinic has connected you.
This endpoint records what you have sent: one line per product per
batch on a delivery, with the batch number and expiry date the clinic
needs for its inventory. A line carries a single batch number and expiry
date, so a product you shipped from two batches is two lines, each with
its own id and the quantity that came from that batch.
Each line is staged for review. Nothing moves into stock automatically, and accepting a batch is a manual step in the clinic’s review screen.
See Receive Batch Orders for the full customer-facing walkthrough of that screen: what clinic staff see after you submit, how they match a line you sent to an item in their own stock, and what accepting a batch does to their inventory.
Submitting receipt lines
curl -X POST \
"https://[env.]provetcloud.com/<provet_id>/api/0.1/wholesaler/<wholesaler_pk>/receipts/" \
-H "Authorization: Bearer <oauth_access_token>" \
-H "Content-Type: application/json" \
-d '{
"receipt_lines": [
{
"id": "INV-202012164-1",
"order_id": 5567,
"invoice_number": "202012164",
"invoice_date": "2026-08-27",
"product_number": "ACM-001",
"product_name": "Amoxicillin 250mg",
"barcode": "6412345678901",
"quantity": "12.00",
"batch_number": "123456789",
"expiry_date": "2027-12-31"
}
]
}'
wholesaler_pk is the per-clinic wholesaler.id you received in
wholesaler.created and see again on every order.placed. The
wholesaler scope is sufficient: no additional Provet
permission is needed.
Each line carries its own invoice number and order reference, so one document can span several orders. Send a delivery note or an invoice, whichever you produce, but only one of them per shipment, or staff accepting both would double the stock.
A product shipped from more than one batch is sent as one line per batch.
The lines repeat the product and the order, and differ in id,
quantity, batch_number and expiry_date:
{
"receipt_lines": [
{
"id": "INV-202012164-2a",
"order_id": 5567,
"invoice_number": "202012164",
"product_number": "ACM-001",
"quantity": "8.00",
"batch_number": "123456789",
"expiry_date": "2027-03-31"
},
{
"id": "INV-202012164-2b",
"order_id": 5567,
"invoice_number": "202012164",
"product_number": "ACM-001",
"quantity": "4.00",
"batch_number": "987654321",
"expiry_date": "2027-06-30"
}
]
}
Reusing one id for both would store the first batch and answer
duplicate for the second, losing it silently.
Field |
Type |
Optional |
Meaning |
|---|---|---|---|
|
string |
No |
Your own identifier for this line, unique among the lines you send for this wholesaler. It is what makes a resubmission safe; see Resubmitting a line. Opaque to Provet: compose it however you like, up to 255 characters. Identifiers are compared without regard to case, so |
|
string |
No |
Units sent, in the package units the order used, not dosable units. A decimal string with up to 9 whole digits and 3 decimal places. Zero is valid and says you have seen the order but shipped nothing yet; a negative quantity is not, since a receipt records what left your warehouse. |
|
string |
Yes* |
Your product code, up to 63 characters. The same product code you would provide to the user when making it available to them via the catalog API |
|
string |
Yes* |
Product barcode, up to 30 characters. |
|
string |
Yes |
Shown to clinic staff when a line has to be matched by hand, so worth sending even when you expect a match. Up to 255 characters. |
|
number |
Yes |
The |
|
string |
Yes |
Your invoice or delivery note number, up to 63 characters. Used to link the line to an order when |
|
string |
Yes |
Calendar date on the invoice, |
|
string |
Yes |
Batch identifier, up to 63 characters. |
|
string |
Yes |
Use-before date from the packaging, |
* A product identifier is required, but a successful match is not.
Every line must carry a product_number or a barcode; a line with
neither is rejected. A line that matches no product is still stored and
left for staff to match, which is how your code gets learned onto the
product, so the first delivery of a product the clinic has not
imported yet is expected to come back unmatched.
Dates are calendar dates. Send the date as it appears on the invoice or the packaging, without converting time zones. A value carrying a time or a UTC offset is stored as absent.
At most 500 lines per call. Split a larger submission across several
calls: lines are deduplicated on their own id, so there is nothing to
reconcile between them.
The response
{
"lines_total": 1,
"lines_accepted": 1,
"lines_rejected": 0,
"lines_duplicate": 0,
"receipt_lines": [
{
"id": "INV-202012164-1",
"status": "accepted",
"order_id": 5567,
"matched_item": true,
"matched_by": "product_code"
}
]
}
Field |
Type |
Meaning |
|---|---|---|
|
string |
The line |
|
string |
|
|
string |
Why the line was rejected. Present only on a rejected line. |
|
number / null |
The order the line was actually linked to, which is not always the one you submitted: a line with no |
|
boolean |
Whether the line resolved to a product in the clinic’s catalogue. |
|
string / null |
|
matched_by does not distinguish an unknown code from an ambiguous
one where two products claim the same code: both come back as no
match.
|
Meaning |
|---|---|
|
The line carried neither a |
|
|
|
The line carried no |
|
|
|
The line was not an object, or a value was longer than the field allows. |
|
The line linked to no order, and the id you posted to does not settle which department the goods are for. Only reachable for integrations predating per-credential-set ids. Send an |
Partial success is a 200
The status code describes the request, not its contents: you get a 200 however many lines were rejected, including all of them.
Check lines_rejected, not the status code. A 200 does not mean
every line was stored.
A 400 means the request itself was unusable and nothing was stored:
malformed JSON, a missing or empty receipt_lines array, or more than
500 lines.
Resubmitting a line
Your line id is the idempotency key. Sending the same id again
for the same wholesaler creates nothing new and returns the original
outcome with status: "duplicate", including the order_id and
match result recorded the first time. Two different wholesalers may use
the same id without colliding.
A line cannot be corrected or retracted once submitted. Resubmitting
an id with different values returns the stored line and changes
nothing, so a correction means a new line with a new id.
A timed-out request is therefore safe to retry: whatever landed comes
back as duplicate, and whatever did not is created.
How a line is matched and filed
Products. Your product_number is tried first, against products the
clinic has recorded as supplied by you, then the barcode. A barcode
only matches a product you supply, so a barcode you have never shipped will
not resolve even if the clinic stocks it.
Orders. order_id is used when you send one, and the
invoice_number is matched against orders otherwise. Either way only
your own orders are considered: a line naming an order placed with someone
else is stored unlinked, with order_id: null. An invoice number is not
unique (one document is expected to span several orders), so a reference
matching more than one of yours also leaves the line unlinked. Send an
order_id when you need the link to be certain.
Departments. A batch belongs to a department, but you don’t send one explicitly: Provet resolves it from the linked order first, and otherwise from the id you posted to. If that id is a department’s own credential set (see wholesaler.created), that settles it outright.
If it is the organization-wide credential set, nothing distinguishes one
of the clinic’s departments from another, and a line with no order link is
filed under the organization’s default department. On a clinic with
several locations that is not necessarily the one the goods were delivered
to, and staff at the other locations will not see the line at all — the
review screen lists only the department they are working in. Send an
order_id, or an invoice_number that matches one of your orders,
whenever a line needs to reach the right location.
If your integration predates per-credential-set ids, Provet
falls back to the wholesaler’s own department configuration instead. A
wholesaler the clinic configured for more than one department then leaves
an unlinked line with nothing to resolve it against, and it comes back
rejected with department_unresolved.
Errors specific to receipts
Situation |
Response |
|---|---|
One or more lines could not be used |
200: check |
An unlinked line with no way to settle its department |
200 with |
|
400 |
More than 500 receipt lines in one call |
400 |
Wholesaler belongs to another integration |
403 |
Wholesaler id doesn’t exist, is archived, or isn’t an integration wholesaler |
404 |
See the error reference for the integration-specific errors that can occur across every phase.