How to work with Advert Vetting

In this article we take you through how Advert vetting works, examine the Advert object fields that will be impacted at each stage of the flow, and work with the API calls that can be used to automate the workflow.

What you’ll learn

  • The high-level Advert Vetting flow
  • The Advert object fields that reflect the state of an Advert in the Vetting process
  • The roles the Operator and the Seller play in the flow
  • The APIs and Webhooks that can be used to automate the Advert Vetting Flow

The Advert Vetting Flow

Advert Vetting (sometimes referred to as Product Vetting) is not enabled by default, this is an opt-in Marketplacer feature. If you want to learn more about enabling this feature, then please refer to this Knowledge Base Article.

The Concept

The high-level concept, or use-case for Advert Vetting is to allow Operators to control what Adverts (Products) can be published, most usually to ensure Advert listings adhere to the standards of the marketplace, e.g.:

  • Image dimensions and quality
  • Product Descriptions
  • Regulatory requirements

A simplified view of the process is shown below:

Simple Advert Vetting Flow

We will elaborate this flow in the next section.

Note: for the remainder of this Article, and for all described scenarios, we are expecting that the Advert Vetting Flow is enabled.

From an API and webhook perspective, the core of the Advert Vetting flow is the Advert Object, specifically the following fields:

Advert Field : Description
requiresVettingtrue if the Advert is an a state where vetting is required to occur
requiresVettingAtThe time when requiresVetting is set to true
vettedtrue only is the advert has successfully been approved as part of the vetting flow
vettingRejectedtrue if the Advert has been through the vetting flow and been rejected, otherwise this will be false
vettingRejectedReasonTextual description of why the advert was rejected.

To further illustrate how these fields are employed as part of the vetting flow, let’s take a look at 2 scenarios:

Scenario 1 - Successful Vetting FLow

StepParticipantrequiresVettingvettedvettingRejectedvettingRejectedReasonrequiresVettingAt
Advert Newly CreatedSellertruefalsefalsenull2024-02-22T09:54:07+11:00
Obtain Adverts to be VettedOperatortruefalsefalsenull2024-02-22T09:54:07+11:00
Newly Created Advert is ApprovedOperatorfalsetruefalsenullnull

This is the simplest of all possible flows where the Advert is approved on the first occasion.

Note that we will cover API calls and Webhooks that can be used to perform these steps in the section below.

Scenario 2 - Initial Rejection followed by Approval

StepParticipantrequiresVettingvettedvettingRejectedvettingRejectedReasonrequiresVettingAt
Advert Newly CreatedSellertruefalsefalsenull2024-02-22T10:04:45+11:00
Obtain Adverts to be VettedOperatortruefalsefalsenull2024-02-22T10:04:45+11:00
Newly Created Advert is RejectedOperatorfalsefalsetruesomething...null
Obtain Rejected AdvertsSellerfalsefalsetruesomething...null
Fix Rejected Advert & resubmitSellertruefalsefalsesomething...2024-02-22T10:09:11+11:00
Previously Rejected Advert ApprovedOperatorfalsetruefalsenullnull

These scenarios can be described in the following workflow, (integration touch-points are identified by the in-circle letters and will be discussed in the next section).

Complex Advert Vetting Flow

APIs & Webhooks

While the Vetting Process can be undertaken entirely using the Seller and Operator Portals, in this section we turn our attention to the APIs and Webhooks that could be employed when undertaking the Advert Vetting Flow.

The Integration Points (IPs) which were drawn as in-circle letters on the last diagram are detailed below, along with some call outs on the process.

IPActionParticipantAPIWebhooksNotes
AAdvert CreationSeller- Seller API: advertUpsert
- Legacy Seller API: Advert Endpoint
N/aN/a
BRetrieval of Adverts to VetOperator- Operator API: advertsWhere
- Operator API: allAdverts
- Advert
- Variant
You should be looking for requiresVetting = true
COperator Vets ProductsOperator- Operator API: advertVettingApprove
- Operator API: advertVettingReject
N/aIn our worked example we are only working with 1 advert however you can use advertVettingApprove and advertVettingReject to work with multiple adverts.
DRetrieval of Rejected AdvertsSeller- Seller API: advertsWhere
- Seller API: allAdverts
- Legacy Seller API: Advert Endpoint
N/aYou should be looking for vettingRejected = true
ERectify Rejected CriteriaSeller- Seller API: advertUpsert
- Legacy Seller API: Advert Endpoint
N/aRectify the reasons for rejection.
 ResubmitSeller- Seller API: advertVettingResubmit
