Marketplaces / Zalando Direct connection / Zalando Product Status Report (PSR)

Zalando Product Status Report (PSR)

Version Created / Updated by Date Notes
1.0 Danail Deltchev N/A Initial creation
1.1 Danail Deltchev 09.02.2023 Change in time in review management

The purpose of this doc is to detail the way of working with the PSR reports so we can track and get what is happening with our products

API Docs: https://developers.merchants.zalando.com/docs/psr-api-overview.html

API Call: POST https://api.merchants.zalando.com/graphql

Via this call we can get near real time what is the current status of all products. We’d like to utilise this to pull the EANs we’ve sent for creation and track their status

The call provides multiple different options of searching for products via criteria and then the status of the called products. It requires page limit with definition of end of results in the following way: “when there are less results on the page than the called ones you’ve reached the last page” ❤

Searches:

Zalando Field Zalando Description
query.psr.countries Get list of countries available to merchant
query.psr.brands Get list of brands available to merchant
query.psr.seasons Get list of seasons available to merchant
query.psr.product_status_codes Get list of product status code
query.psr.product_models Search for product models. Complete product structure: model with all the configs, simples, and offers

Each search has its own options that can be found better in the interactive documentation provided for Insomnia

The one query that we will need the most is “product_models” as this is where we can call an actual product from and get its statuses

Example call:

{
  psr {
    product_models(
      input:
      { merchant_ids: ["YOUR_MERCHANT_ID"]
      , status_clusters: []
      , status_detail_codes: ["STATUS_DETAIL_CODE"]
      , season_codes: []
      , brand_codes: []
      , country_codes: []
      , search_value: "YOUR_PARTNER_MODEL_ID"
      , limit: 10
      }) {
      items {
        size_group { size length }
        product_configs {
          color_primary { code localized { en } }
          product_simples {
            ean
            size_codes { size length }
            status { status_detail_code }
          }
        }
      }
    }
  }
}

where the “YOUR_PARTNER_MODEL_ID” is our product main ID

This will return all of its variants as product_simples with their relevant EANs and status codes

Example response:

{
  "data": {
    "psr": {
      "product_models": {
        "items": [
          {
            "size_group": [
              {
                "size": "2FKO000E3A",
                "length": null
              }
            ],
            "product_configs": [
              {
                "color_primary": {
                  "code": "917",
                  "localized": {
                    "en": "multi-coloured"
                  }
                },
                "product_simples": [
                  {
                    "ean": "4351261305362",
                    "size_codes": {
                      "size": "36",
                      "length": null
                    },
                    "status": [
                      {
                        "status_detail_code": "STATUS_DETAIL_CODE"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "size_group": [
              {
                "size": "2FKO000E3A",
                "length": null
              }
            ],
            "product_configs": [
              {
                "color_primary": {
                  "code": "802",
                  "localized": {
                    "en": "black"
                  }
                },
                "product_simples": [
                  {
                    "ean": "4351261305370",
                    "size_codes": {
                      "size": "36",
                      "length": null
                    },
                    "status": [
                      {
                        "status_detail_code": "STATUS_DETAIL_CODE"
                      }
                    ]
                  },
                  {
                    "ean": "4351261305371",
                    "size_codes": {
                      "size": "38",
                      "length": null
                    },
                    "status": [
                      {
                        "status_detail_code": "STATUS_DETAIL_CODE"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  }
}

where the STATUS_DETAIL_CODE will be in the likes of:

{
  "ean":"4351261305361",
  "status":[
    {
      "status_detail_code":"ZANOP_01",
      "status_cluster": "BLOCKED"
}

Additional Information:

  • When building the query we can request specific information and not the whole product. To limit the payloads and data transfers we can request only what is needed. Below the mandatory fields we want to have in the response:
    • Status Detail Code
    • Status Cluster
    • Simple ID
  • The “Status Detail Code” is the field that provides us an error code if there is one. All Zalando errors are provided only in codes. We should create a set of values somewhere in Hemi so we can enrich this information (either in the DB or as a lookup file) - for the current list of values please refer to the attached error codes files. We want in the message stored in Hemi to keep both the code and the actual message just in case someone needs to check with Zalando
  • The “Status Cluster” is the actual status of the product. It has the following status options
    • LIVE - everything is ok with the item and it can sell
    • BLOCKED - item is stopped from selling
    • REJECTED - item is not accepted in the first place from selling
    • IN_REVIEW - item is examined by Zalando to decide what to do with it LIVE is the last status - if it is on LIVE status we should simply treat it as success. IN_PROGRESS is a skip status for and BLOCKED is a sure error. REJECTED is a little different - it is a status that usually goes into error BUT there are some specific errors which we should treat as “success” because these errors are not directly connected with the content of the product but are actually connected with the following flows (stock and price) or as “skip” as Zalando is still processing but still returns REJECTED with some information. So if we see REJECTED we need to check if the error code is in an “approved” list for us to proceed as success or skip this update and if not store as error. Below the error codes that are currently to be treated as success: ZANON_01, ZANON_02, ZANON_03, ZANOP_01, ZANOS_01, ZAON_01, ZAPRO_05 Below the error codes that are currently to be treated as skip: ACSBL_02, ACSREJ_68, JETBL_01, JETBL_02, JETBL_03, PSPRO_01, PSPRO_02, ZAPRO_01, ZAPRO_02, ZAPRO_03, ZAPRO_04 All “skip” statuses are to be treated the same way as if we haven’t received information for this product for the appointed Hemi waiting time. After this the product is to go into error with the error code from Zalando that we’ve been skipping
  • Every time the cron runs the expectation is it will pick all necessary products and based on Zalando limitations try to check their status. Please have in mind that there is also a limit of 240 calls per minute for the PSR but besides Zalando enforced limitations we are not expecting any throttling on our end
  • The cron is supposed to pick all products that have Product Account > Product Status = “Product Not Created“, ChannelItemID = ““ AND List / Update the whole item = “sent“
  • IF we receive a “success” status we want to store the ChannelItemID as per our abstraction explanations, set the Product Status to “Product Created”, List / Update the whole item = “normal” AND put Update Quantity and Update Price on “pending”
  • IF we receive an “error” status we want to store the message as per the above explanation and set List / Update the whole item = “error”

Notes:

  • In any example there doesn’t seem to be any “error” descriptions whatsoever as to why a product might be “REJECTED” (the presumed status for “error”)
  • I didn’t manage to find any other way of calling products to figure what is happening with them. This call can be used up to 240 times per minute as per Zalando’s rate limitsNEW - 19.10.2022:
  • Please have in mind there is an option to not receive the items immediately once we’ve submitted them (at which point we will receive empty response for the requested items). We need to implement a solution for this to track some time after we’ve successfully sent the item. Suggested method is via Product Account > Product Status Date Updated and for us to update it when we successfully send the product to Zalando. Then we can check on the cron runs to see if it is over the threshold we should treat this product as “missing” and mark it with “error” with the following message: “There is no product status report information found for this product for more than the selected threshold period. Please resubmit and/or contact Zalando support”. New (09.02.2023) The threshold should be managed by a value under Account Zalando. The new field should be called “Allowed hours in review status” and should take an integer value that is to be direct representative of the hours we should be waiting. This way this can be managed via setup and ensured it is working the right way on production for different clients. The new field should be mandatory when setting up the Account Zalando record. If this value is still empty though we should use 24 hours as default value just as a final fallback if something is broken.

Article status code definitions - full list.pdf

Article status code definitions - list.pdf

Article status code definitions - list.xlsx

Is this article helpful?
0 0 0