Fruugo Create Products
Summary of Changes: (The purpose of this table is to keep traceability and Product team to highlight the things that were changed into the scope, based on comments or discussions)
Version | Date | Created / Updated | Notes |
---|---|---|---|
v1.0 | 15/01/2024 | Hristiyan | First publish |
We are able to create products in Fruugo using the create products call. The request will be made via a JSON payload which will contain all the information for the products. Since the API calls are asynchronous, we should provide a webhook endpoint to Fruugo in order for them to provide us with a response whenever it is ready.
We want all triggers, validations and standardizations to be as per the abstraction Product Listing general requirements
When submitting the request, we can provide a X-Correlation-ID
which is an identifier used for tracking the request. The X-Correlation-ID
is used to tie the response back to the original request. If we provide a correlation id when making a request, Fruugo will use that and include it in the webhook response. If we don’t provide one, Fruugo will auto-generate one for us, which should be included in the initial response to our request, then it will also be included in the web-hook response. We are fine with both cases so it is up to the developers to decide whether to provide a X-Correlation-ID
or not.
The webhook endpoint is : TBA
API Docs : https://developer.fruugo.com/#tag/products-v1
API Call : POST https://product-api.fruugo.com/v1/products
Example request :
{
"products": [
{
"product": {
"productId": "prod-ab-1234",
"brand": "string",
"manufacturer": "string",
"category": "string"
},
"skus": [
{
"skuId": "string",
"gtins": [
{
"codeType": "EAN",
"code": "string"
}
],
"details": {
"skuDescriptions": [
{
"language": "ar",
"title": "string",
"text": "string",
"attributes": [
{
"name": null,
"value": null
}
]
}
],
"media": [
{
"description": "string",
"url": "string",
"type": "IMAGE"
}
]
},
"supplyInfo": {
"stockStatus": "INSTOCK",
"stockQuantity": 99999,
"leadTime": 1,
"restockDate": "2019-08-24"
},
"pricingInfo": [
{
"vatRate": 20.55,
"currency": "AED",
"country": [
"AE"
],
"normalPrice": {
"price": 0,
"vatInclusive": false
},
"discountPrice": {
"price": 0,
"vatInclusive": false,
"startDate": "2019-08-24",
"endDate": "2019-08-24"
}
}
],
"packageWeight": 99999,
"volume": 0
}
]
}
]
}
Request mapping :
Fruugo Field | Fruugo Notes | Required? | Hemi Field | Hemi Notes | ||||
---|---|---|---|---|---|---|---|---|
products |
||||||||
product |
||||||||
productId |
Product identifier code that you recognise when it is provided on order information. The same ProductId should be used to group together SKUs where they are available with multiple options (Colours, Sizes etc.). | Yes | Product Account > Variation Group |
OR
Product
> SKU
| We need to have a logic when the products we are sending are part of a variation, we need to send the Variation Group
, if it is a single item then send the SKU. |
| | | brand
| | | The brand name of the Product. | No | Product Account
> Item Specifics
OR
Product
> Brand
| Product Account
> Item Specifics
is with priority. |
| | | manufacturer
| | | The name of the manufacturer of the Product. | No | Product Account
> Item Specifics
| |
| | | category
| | | The accurate category of the Product to be classified in on Fruugo using the Fruugo category path. The category value must be accurate to the nature of the Product for taxation and use the full Fruugo category path. | Yes | Product Account
> Primary Category ID
| |
| | skus
| | | | | | | |
| | | skuId
| | | If the Product has multiple options, the SkuId uniquely identifies and separates each option. If a Product doesn’t have multiple options the SkuId can be the same as the ProductId. Each ProductId & SkuId combination must be unique and not repeated for any other Product(s) | Yes | Product
> SKU
| |
| | | gtins
| | | | | | |
| | | | codeType
| | | Yes | EAN
OR
MPN
OR
UPC
OR
ISBN
| We need to send the type of the product code.
We want to be able to control which codeType to be sent via radio buttons in the Account Fruugo
table. |
| | | | code
| | The Product code. Do not include spaces or hyphens. | Yes | Product
> EAN
OR
MPN
OR
UPC
OR
ISBN
| string <= 14 characters.
Depending on what is selected in the Account Fruugo
, we send the value of the selected field. |
| | | details
| | | Describes the details of the Product SKU | Yes | | |
| | | | skuDescriptions
| | | | | |
| | | | language
| | Two digit ISO code (lower case) of the language of the text fields of the feed, such as Title, Description, AttributeColor etc. The language must be one of those supported on Fruugo | Yes | Account Fruugo
> Language Default
| We want to have a dropdown list with predefined languages : ar
cs
da
de
el
en
"es"
et
fi
fr
he
hi
hu
it
jp
ko
lt
lv
nl
no
pl
pt
ro
ru
sk
sv
tr
zh
Default value should be en
. If the Account Fruugo
> Language Default
is filled, we send the value in it, otherwise we send the default value. |
| | | | title
| | Product in title case (not block capitals) which identifies the nature of the Product. | Yes | Product Account
> Title
| |
| | | | text
| | | Yes | Product Account
> Description
| |
| | | | attributes
| | | No | | |
| | | | | name
| The name if this attribute, custom mappings to Fruugo attribute types are currently defined by Fruugo operations. Use of the name "Colour" and "Size" are universally mapped to Fruugo Colour and Size types respectively | Yes | Product Account
> Variation Specifics
OR
Product Account
> Item Specifics
| We need to send the Variation Specifis name
If the item is not a variation, we sent the Item Specifics name |
| | | | | value
| The value of this attribute | Yes | Product Account
> Variation Specifics
OR
Product Account
> Item Specifics
| We need to send the Variation Specifics value
If the item is not a variation, we sent the Item Specifics value |
| | | media
| | | | No | | |
| | | | description
| | | No | N/A | |
| | | | url
| | | Yes | This should be handled as per the Images Abstraction https://hemi.atlassian.net/wiki/spaces/HEMI/pages/1969540
| We need to send all images that we have for the product as separate nodes under the media
node. |
| | | | type
| | | Yes | Hardcoded as IMAGE
| |
| | | supplyInfo
| | | Provides the availability of this Product option | Yes | | |
| | | | stockStatus
| | The stock status of a Product which indicates whether it’s available for purchase. | Yes | Check Notes | The permitted values are:
• INSTOCK – We have the Product currently in stock.
• BACKORDERED – The Product is currently out of stock but is on backorder.
• OUTOFSTOCK – The Product is currently out of stock but may return.
• NOTAVAILABLE – The Product is permanently out of stock and needs to be removed.
We want to use the INSTOCK and OUTOFSTOCK for now.
We want to have a logic :
If we have Product Account
> Quantity
>= 1 , we send INSTOCK
If we have Product Account
> Quantity
<= 0 we send OUTOFSTOCK. |
| | | | stockQuantity
| | | | Product Account
> Quantity
| |
| | | | leadTime
| | The number of days from order to dispatch for any INSTOCK Product. | No | Shipping Template
> Dispatch Time Max
OR
Product Account
> Dispatch Time Max
| Product Account
> Dispatch Time Max
is with priority. If we dont have it in the product account, we use the shipping template.
If we dont have a shipping template we just exclude the node from the payload as it is not required. |
| | | | restockDate
| | The date when a Product which is currently marked as OUTOFSTOCK in 'StockStatus' will be back in stock and available for order. | No | N/A | |
| | | pricingInfo
| | | | Yes | | |
| | | | vatRate
| | | No | Account
> VAT
OR
Product Account
> VAT
| Product Account
> VAT
is with priority. |
| | | | currency
| | Three letter ISO code (Upper Case) of the currency of the price properties | Yes | Account
> Exchange Rate Calculator Currency
| |
| | | | country
| | | No | Account
> Country
| We need to send the ISO 3166-1 alpha-2 code. For example Great Britain will be GB, USA is US etc. |
| | | | normalPrice
| | The normal price for this Product option. | Yes | | |
| | | | | price
| | Yes | Product Account
> RRP
| If RRP
is empty we should send the Product Account
> Price
as normalPrice
and not include discountPrice
in the payload. |
| | | | | vatInclusive
| Is the price inclusive of VAT? | Yes | Account Fruugo
> Price includes VAT
| boolean
Options are true
or false
|
| | | | discountPrice
| | | No | | |
| | | | | price
| | Yes | Product Account
> Price
| |
| | | | | vatInclusive
| | Yes | Account Fruugo
> Price includes VAT
| boolean
Options are true
or false
|
| | | | | startDate
| The start date for the discount price to begin being displayed, if it is a time limited promotion. | No | Product Account Fruugo
> Sale Start Date
| New field in Product Account Fruugo
If only the Sale End Date
is filled we push NOW() as Sale Start Date
If none of the fields are filled we exclude them from the payload.
Date format is 2019-08-24
|
| | | | | endDate
| The end date for the discount price to stop being displayed, if it is a time limited promotion. | No | Product Account Fruugo
> Sale End Date
| New field in Product Account Fruugo
If only the Sale Start Date
is filled we exclude both fields from the payload.
If the field is empty we just exclude it from the payload.
Date format is 2019-08-24
|
| | | packageWeight
| | | The shipping weight of the Product provided in grams with no decimal places or unit of measurement. For example, 190. | No | Product
> Weight
| |
| | | volume
| | | | No | N/A | |
As mentioned above, the connection is asynchronous, so we would not immediately receive a response. A successful request would return a 204 ****Request has been accepted for processing, and update will be performed asynchronously response.
Callback
POST:
Callback payload sample :
{
"value": {
"type": "SaveProductResponse",
"merchantId": 7418,
"correlationId": "c3145570-0731-45db-9c9a-33f97d588400",
"payload": "{'productCreated': true, 'productUpdated': false, 'merchantProductId': 'papi599VAT','createdSkus': [{'merchantSkuId': 'papi599_1VAT','merchantSkuQualityStatus': 'OK','validationErrors': []}],'updatedSkus': []}"
}
}
Mapping :
Fruugo Field | Hemi Field | Hemi Notes | |
---|---|---|---|
value |
|||
type |
N/A | ||
merchantId |
N/A | ||
correlationId |
N/A | ||
payload |
N/A | From the payload node we only want to check for two things : 'productCreated' and 'validationErrors' . |
If productCreated
is false and there are any validation errors we want to store the errors in the Product Account
> Update Item Error
(as per the abstraction) and not mark the product as created.
If productCreated
is true then there shouldn’t be any validationErrors
so we consider the product as created successfully. |
Example unsuccessful responses :
400 Bad Request
Response sample :
[
{
"type": "field",
"field": "productId",
"message": "must not be null"
},
{
"type": "field",
"field": "skuIds",
"message": "size must be between 1 and 200"
}
]
Mapping :
Fruugo Field | Hemi Field | Notes |
---|---|---|
type |
N/A | |
field |
N/A | |
message |
Product Account > Update Item Error |
429 Request has been rejected due to exceeding rate limit. Retry after delay specified in “Retry-After” response header.
Sample response :
{
"status": 429,
"reason": "Too Many Requests",
"method": "GET",
"path": "/v1/Products"
}
If we receive a 429 response we don’t want to store any error or map anything. Instead we want to check in the response header for the ‘retry-after’ where Fruugo will provide the time (in seconds) we need to wait before retrying the request and then re-send the request after the givne time has passed.