Registering an Available Wholesaler
Note
Part of the Wholesaler Integrations guide. See
Scope, Webhooks and Errors first for the wholesaler OAuth
scope and the webhook delivery/signing mechanics referenced below.
This is the first step of the integration: without a registered
AvailableWholesaler, clinics cannot find or link you, and none of the
other endpoints in this guide will accept your token for a wholesaler that
doesn’t exist yet.
Note
Step 0, before any of this: your application itself must already be
registered as an integration — see Adding an Application in Provet. You can’t
obtain a wholesaler-scoped access token until that’s done. Consider
subscribing to the installation webhook described in
Get Notified for Installations of Your Applications in a Provet Organisation so you’re notified as soon as a clinic
installs your application, and can automate the rest of onboarding
(registering your AvailableWholesaler for them, etc.) from there.
Step 1 — Register an Available Wholesaler
An AvailableWholesaler lives in the clinic’s Provet database
and represents a wholesaler integration a specific clinic can later pick
from a list. In practice, you create or update this entry once your system
is registered as an integration with Provet.
# Create a new available wholesaler.
curl -X POST "https://[env.]provetcloud.com/<provet_id>/api/0.1/available_wholesaler/" \
-H "Authorization: Bearer <oauth_access_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Veterinary Supplies",
"email": "orders@acmevet.example",
"register_number": "FI12345678",
"webhook_callback_url": "https://acmevet.example/webhooks/inbound",
"verification_salt": "<generated_secret>"
}'
# List all registered available wholesalers.
curl "https://[env.]provetcloud.com/<provet_id>/api/0.1/available_wholesaler/" \
-H "Authorization: Bearer <oauth_access_token>"
# Retrieve one by ID.
curl "https://[env.]provetcloud.com/<provet_id>/api/0.1/available_wholesaler/<id>/" \
-H "Authorization: Bearer <oauth_access_token>"
# Update one (e.g. rotate verification_salt, or update webhook_callback_url).
curl -X PATCH "https://[env.]provetcloud.com/<provet_id>/api/0.1/available_wholesaler/<id>/" \
-H "Authorization: Bearer <oauth_access_token>" \
-H "Content-Type: application/json" \
-d '{"verification_salt": "<new_generated_secret>"}'
# Delete one.
curl -X DELETE "https://[env.]provetcloud.com/<provet_id>/api/0.1/available_wholesaler/<id>/" \
-H "Authorization: Bearer <oauth_access_token>"
[env.] is the deployment the clinic lives on (e.g. us.,
enterprise., or omitted for the default environment).
The owning integration is always taken from your access token — a value sent in the request body is ignored, and creating one with a legacy API key instead of an OAuth 2.0 access token answers 401.
webhook_callback_url and verification_salt
webhook_callback_url must use HTTPS and point to a publicly
reachable host — private, loopback, and localhost addresses are
rejected with 400, since deliveries can carry decrypted credentials
(see Step 2 — Clinic connects the wholesaler). This applies while you
are developing too, so point the URL at a public tunnel to your local
receiver, or at a deployed one, rather than at your own machine.
verification_salt is the shared secret used to sign every webhook
delivered to that URL — see “Webhook signature verification” in
Scope, Webhooks and Errors for the full scheme.
Note
Despite the field name, this is a shared secret, not a salt: it is
the HMAC key, it is never sent to you in a delivery, and it must be
protected like any other credential. Generate it from a
cryptographically secure random source — 32 random bytes rendered as 64
hexadecimal characters is a good default, for example
openssl rand -hex 32. Do not derive it from your company name, your
client id, or anything else guessable.
In short:
Accepted on create and update, as a field on
available_wholesaler/.Required on create — a registration cannot be created without one.
Write-only — Provet never returns it on any response, so keep your own copy.
16 to 128 characters, and any character is accepted. It is encoded as UTF-8 to form the HMAC key, so a value outside US-ASCII must be encoded the same way on your side when you verify.
Updates take effect atomically for the next delivery, with no grace period, so rotation is coordinated from your side (see Scope, Webhooks and Errors).
These records are later used in two places: clinic staff see them when
choosing a wholesaler to connect, and your system authenticates later
uploads against the registered AvailableWholesaler.
Step 2 — Clinic connects the wholesaler
This step is internal to Provet. Clinic staff pick your
wholesaler from a list in the UI and link it to their
AvailableWholesaler registration, from Stock > Inventory >
Wholesalers > Create Wholesaler, selecting “Integration” as the
wholesaler type — your available wholesaler is shown as one of the
options. See Add Wholesalers for the
customer-facing walkthrough of this screen, and Wholesalers in
Provet for how clinics manage
wholesalers more generally.
Once linked:
A
Wholesaleris created in the clinic’s database, pointing back at yourAvailableWholesaler.Provet
POSTs awholesaler.createdevent to yourwebhook_callback_url.
Use the wholesaler.id you receive as the wholesaler_pk for catalog
uploads (Making Products Available) and to correlate incoming
orders (Handling Orders).
Later on, if clinic staff edit the credentials or clinic location of that
Wholesaler, you receive a wholesaler.updated event with the new
values; if they remove it, you receive a wholesaler.deleted event for
that wholesaler.id. See the event references below.
The id identifies one set of credentials, not the clinic as a whole. A clinic
that enters separate credentials for some of its locations sends you one
wholesaler.created per location, each with its own wholesaler.id and
its wholesaler.department set; the credentials shared by the remaining
locations arrive as one more event with wholesaler.department null.
If that clinic-wide event carries only null credentials, the clinic has not
entered clinic-wide credentials yet; keep the id, it stays valid.
Treat every id as a separate account, and expect order.placed to carry the
id of the account the ordering location uses. You never need to know whether
an id is location-specific or clinic-wide.
Note
The catalog belongs to a credential set, not to the clinic as a whole: uploading against one id replaces the catalog only for the locations that order under that id. A location whose id has no upload of its own sees no catalog, even if a sibling id of the same clinic (including the clinic-wide one) does. Upload against every id you have been given.
wholesaler.created event reference
Note
This event is also published in the API reference, whose machine-readable payload schema you can generate a client from.
Provet POSTs this event to
available_wholesaler.webhook_callback_url when a clinic connects the
wholesaler. It only fires for integration wholesalers, so it
always describes a Wholesaler created from one of your
AvailableWholesaler registrations. Any field the clinic left blank is
sent as null.
{
"event": "wholesaler.created",
"event_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"available_wholesaler": {
"id": 7,
"name": "Acme Veterinary Supplies",
"email": "orders@acmevet.example",
"register_number": "FI12345678"
},
"wholesaler": {
"id": 123,
"customer_number": "CLINIC-42",
"customer_username": "clinic42-login",
"customer_password": "s3cr3t",
"department": {"id": 4, "name": "Main Clinic"}
}
}
Field |
Type |
Optional |
Meaning |
|---|---|---|---|
|
string |
No |
Always |
|
string |
No |
UUID identifying this event. See deduplication rules. |
|
number |
No |
ID of your registered |
|
string |
No |
Name of that |
|
string / null |
Yes |
Contact email registered for the |
|
string / null |
Yes |
Business / registration number registered for the |
|
number |
No |
Identifies this set of credentials — the |
|
string / null |
Yes |
The clinic’s customer number with the wholesaler. |
|
string / null |
Yes |
Login / username the clinic entered for this wholesaler. |
|
string / null |
Yes |
Password the clinic entered, decrypted. Treat it as a secret. |
|
object / null |
Yes |
Set when credentials are specific to one clinic location; |
Note
name, email, and register_number live under
available_wholesaler because they describe the integration you
registered. For an integration wholesaler the clinic cannot edit them,
so they are not repeated on the wholesaler object.
Provet customers can choose to enable wholesalers globally in
their organization, or locally in specific clinic locations / departments.
The credential fields carry department-specific values when department
is present, otherwise the wholesaler-global values.
wholesaler.updated event reference
Note
This event is also published in the API reference, whose machine-readable payload schema you can generate a client from.
Provet POSTs this event when clinic staff edit the
credentials, or the clinic location, of a Wholesaler created from one
of your AvailableWholesaler registrations. Only a real change is
delivered — editing the wholesaler’s name or address sends nothing, since
that data lives on available_wholesaler and cannot be edited by the
clinic anyway.
A clinic location entering its own credentials for the first time is not an
update: that creates a new credential set, which reaches you as
wholesaler.created under its own wholesaler.id.
The body repeats the wholesaler.created shape carrying the new values,
so one handler can serve both events; only event tells them apart.
Credentials are reported as the editing clinic location sees them, so an
edit to a location-specific override carries wholesaler.department,
exactly as it would for wholesaler.created.
Important
One edit can update several ids. Clinic locations that were given the
same username share a single account with you, so Provet keeps
their password identical: changing it for one location changes it for
every location using that username. Each of those is a credential set of
its own, so you receive one wholesaler.updated per affected
wholesaler.id, not just for the location the clinic was editing.
Expect, therefore, to receive an update for an id nobody appears to have touched, and apply each event to the id it names rather than assuming a clinic edit only concerns one account. Locations set up with different usernames are separate accounts and are never changed by an edit to another location.
{
"event": "wholesaler.updated",
"event_id": "5a4f0e1a-1b2c-4d3e-9f8a-7c6b5a4d3e2f",
"available_wholesaler": {
"id": 7,
"name": "Acme Veterinary Supplies",
"email": "orders@acmevet.example",
"register_number": "FI12345678"
},
"wholesaler": {
"id": 123,
"customer_number": "CLINIC-42-NEW",
"customer_username": "clinic42-login",
"customer_password": "n3w-s3cr3t",
"department": {"id": 4, "name": "Main Clinic"}
}
}
Fields have the same meaning as on wholesaler.created — see the table
above.
wholesaler.deleted event reference
Note
This event is also published in the API reference, whose machine-readable payload schema you can generate a client from.
Provet POSTs this event when clinic staff remove (archive)
a Wholesaler created from one of your AvailableWholesaler
registrations. Stop pushing catalogs for that wholesaler.id — the
clinic keeps its purchase history, so past orders remain valid. Removing
the whole wholesaler sends you one event, and one id, per location: the
same set of ids wholesaler.created gave you.
It also fires for a single id on its own, when one clinic location gives up its location-specific credentials while the rest of the clinic keeps ordering. That location goes back to ordering under the clinic-wide id, which is unaffected; the id it used to have is dead and is never reissued.
If the clinic connects again later, you receive a fresh
wholesaler.created for each wholesaler.id, which you should treat
exactly as a first connection. The id may be one you had retired: restoring
a link keeps its ids, connecting again from scratch gets new ones. Match on
the id you are given.
{
"event": "wholesaler.deleted",
"event_id": "9c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"available_wholesaler": {
"id": 7,
"name": "Acme Veterinary Supplies",
"email": "orders@acmevet.example",
"register_number": "FI12345678"
},
"wholesaler": {
"id": 123
}
}
Field |
Type |
Optional |
Meaning |
|---|---|---|---|
|
string |
No |
Always |
|
string |
No |
UUID identifying this event. See deduplication rules. |
|
number |
No |
ID of your registered |
|
string |
No |
Name of that |
|
string / null |
Yes |
Contact email registered for the |
|
string / null |
Yes |
Business / registration number registered for the |
|
number |
No |
The id of the credential set that was removed — the same one |
Note
No credentials are sent — they have no bearing on a removal.
Errors specific to registration
Situation |
Response |
|---|---|
OAuth token invalid/expired |
401 |
Update / delete on an |
403 |
Creating an |
401 |
See the error reference for the integration-specific errors that can occur across every phase.