- Legacy Seller API: Advert Endpoint
N/aDedicated resubmit mutation required in addition to making advert changes

Worked Example

Referring back to Scenario 2 (above), we’ll now step through a worked example using the Operator and Seller APIs.


1. Seller Creates Advert(s) (Seller API)

The full advert creation process is described here, please refer to this for more detail. Below is an example advertUpsert mutation that a Seller would use to create a Product (Advert).

mutation {
	advertUpsert(
		input: {
			attributes: {
				attemptAutoPublish: true
				brandId: "QnJhbmQtMjQ1"
				taxonId: "VGF4b24tMjk3Mg=="
				title: "T-Shirt Red"
				description: "This is a great T-Shirt"
				price: "9.99"
				images: [
					{
						filename: "Red T-Shirt"
						sourceUrl: "https://www.someurl.com/redtshirt.jpg"
					}
				]
				variants: [
					{
						countOnHand: 1000
						optionValueIds: [
							"T3B0aW9uVmFsdWUtMTQ1Nzc="
							"T3B0aW9uVmFsdWUtMTQ1ODA="
						]
					}
				]
			}
		}
	) {
		advert {
			id
			legacyId
			requiresVetting
			requiresVettingAt
			vetted
			vettingRejected
			vettingRejectedReason
		}
		errors {
			field
			messages
		}
	}
}

Running this mutation will create a simple product, you can see the values of the 5 vetting fields that we’ve asked for in the mutation return response below:

{
	"data": {
		"advertUpsert": {
			"advert": {
				"id": "QWR2ZXJ0LTEwMDAwMjU3OA==",
				"legacyId": 100002578,
				"requiresVetting": true,
				"requiresVettingAt": "2024-02-22T09:54:07+11:00",
				"vetted": false,
				"vettingRejected": false,
				"vettingRejectedReason": null
			},
			"errors": null
		}
	}
}

2. Operator Retrieves Adverts to be Vetted (Operator API)

In the example below we are using the advertsWhere query to return all Adverts that require vetting for a individual Seller / Retailer:

query {
	advertsWhere(
		retailerIds: ["U2VsbGVyLTE2NQ=="], 
		requiresVetting: true) {
		nodes {
			__typename
			id
			legacyId
			vettingRejected
			requiresVetting
			requiresVettingAt
			vetted
			vettingRejectedReason
		}
	}
}

In this case, this Seller has 1 Advert that requires vetting, (the Advert we just created):

{
	"data": {
		"advertsWhere": {
			"nodes": [
				{
					"__typename": "Advert",
					"id": "QWR2ZXJ0LTEwMDAwMjU3OA==",
					"legacyId": 100002578,
					"vettingRejected": false,
					"requiresVetting": true,
					"requiresVettingAt": "2024-02-22T09:54:07+11:00",
					"vetted": false,
					"vettingRejectedReason": null
				}
			]
		}
	}
}

3. Operator Rejects this Advert (Operator API)

The Operator can use advertVettingReject to reject this Advert as follows:

mutation {
	advertVettingReject(
		input: {
			advertIds: ["QWR2ZXJ0LTEwMDAwMjU3OA=="]
			vettingRejectedReason: "The description is not long enough."
		}
	) {
		adverts {
			nodes {
				id
				legacyId
				requiresVetting
				requiresVettingAt
				vetted
				vettingRejected
				vettingRejectedReason
			}
		}
		errors {
			messages
			field
		}
	}
}

You can see the changes to the vetted fields as part of the mutation response:

{
	"data": {
		"advertVettingReject": {
			"adverts": {
				"nodes": [
					{
						"id": "QWR2ZXJ0LTEwMDAwMjU4Nw==",
						"legacyId": 100002587,
						"requiresVetting": false,
						"requiresVettingAt": null,
						"vetted": false,
						"vettingRejected": true,
						"vettingRejectedReason": "The description is not long enough."
					}
				]
			},
			"errors": null
		}
	}
}

4. Seller queries for rejected Adverts (Seller API)

The Seller can use the advertsWhere query to return all the adverts that have been rejected:

query {
	advertsWhere(
		retailerIds: ["U2VsbGVyLTE2NQ=="], 
		rejectedViaVetting: true) {
		nodes {
			__typename
			id
			legacyId
			vettingRejected
			requiresVetting
			requiresVettingAt
			vetted
			vettingRejectedReason
		}
	}
}

