Making Products Available
Note
Part of the Wholesaler Integrations guide. This step assumes you have
already completed Registering an Available Wholesaler — catalog
uploads are checked against a specific clinic’s Wholesaler and its
linked AvailableWholesaler.
Once a clinic has connected your wholesaler (and you have received its
wholesaler.created webhook), you can push a product catalog for that
clinic. Clinic staff later import the catalog into their own stock from
the Provet UI.
A CSV file is the only way to provide a catalog. There is no endpoint that accepts individual products one at a time, and no other file format is accepted — every product you want a clinic to be able to import must be a row in the CSV described below, uploaded as a whole file in one go (see CSV column reference).
A minimal example file, using only the required columns:
product_type,product_number,product_name,wholesale_price,vat
Medicine,ACM-001,Amoxicillin 250mg,4.50,24
Supply,ACM-200,Surgical Gloves M,0.80,14
Step 1 — Request a presigned upload URL
# pk = the Wholesaler ID received in the wholesaler.created webhook.
curl -X POST \
"https://[env.]provetcloud.com/<provet_id>/api/0.1/wholesaler/<wholesaler_pk>/catalog_upload_url/" \
-H "Authorization: Bearer <oauth_access_token>" \
-H "Content-Type: application/json" \
-d '{"filename": "acme-catalog-2026-06.csv"}'
Response:
{
"upload_id": 55,
"filename": "acme-catalog-2026-06.csv",
"s3_key": "<opaque S3 object key>",
"url": "<presigned S3 upload URL>",
"fields": {
"key": "<opaque S3 object key>",
"AWSAccessKeyId": "...",
"policy": "...",
"signature": "...",
"x-amz-security-token": "..."
},
"expected_columns": ["product_type", "product_number", "..."]
}
s3_key, url, and fields are opaque — pass them through as-is to
the S3 upload in Step 2 without parsing or constructing them yourself.
fields is not a fixed set: submit every key-value pair it returns, not
just the ones shown here.
Authorization check. The target must be an active integration
wholesaler — not archived, and created from an AvailableWholesaler
(Registering an Available Wholesaler). Checks run in order, so the
failure you see depends on which condition is not met:
Not an active integration wholesaler (does not exist, archived, or a wholesaler registered with a type other than
Integration, such asEmail, with no linkedAvailableWholesaler): 404 (No Wholesaler matches the given query.).An active integration wholesaler whose linked
AvailableWholesalerbelongs to a different OAuth application: 403 (Token does not match this wholesaler.).
Filename validation: must end in .csv, no path traversal (/,
\, ..).
Step 2 — Upload the CSV directly to S3
curl --request POST \
--url "<url from step 1>" \
--form "key=<key from fields>" \
--form "AWSAccessKeyId=<AWSAccessKeyId from fields>" \
--form "policy=<policy from fields>" \
--form "signature=<signature from fields>" \
--form "x-amz-security-token=<x-amz-security-token from fields, if present>" \
--form "file=@/path/to/acme-catalog-2026-06.csv"
Include every field returned under fields in Step 1 as its own
--form entry, in the order S3 returned them, with file last. Do
not set a Content-Type: multipart/form-data header yourself —
curl --form generates the multipart body and its matching boundary
for you, and a manually-set header without that boundary breaks the
upload.
Note
Nothing needs to be reported back to Provet once the upload completes — Provet is notified automatically by S3 and proceeds with the product import step once a clinic imports it. The CSV itself is not read or validated at upload time; parsing and row-level validation happen at import time (Clinic-side import), so a malformed CSV uploads successfully here and only surfaces as an error later.
CSV column reference
Each row describes one product. A header row is mandatory; columns are matched by name and may appear in any order. Files are read as UTF-8 (a byte-order mark is tolerated), values are trimmed of surrounding whitespace, and a blank optional cell is treated as “not set”.
Required columns
Every row must include these five columns, or the row is rejected:
Column |
Type |
Meaning |
|---|---|---|
|
string |
Product category: |
|
string |
Your product code / SKU. Primary matching key: matched against each item’s primary, then secondary, then tertiary wholesaler code, and written to the item’s wholesaler code on import. |
|
string |
Product display name. Stored on the matched item; used for search and sorting in the import UI. |
|
decimal |
Purchase price excluding VAT ( |
|
decimal |
VAT rate as a percentage (e.g. |
Optional columns
Column |
Type |
Meaning |
|---|---|---|
|
string |
Unit of measure / package description, e.g. |
|
string |
Product barcode (EAN / GTIN). Stored on the item; used as a lowest-priority matching key when barcode matching is enabled. |
|
decimal |
Recommended retail price excluding VAT. Pre-populates the item’s selling price, and is the fallback source for |
|
string |
ISO 4217 currency of the prices, e.g. |
|
decimal |
Total dosable units across all packages. If omitted or not greater than 0, computed as |
|
decimal |
Dosable units in a single package (e.g. |
|
decimal |
Number of packages in one wholesale unit (e.g. |
|
string |
Active pharmaceutical ingredient, for medicines. |
|
integer |
Route-of-administration code for medicines ( |
|
decimal |
Strength / concentration of the active substance, for medicines. |
Note
wholesale_price is the clinic’s purchase cost; product_price is
the suggested resale price. Matching is always scoped by
product_type, then resolved by product_number (primary, then
secondary, then tertiary wholesaler code), with barcode as a fallback
when barcode matching is enabled.
Clinic-side import
Importing the uploaded catalog into stock is a manual step clinic staff perform later from the Provet UI, under Settings > Import & Export > Import from Lists — it is not something your system calls or needs to wait for. Clinic staff filter by wholesaler and item type, review new items (highlighted) and updates to existing items (compared side-by-side) in the generated list, then assign a sub-group, VAT group, and markup before finalizing the import.
See Import or Update Items from Integrated Wholesalers for the full customer-facing walkthrough of this screen. There is currently no way for your integration to be notified of, or query, problems found at import time (e.g. a malformed row) — those surface only in the clinic’s own import UI.
See the full error reference for the errors your own API calls can receive.