Veterinary pricing — price override by relation
Custom data + override use case · The flagship pattern that illustrates the power of ShopiMind: the same product displays a different price per customer because each customer is attached to a vet who negotiates their own pricing.
The business need
You sell therapeutic foods and veterinary care products for pets. Each customer was referred by a prescribing vet who negotiated their own prices on all or part of your catalog.
Concretely:
- The Veterinary Diet 12 kg bag costs €79.90 at public price.
- Dr Martin (Lyon practice) sells it at €64 to their 230 referred customers.
- Dr Durand (Lille practice) sells it at €71 to their 180 referred customers.
- Mrs Dubois (Dr Martin's customer) must see €64 across all marketing content sent to her: post-purchase email, abandoned cart recovery, recommendation email, on-site popins and smart content.
Managed via manual promo codes, this is unmanageable (700 vets × 1,200 products = 840,000 potential combinations) and marketing emails always display the public price.
The ShopiMind solution
Three custom data definitions chained by relations, plus one override that replaces the native price with the negotiated price when the connected contact is attached to a vet.
The data model
"Veterinarian" definition
Reference list of prescribing practices.
nom(text) — uniqueness key.ville,telephone,email(text).
"Vet pricing" definition
The negotiated grid. One row = one price negotiated by a vet on a product.
veto_id(number) — relation to the Veterinarian definition.product_id(text) — relation to the Product entity.price(decimal) — the negotiated price.price_discount(decimal) — optional discounted price.- Uniqueness key:
(veto_id, product_id)— guarantees a single price per vet/product pair.
"Vet assignment" definition
Which vet follows which customer.
contact_id(number) — relation to the Contact entity.veto_id(number) — relation to the Veterinarian definition.since(date) — start of the customer/vet relationship.- Uniqueness key:
contact_id— a customer is followed by at most one vet.
Price override
The products.price field gets a priority override sourced from Vet pricing.price. On every product resolution for a given contact, ShopiMind:
- Reads the contact's Vet assignment.
- Looks up Vet pricing for that
(veto_id, product_id)pair. - If found, returns the vet grid price. Otherwise falls back to the public price.
Implementation
1. Create the catalog of vets
curl -X POST 'https://core.shopimind.com/v1/custom-data-definitions' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '{
"name": "Veto",
"description": "Catalog of prescribing veterinarians",
"unique_keys": ["nom"],
"fields": [
{ "name": "nom", "label": "Vet name", "type": "text", "required": true },
{ "name": "ville", "label": "City", "type": "text", "required": false },
{ "name": "telephone", "label": "Phone", "type": "text", "required": false },
{ "name": "email", "label": "Email", "type": "text", "required": false }
]
}'Note the returned id_definition — say 184.
2. Create the vet × product pricing grid
This is the core of the system: a definition with two relations (to the Veterinarian definition, and to the Product entity).
curl -X POST 'https://core.shopimind.com/v1/custom-data-definitions' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '{
"name": "VetoTarif",
"description": "Price negotiated by a vet on a catalog product",
"unique_keys": ["veto_id", "product_id"],
"fields": [
{ "name": "veto_id", "label": "Vet", "type": "number", "required": true },
{ "name": "product_id", "label": "Product (ID)", "type": "text", "required": true },
{ "name": "price", "label": "Negotiated price","type": "decimal", "required": true },
{ "name": "price_discount", "label": "Promo price", "type": "decimal", "required": false }
],
"relationships": [
{ "sourceField": "veto_id", "targetSchemaType": "custom", "targetSchema": "184", "targetField": "id" },
{ "sourceField": "product_id", "targetSchemaType": "system", "targetSchema": "products", "targetField": "product_id" }
]
}'Returned id_definition: 185.
3. Create the contact → vet assignment
curl -X POST 'https://core.shopimind.com/v1/custom-data-definitions' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '{
"name": "AffectationVeto",
"description": "Which veterinarian follows each contact",
"unique_keys": ["contact_id"],
"fields": [
{ "name": "contact_id", "label": "Contact", "type": "number", "required": true },
{ "name": "veto_id", "label": "Vet", "type": "number", "required": true },
{ "name": "since", "label": "Since", "type": "date", "required": false }
],
"relationships": [
{ "sourceField": "contact_id", "targetSchemaType": "system", "targetSchema": "contacts", "targetField": "id_contact" },
{ "sourceField": "veto_id", "targetSchemaType": "custom", "targetSchema": "184", "targetField": "id" }
]
}'Returned id_definition: 186.
4. Activate the price override
This is the critical step. You're telling ShopiMind: "for the products.price field, look up VetoTarif.price first."
curl -X PUT 'https://core.shopimind.com/v1/custom-data-definitions/overrides?target_system_schema=products&system_field_name=price' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '{
"overrides": [
{ "id_custom_data_definition": 185, "custom_field_name": "price", "priority": 1 }
]
}'And the same thing for price_discount:
curl -X PUT 'https://core.shopimind.com/v1/custom-data-definitions/overrides?target_system_schema=products&system_field_name=price_discount' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '{
"overrides": [
{ "id_custom_data_definition": 185, "custom_field_name": "price_discount", "priority": 1 }
]
}'Immediate effect
Once the override is active, every ShopiMind scenario that resolves a product for a contact automatically applies the right price: post-purchase emails, recommendation emails, abandoned cart recovery, on-site popins, smart content. No conditional logic in templates — ShopiMind resolves the value dynamically.
5. Sync data from your back-office
Veterinarians (on each add/update on the CRM side):
curl -X POST 'https://core.shopimind.com/v1/custom-data-records/184' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '[
{ "nom": "Dr Martin Lyon", "ville": "Lyon", "telephone": "04 78 12 34 56", "email": "contact@vetmartin.fr" },
{ "nom": "Dr Durand Lille", "ville": "Lille", "telephone": "03 20 56 78 90", "email": "contact@drdurand.fr" }
]'The upsert is on nom (uniqueness key). Capture the returned id values (e.g. Dr Martin = 12, Dr Durand = 13) — they're the foreign keys for the pricing grid and the assignments.
Pricing grid (likely daily, batched):
curl -X POST 'https://core.shopimind.com/v1/custom-data-records/185' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '[
{ "veto_id": 12, "product_id": "VET-DIET-12KG", "price": 64.00, "price_discount": 58.00 },
{ "veto_id": 12, "product_id": "VET-DIET-3KG", "price": 19.50, "price_discount": null },
{ "veto_id": 13, "product_id": "VET-DIET-12KG", "price": 71.00, "price_discount": null }
]'Maximum 50 records per request — an initial push for 700 vets × 200 products on average means ~2,800 batched requests (parallelizable to ~10 workers while staying within the 600 req/min quota).
Contact → vet assignments:
curl -X POST 'https://core.shopimind.com/v1/custom-data-records/186' \
-H 'spm-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '[
{ "contact_id": { "by": "id_customer", "value": "CUST-A4521" }, "veto_id": 12, "since": "2024-03-15" },
{ "contact_id": { "by": "email", "value": "marie.dupont@example.com" }, "veto_id": 12, "since": "2025-09-20" },
{ "contact_id": { "by": "id_customer", "value": "CUST-A4523" }, "veto_id": 13, "since": "2025-11-08" }
]'Reference a contact without pre-resolution
No need to fetch the ShopiMind id_contact before writing: send your id_customer or email directly, the server resolves it. See Reference a contact.
6. Verify the override in an email
Create a simple post-purchase recap scenario. On send, ShopiMind automatically resolves each product's price based on the recipient and their vet assignment.
For Mrs Dubois (assigned to Dr Martin), the bag of kibble appears at €64; for Mr Bernard (Dr Durand), the same line displays €71; for a customer without a vet, €79.90. No conditional logic in the template — ShopiMind dynamically resolves the right value for each recipient.
Going further
Auto-refresh of prices
When a vet updates their pricing, repush their grid in bulk via saveCustomDataRecords (the upsert on (veto_id, product_id) updates existing rows without duplication).
Limits to know
- The override only covers
products.priceandproducts.price_discounttoday. Not (yet)quantity(stock),nameordescription. - Three priority sources max per overridden field.
Recap
| Step | Endpoint | When |
|---|---|---|
| Create Vet definition | createCustomDataDefinition | Once |
| Create VetoTarif definition (with 2 relations) | createCustomDataDefinition | Once |
| Create AffectationVeto definition (with 2 relations) | createCustomDataDefinition | Once |
| Activate the price override | updateFieldOverridePriorities × 2 | Once (price + price_discount) |
| Sync vets | saveCustomDataRecords | On each CRM add/update |
| Sync pricing grid | saveCustomDataRecords | Daily batched OR real-time on change |
| Sync assignments | saveCustomDataRecords | Daily OR on each account creation |