The response from this query will return the Advert that was just rejected:

{
	"data": {
		"advertsWhere": {
			"nodes": [
				{
					"__typename": "Advert",
					"id": "QWR2ZXJ0LTEwMDAwMjU3OA==",
					"legacyId": 100002578,
					"vettingRejected": true,
					"requiresVetting": false,
					"requiresVettingAt": null,
					"vetted": false,
					"vettingRejectedReason": "The description is not long enough."
				}
			]
		}
	}
}

5. Seller Rectifies the rejected advert (Seller API)

The Seller can update the Advert as follows using adverUpsert:

mutation {
	advertUpsert(
		input: {
			advertId: "QWR2ZXJ0LTEwMDAwMjU3OA=="
			attributes: {
				description: "This is a fine cotton T-Shirt available in a range of sizes and colors"
			}
		}
	) {
		advert {
			id
			legacyId
			requiresVetting
			requiresVettingAt
			vetted
			vettingRejected
			vettingRejectedReason
		}
		errors {
			field
			messages
		}
	}
}

The response from this mutation can be seen below:

{
	"data": {
		"advertUpsert": {
			"advert": {
				"id": "QWR2ZXJ0LTEwMDAwMjU3OA==",
				"legacyId": 100002578,
				"requiresVetting": true,
				"requiresVettingAt": "2024-02-22T10:57:07+11:00",
				"vetted": false,
				"vettingRejected": false,
				"vettingRejectedReason": "The description is not long enough."
			},
			"errors": null
		}
	}
}

6. Seller Resubmits the Advert (Seller API)

mutation {
	advertVettingResubmit(input: 
		{ 
			advertIds: ["QWR2ZXJ0LTEwMDAwMjU3OA=="]
		}) {
		adverts {
			nodes {
				id
				vetted
				vettingRejected
				vettingRejectedReason
				requiresVetting
				requiresVettingAt
			}
		}
	}
}

Note: that you will be able to supply multiple advert Ids with the advertVettingResubmit mutation

The response from this mutation can be seen below:

{
  "data": {
    "advertVettingResubmit": {
      "adverts": {
        "nodes": [
          {
            "id": "QWR2ZXJ0LTEwMDAwMjU3OA==",
            "vetted": false,
            "vettingRejected": false,
            "vettingRejectedReason": "The description is not long enough.",
            "requiresVetting": true,
			"requiresVettingAt": "2024-02-22T10:57:07+11:00"
          }
        ]
      }
    }
  }
}

7. Operator Retrieves Adverts to be Vetted (Operator API)

This is essentially a repeat of Step #2

query {
	advertsWhere(
		retailerIds: ["U2VsbGVyLTE2NQ=="], 
		requiresVetting: true) {
		nodes {
			__typename
			id
			legacyId
			vettingRejected
			requiresVetting
			requiresVettingAt
			vetted
			vettingRejectedReason
		}
	}
}

Here we can see the previously rejected Advert:

{
	"data": {
		"advertsWhere": {
			"nodes": [
				{
					"__typename": "Advert",
					"id": "QWR2ZXJ0LTEwMDAwMjU3OA==",
					"legacyId": 100002578,
					"vettingRejected": false,
					"requiresVetting": true,
					"requiresVettingAt": "2024-02-22T10:57:07+11:00",
					"vetted": false,
					"vettingRejectedReason": "The description is not long enough."
				}
			]
		}
	}
}

8. Operator Approves the Advert (Operator API)

Finally, the operator can use advertVettingApprove to approve the Advert

mutation {
	advertVettingApprove(input: 
		{ advertIds: ["QWR2ZXJ0LTEwMDAwMjU4OA=="] }) {
		adverts {
			nodes {
				id
				legacyId
				requiresVetting
				requiresVettingAt
				vetted
				vettingRejected
				vettingRejectedReason
			}
		}
		errors {
			field
			messages
		}
	}
}

The results of this mutation can be seen below where the Advert is approved:

{
	"data": {
		"advertVettingApprove": {
			"adverts": {
				"nodes": [
					{
						"id": "QWR2ZXJ0LTEwMDAwMjU4OA==",
						"legacyId": 100002588,
						"requiresVetting": false,
						"requiresVettingAt": null,
						"vetted": true,
						"vettingRejected": false,
						"vettingRejectedReason": null
					}
				]
			},
			"errors": null
		}
	}
}