Safeguard listing check

Score a listing from 0 to 10 the way a careful buyer would, see exactly what to fix, and ask the seller for what is missing.

Safeguard reads a listing the way a careful buyer would before spending their own money, scores it from 0 to 10 for how ready a buyer could be to buy it exactly as it stands, and tells the seller what to fix.

What a result means, and what it does not

  • A high score means the listing is complete and consistent: a buyer is told what the item is, what condition it is in and what is included. It does not mean the item is genuine.
  • Safeguard can sometimes miss a problem, including a difference between a photo and the description. It supports a human decision rather than replacing it.
  • Safeguard is not fraud prevention. It cannot inspect, test or authenticate an item, it cannot tell whether a seller is honest, and a complete listing of a fake can score well. It does flag a claim that the photos or the listing contradict, a price far from what the item sells for, and signs of a scam.
  • The score is the AI's own judgement against a fixed scale (shown below). PCIFIC checks that the answer is complete and real, but never changes the score. The same listing can score slightly differently if you check it more than once. In our testing the difference is normally within 1 point, and we are actively working to make scores more consistent. Visual findings and wording can still vary even when scores match; check the evidence itself.
  • passed and the verdict are your shop's decision made visible. Safeguard never publishes, blocks, buys, bids or messages anyone.

What you can use it for

Real uses, by kind of business
Kind of businessWhat you sendWhat you do with the result
Shops, before publishThe seller's draft listing, in your fields, and its photos.Show the score and the fixes on the publish screen. Publish when the verdict means pass, hold the rest, and let the seller improve the listing in place.
Insurance claims (listings and evidence descriptions)The written description of a claimed item or its evidence, in a category with fields such as make, model, serial number, date bought, value and what happened, plus the photos.Tell the claimant what is missing before they submit, and sort claims for the handler by verdict. Safeguard checks the description is complete and agrees with itself and the photos. It does not decide whether a claim is true.
Rentals and holiday letsA host's listing: rooms, amenities, house rules, deposit, cancellation, area.Ask the host for the facts a guest would want in one set of questions, and put the FAQ on the listing page.
Car dealersAn advert: make, model, year, mileage, service history, previous owners, faults.Flag missing service history or mileage before the advert goes live, and offer a clearer title in your house style.
Resale and second hand (phones, clothes, bags)A seller's listing with size, condition, flaws and what is included, plus photos.Ask the seller for the missing size or battery health. Safeguard does not authenticate an item: a complete listing of a fake still scores well.
Classifieds and imported feedsListings that arrive from a partner feed or a bulk upload, with no seller present.Review each one in the background and send the low verdicts to a person. Seller questions need a seller, so ignore them for feeds.
Tickets and servicesDate, venue, seat or area, what is included, refund terms. Set requirePhotos to false for a text-only review.Check the details a buyer needs are stated before the offer goes live.

Safeguard also gives the buyer side something to show: questions a careful buyer would still ask (questions), a buyer FAQ written from the listing (faq), and suggested new wording (rewrite).

Before you start

  • A key with the safeguard permission. Use a test key while you build: it runs the real review and spends no credits, and each review, set of answers or retry counts as one AI request towards the shop's daily cap, shared by its test keys. PCIFIC's ceiling always applies, 200 per shop per UTC day by default, or your lower saved cap. Live keys spend credits after you choose Live key and press Activate live key on the API keys page. See API keys and permissions.
  • Published listing fields (a schema, the files that describe your listing's fields) for the category you review. Safeguard reads each field's description, so write a clear one. Without a published version a review is refused with SAFEGUARD_NO_SCHEMA. See Set up your listing fields.
  • Safeguard switched on. It is on by default. Starting a review, sending answers and retrying are all refused with SAFEGUARD_TURNED_OFF while it is off.
  • Only if you send photos: Orbit switched on too. The photo upload belongs to Orbit, so it follows Orbit's switch. A key with the orbit or the safeguard permission can upload.
  • Optional: your own minimum score, banned items and verdict names. Change them on the dashboard, or with PUT /api/v1/safeguard/settings and a test key that has the settings permission. A live key can read the settings but never change them. See Shop configuration.

How a review works

How a review runs
  1. Your server starts a reviewPOST /api/v1/safeguard/reviews with the listing, its category and any photo ids. A wrong request is refused at once with an error that says how to fix it, and no review is started.
  2. Safeguard answers 202 at onceThe answer holds a reviewId and the status working. Keep the reviewId. Nothing is waited for, so your seller's page never hangs.
  3. The judge works in the backgroundOne AI judge reads your fields, the listing, the photos and what the seller said, and may search the web up to twice. PCIFIC checks its answer and sends it back to fix up to 3 times. Most reviews take 5 to 60 seconds, and none more than 180.
  4. You read the review backGET /api/v1/safeguard/reviews/{reviewId} every few seconds (polling, asking again on a timer) until status is done or failed.
  5. You show the resultShow the score, the verdict name, the summary and the fixes. Decide what pass, review and block mean on your site.
  6. Optional: the seller answersShow every question in sellerQuestions together, send all the answers at once, and read the same review again. The listing comes back updated with a new score.

A review is kept for 30 minutes after its last change (started, finished, answered or retried), then deleted. Save the score and the updated listing on your side before then. Safeguard never saves your listing for you.

Quickstart

These steps assume a test key in $PCIFIC_TEST_KEY and a published Phones category like the one in Set up your listing fields. The price is a money field: amount is in minor units (pence or cents, so 31500 with GBP is £315) and currency is your shop's currency.

Step 1: read your settings (optional)

Read the settings
curl https://business.pcific.ai/api/v1/safeguard/settings \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY"
The answer on a new account
{
  "enabled": true,
  "minimumScore": 7,
  "webSearch": true,
  "bannedItems": [],
  "instructions": null,
  "orbitDefault": false,
  "verdicts": [
    {
      "name": "High risk",
      "from": 0,
      "meaning": "block"
    },
    {
      "name": "Caution",
      "from": 4,
      "meaning": "review"
    },
    {
      "name": "Looks good",
      "from": 7,
      "meaning": "pass"
    }
  ],
  "categoryVerdictStarts": {}
}

Step 2: upload photos

At least one photo is required by default on POST /api/v1/safeguard/reviews, including when the listing comes from orbitRunId. A request with no photos is refused before work with PHOTOS_REQUIRED (422), with no charge. Strongly recommended: keep photos on. Set requirePhotos to false only when you choose a text-only request, for example for a service. Without photos results are less reliable and can vary more. Send each photo as a file in a multipart form (a form that carries files). You get a photo id for each, which lives for 30 minutes. Orbit's page has the full rules for photos.

Upload a photo
curl -X POST https://business.pcific.ai/api/v1/images \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -F "photo=@front.jpg"
The answer
{
  "images": [
    {
      "id": "3f1c2a9e-5b7d-4c1e-9a52-7d0c6e8b1f34",
      "width": 3024,
      "height": 4032,
      "preview": "https://files.example/preview.webp",
      "listing": {
        "url": "https://files.example/listing.webp",
        "format": "webp"
      },
      "expiresAt": "2026-10-19T10:30:00Z",
      "fileName": "front.jpg"
    }
  ]
}

Step 3: start the review

Start a review
curl -X POST https://business.pcific.ai/api/v1/safeguard/reviews \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"category":"Phones","listing":{"title":"iPhone 13 128GB, good condition, works fine","description":"Selling my old phone. No box. Collection preferred.","brand":"Apple","model":"iPhone 13","storage_gb":128,"condition":"Good","price":{"amount":31500,"currency":"GBP"}},"images":["3f1c2a9e-5b7d-4c1e-9a52-7d0c6e8b1f34"],"requirePhotos":true,"review":true,"faq":false}'

Before work starts, Safeguard refuses wrong field types, values outside fixed options and unreadable prices with 400 INVALID_LISTING. problems has one entry per affected field, with path, problem and tryThis. No credits are spent. Missing details, including required fields, and unusual values that have the right type and are allowed still go to the AI judge. A price amount containing a decimal point or exponent, including 749.0, is refused. Send whole minor units, like 4500 pence for £45. Any price that cannot be read in a result is explained in pcific.priceProblems with path, problem and tryThis.

The answer (HTTP 202)
{
  "status": "working",
  "reviewId": "1f9380fa-a135-4b3d-ab70-1dc1fc50f065",
  "tryThis": "Read the review with GET /api/v1/safeguard/reviews/1f9380fa-a135-4b3d-ab70-1dc1fc50f065 in a few seconds.",
  "warnings": []
}

The addresses inside tryThis and retry already use the public /api/v1/ prefix. Call them as returned.

Step 4: read the review until it is done

Read the review
curl https://business.pcific.ai/api/v1/safeguard/reviews/1f9380fa-a135-4b3d-ab70-1dc1fc50f065 \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY"
While it works
{
  "status": "working",
  "reviewId": "1f9380fa-a135-4b3d-ab70-1dc1fc50f065",
  "tryThis": "Read this again in a few seconds."
}
When it is done
{
  "status": "done",
  "reviewId": "1f9380fa-a135-4b3d-ab70-1dc1fc50f065",
  "score": 6,
  "passed": false,
  "minimumScore": 7,
  "verdict": {
    "name": "Caution",
    "meaning": "review"
  },
  "banned": {
    "isBanned": false,
    "reason": null
  },
  "review": {
    "summary": "Clear model and storage, but the battery health and lock status are missing.",
    "fixes": [
      {
        "field": "/battery_health",
        "importance": "important",
        "problem": "Battery health is not stated.",
        "fix": "Add the battery health percentage from Settings."
      },
      {
        "field": "/description",
        "importance": "important",
        "problem": "It does not say whether the phone is unlocked.",
        "fix": "Say whether the phone is unlocked or tied to a network."
      }
    ]
  },
  "questions": [
    "Has the screen or battery ever been replaced?"
  ],
  "sellerQuestions": [
    {
      "id": "q1",
      "question": "What is the battery health percentage?",
      "field": "/battery_health"
    },
    {
      "id": "q2",
      "question": "Is the phone unlocked or tied to a network?",
      "field": "/description"
    }
  ],
  "listing": {
    "title": "iPhone 13 128GB, good condition, works fine",
    "description": "Selling my old phone. No box. Collection preferred.",
    "brand": "Apple",
    "model": "iPhone 13",
    "storage_gb": 128,
    "condition": "Good",
    "price": {
      "amount": 31500,
      "currency": "GBP"
    }
  },
  "pcific": {
    "release": "2.0.0",
    "category": "Phones",
    "schemaVersion": 3,
    "prices": {
      "/price": {
        "amount": 31500,
        "currency": "GBP",
        "display": "£315.00"
      }
    },
    "warnings": []
  },
  "expiresAt": "2026-10-19T10:30:00Z"
}

Here the phone scores 6 because two important facts are missing, so passed is false against a minimum of 7 and the verdict is Caution, which means review. Show the fixes to the seller and ask the questions in sellerQuestions.

A fix carries field only when the seller would change that field's value. Photo fixes and fixes about the listing as a whole are general suggestions with no field, for example "Add a clear photo of the back." Show them under the photos or as a note on the listing. A fix that needs uploaded or replacement photos carries target: photos. Use that target to open your photo uploader; do not write the photo request into /description. Photo seller questions can carry the same target with no field. Upload the requested photos, then start a new review using the returned image ids.

Step 5: send the seller's answers (optional)

Send the answers
curl -X POST https://business.pcific.ai/api/v1/safeguard/reviews/1f9380fa-a135-4b3d-ab70-1dc1fc50f065/answers \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"answers":[{"id":"q1","answer":"91%"},{"id":"q2","answer":"It is unlocked, works on any network"}]}'
The answer (HTTP 202, the same reviewId)
{
  "status": "working",
  "reviewId": "1f9380fa-a135-4b3d-ab70-1dc1fc50f065",
  "tryThis": "Read the review with GET /api/v1/safeguard/reviews/1f9380fa-a135-4b3d-ab70-1dc1fc50f065 in a few seconds.",
  "warnings": []
}

Read the review again as in step 4. When it is done it holds the updated listing and a new score. The seller's answers changed two fields, and changes shows the seller's exact words for each:

The review after the answers
{
  "status": "done",
  "reviewId": "1f9380fa-a135-4b3d-ab70-1dc1fc50f065",
  "score": 9,
  "passed": true,
  "minimumScore": 7,
  "verdict": {
    "name": "Looks good",
    "meaning": "pass"
  },
  "banned": {
    "isBanned": false,
    "reason": null
  },
  "review": {
    "summary": "Clear model, storage, battery health and lock status. Only the charger is not mentioned.",
    "fixes": [
      {
        "importance": "minor",
        "problem": "It does not say whether a charger is included.",
        "fix": "Say whether the charger is included."
      }
    ]
  },
  "questions": [],
  "sellerQuestions": [],
  "changes": [
    {
      "field": "/battery_health",
      "before": null,
      "after": 91,
      "questionId": "q1",
      "quote": "91%"
    },
    {
      "field": "/description",
      "before": "Selling my old phone. No box. Collection preferred.",
      "after": "Selling my old phone. Unlocked, works on any network. No box. Collection preferred.",
      "questionId": "q2",
      "quote": "It is unlocked, works on any network"
    }
  ],
  "listing": {
    "title": "iPhone 13 128GB, good condition, works fine",
    "description": "Selling my old phone. Unlocked, works on any network. No box. Collection preferred.",
    "brand": "Apple",
    "model": "iPhone 13",
    "storage_gb": 128,
    "condition": "Good",
    "battery_health": 91,
    "price": {
      "amount": 31500,
      "currency": "GBP"
    }
  },
  "pcific": {
    "release": "2.0.0",
    "category": "Phones",
    "schemaVersion": 1,
    "prices": {
      "/price": {
        "amount": 31500,
        "currency": "GBP",
        "display": "£315.00"
      }
    },
    "warnings": []
  },
  "expiresAt": "2026-10-19T10:30:00Z"
}

Request fields

Send a JSON object (text in curly brackets) with the header Content-Type: application/json. Keys Safeguard does not know are ignored, and each is named in warnings so a typo does not pass silently. Your listing keeps your own field names. Safeguard asks for no field in particular, not even a title or description, and judges against the fields of the category you name.

Request body for POST /api/v1/safeguard/reviews
FieldTypeRequiredWhat it is
categorystringNoThe listing's category, named as in the latest published version of your listing fields. Required, unless you send orbitRunId. null is the same as leaving it out.
listingobjectNoCustomer-defined: the listing in your own field names, exactly as you send them. null or left out only with orbitRunId, which supplies the listing. Up to 50,000 characters once written as JSON. Required, unless you send orbitRunId. Photo and listing-field problems are collected together. A value of the wrong type, or one outside a field's allowed options, is refused with a 400 error that names each field in problems. Required fields are judged, not validated. A missing required field is accepted (202); the judge flags it and can ask the seller. Safeguard asks for no field in particular: not even a title or a description.
listing.priceobjectNoA price field (one marked x-pcific-money in your schema; this one is called price) is {amount, currency}. Leave it out when the listing has no price.
listing.price.amountintegerNoA whole number in minor units (pence or cents, so 31500 with GBP is £315). Decimal points and exponents, including 749.0, are refused; send 4500 pence for £45. Text amounts are refused too. Any other amount (fractional, negative, missing or out of range) is refused with a 400 error that names it in problems.
listing.price.currencystringNoThe currency code. Prices are never converted, so it must be your shop's currency (the currency setting in the Orbit settings).
imagesstring[]NoUp to 12 photo ids from POST /api/v1/images. At least one is required by default, including when using orbitRunId. A photo is kept for 30 minutes after upload. The same id twice counts once. null is the same as leaving it out.
requirePhotosbooleanNoDefault true on POST /api/v1/safeguard/reviews, also when null. With no photos the request is refused before work, with no charge. Strongly recommended: keep photos on. Set false to allow a text-only request, for example for a service. Without photos results are less reliable and can vary more.
sellerNotesstringNoWhat the seller wrote or said about the item, up to 5,000 characters. It counts as the seller's own words. null is the same as leaving it out.
orbitRunIdstringNonull is the same as leaving it out. The runId of a finished Orbit listing less than 30 minutes old. Its listing, category, photos and the seller's words are used. listing, category and images, when sent too, replace Orbit's. sellerNotes is added to what the seller told Orbit.
reviewbooleanNotrue (the default, also when null) scores the listing and returns the fixes and questions. false leaves them out, so send faq or rewrite with it.
faqbooleanNotrue adds 3 to 6 buyer questions and answers. Default false, also when null. The review keeps web search when enabled; FAQ answers never use web pages.
rewriteboolean | string[]Notrue suggests new wording for /title and /description. Instead of true you can send a list of 1 to 5 field paths, each once and none inside a list, like ["/title"]. Default false.
modelTierstringNostandard (the default) or ultra-fast. Ultra Fast answers in about half a second instead of several, and a live use spends more credits: the Credits page in your dashboard shows how many. A use is charged as Ultra Fast only when Ultra Fast answered the whole request; otherwise Standard answers and it is charged as Standard. A business on its own AI key is answered by its own model. null is the same as leaving it out.

What INVALID_FIELD means here

INVALID_FIELD (400) means a field has the wrong type or size. The error names it, and problems lists each place with a next step.

Rules behind INVALID_FIELD
FieldWhy it is refused
listingMore than 50,000 characters as JSON, or not a non-empty object (that one is LISTING_REQUIRED).
categoryNot text.
imagesNot a list of photo ids (UUIDs, the ids POST /api/v1/images answers with), or more than 12.
sellerNotesNot text, or longer than 5,000 characters.
orbitRunIdNot the runId of an Orbit listing.
review, faq, requirePhotosNot true or false.
rewriteNot true, false or a list of 1 to 5 field paths, or a path named twice, or a field inside a list (a list's entries each have their own value, so there is no one value to rewrite).
answersNot a non-empty list of {id, answer}, an answer that is not text or is over 1,000 characters, or the same question answered twice in one request.

Numbers, true or false, and fixed options in your listing

Photo problems and listing-field problems are collected together in problems. A photo error keeps its 422 code; otherwise listing errors use 400 INVALID_LISTING. Required fields are judged, not validated. A missing required field is accepted (202); the judge flags the gap and can ask the seller. Each value supplied in listing must have the type your listing fields set. Text where a number belongs ("ninety"), a word where true or false belongs ("yes"), or a choice that is not one of the allowed options is refused with a 400 error, before any review starts. Nothing is charged. problems names each field, what is wrong with it and what to send instead. Missing details and unusual values of the right type that satisfy any fixed options are not refused: Safeguard mentions them in the review.

A price amount is a whole number in the smallest unit of the currency. Decimal points and exponents, including 749.0, are refused; send 4500 pence for £45. Text amounts are refused too. Any other amount (fractional, negative, missing or out of range) is refused in the same way, and problems names the price.

Response fields

Starting, answering and retrying all answer 202 with the same four fields. Fields not listed may be added at any time; ignore what you do not use.

The 202 answer (start, answers and retry)
FieldTypeRequiredWhat it is
statusstringYesAlways working.
reviewIdstringYesThe review. Read it with GET /api/v1/safeguard/reviews/{reviewId}.
tryThisstringYesWhat to do next: read the review again in a few seconds.
warningsstring[]YesOne sentence for each key in your request that Safeguard does not know and ignored. Empty when there are none.

GET /api/v1/safeguard/reviews/{reviewId} answers 200 with one of three shapes, chosen by status.

While status is working
FieldTypeRequiredWhat it is
statusstringYesworking.
reviewIdstringYesThe review.
tryThisstringYesRead the review again in a few seconds.
When status is done
FieldTypeRequiredWhat it is
statusstringYesdone.
reviewIdstringYesThe review.
modelTierobjectNoWhen the review is done. Which tier answered: {requested, handledBy, message}.
modelTier.requestedstringYesstandard or ultra-fast, as the request asked.
modelTier.handledBystringYesThe tier that answered the whole request: standard, ultra-fast, or byo when your own AI key answered. A use is charged as Ultra Fast only when this is ultra-fast.
modelTier.messagestringYesWhy handledBy differs from requested, else null.
scoreintegerNo0 to 10, how ready a buyer could be to buy it as it stands. Checking the same listing again can give a slightly different score, normally within 1 point. Only when review is on.
passedbooleanNotrue when score reached minimumScore, the item is not banned, and no fix is important or warning. A listing with an important or warning fix never passes, even at or above minimumScore. Only when review is on.
minimumScoreintegerNoYour minimumScore setting when the review ran, 0 to 10. Only when review is on.
verdictobjectNoYour own name for where the score falls. When passed is false because of an important or warning fix, it is your best verdict that is not a pass and that the score reaches. Only when review is on.
verdict.namestringNoThe verdict's name, written by you in your settings. The standard names are High risk, Caution and Looks good. Show this to people.
verdict.meaningstringNoblock, review or pass. Branch your code on this, never on the name.
bannedobjectNoWhen review or rewrite is on. The AI judge flags banned items and scams from evidence in the listing.
banned.isBannedbooleanNotrue when the evidence shows the item is on your banned list, or is plainly illegal or a scam. The score is then 0.
banned.kindstringNobanned_item when the item is on your banned list or illegal to sell; scam when the listing shows clear deception. Only present when flagged.
banned.reasonstringNoRequired plain reason pointing to evidence in the listing when flagged. null when isBanned is false.
reviewobjectNoOnly when review is on.
review.summarystringNoOne or two plain sentences saying what the score is for, up to 600 characters.
review.fixesobject[]NoUp to 10, each {field, target, importance, problem, fix}. field is the path of the field whose value the seller would change, like /battery_health. Photo fixes and fixes about the listing as a whole are general suggestions with no field. Show them under the photos or as a note on the listing. A fix needing uploaded or replacement photos carries target: photos, with no field. importance is important (a buyer would not pay without it), minor (a buyer would still buy) or warning (a warning sign, which makes the score 3). Empty at a score of 10.
questionsstring[]NoUp to 6 things a careful buyer would still ask the seller. Can be empty.
sellerQuestionsobject[]NoUp to 6 questions only the seller can answer, each {id, question, field?, target?, options?}. field is the path the answer fills. Photo requests can carry target: photos with no field, rather than /description. Use the photo uploader and start a new review with its returned image ids. options is that field's list of allowed values when it has a short list. Send the answers with POST .../answers.
sourcesobject[]NoUp to 6 web pages the judge relied on, each {url, title, finding}. Only when it used some.
faqobject[]NoOnly with faq on. 3 to 6 entries {question, answer, needsSeller}. A question the listing does not answer has answer null and needsSeller true: ask the seller.
rewriteobject[]NoOnly with rewrite on. One {field, before, after} for each field asked for, or [] when flagged. The listing is not changed.
rewriteSkippedReasonstringNoPresent when a flag prevents requested rewrites. Explains why they were skipped, including the flag reason.
changesobject[]NoAfter answers: {field, before, after, questionId, quote}. Any relevant published field can change, including title and description. supportingAnswers: [{questionId, quote}] names other contributing answers, including earlier ones. Unchanged values are omitted. You choose what to accept.
listingobjectNoThe listing as it now stands, with the seller's answers in it. Safeguard never saves it: you decide whether to.
pcificobjectNoFacts about this review.
pcific.releasestringNoThe Safeguard release that ran, 2.0.0.
pcific.categorystringNoThe category as named in your schema.
pcific.schemaVersionintegerNoThe version of your listing fields the review used.
pcific.modelstringNoThe id of the AI model that wrote the review, or the FAQ when only a FAQ was asked for. Empty text when no model ran.
pcific.priceProblemsobject[]NoAny price that could not be read in the result, with path, problem and tryThis. Invalid submitted prices are refused before work starts.
pcific.priceProblems.pathstringYesWhere the price is in the listing, like /price.
pcific.priceProblems.problemstringYesWhat is wrong with how it is written.
pcific.priceProblems.tryThisstringYesHow to write it instead.
pcific.pricesobjectNoEach price Safeguard read in the listing, by field path, as {amount, currency, display}. Only whole numbers written without decimal points or exponents are accepted. Any unreadable result price is explained in pcific.priceProblems.
pcific.prices.{field}objectYesOne price, keyed by its field path like /price.
pcific.prices.{field}.amountintegerYesA whole number in the smallest unit.
pcific.prices.{field}.currencystringYesYour shop's currency code.
pcific.prices.{field}.displaystringYesThe price written for people, like £315.00.
pcific.warningsstring[]NoThe warnings from the request, kept with the review.
pcific.orbitRunIdstringNoOnly when the review is of an Orbit listing.
expiresAtstringNoWhen the review is deleted: 30 minutes after its last change.

With review set to false there is no score, passed, verdict, review, questions or sellerQuestions. A rewrite still includes banned so a flagged listing never receives polished wording. You also get listing and pcific.

When status is failed
FieldTypeRequiredWhat it is
statusstringYesfailed. This answer is HTTP 200: the failure is inside error.
reviewIdstringYesThe review.
errorobjectYes{error, code, tryThis}: what went wrong, a fixed code to branch on, and the next step.
retrystringYesThe call that tries the same review again, like POST /api/v1/safeguard/reviews/{reviewId}/retry. Call this address as returned.
A failed review (HTTP 200)
{
  "status": "failed",
  "reviewId": "1f9380fa-a135-4b3d-ab70-1dc1fc50f065",
  "error": {
    "error": "Safeguard took over 180 seconds on this review.",
    "code": "SAFEGUARD_TIMED_OUT",
    "tryThis": "Try it again with POST /api/v1/safeguard/reviews/1f9380fa-a135-4b3d-ab70-1dc1fc50f065/retry. If it keeps happening, contact PCIFIC support with the reviewId."
  },
  "retry": "POST /api/v1/safeguard/reviews/1f9380fa-a135-4b3d-ab70-1dc1fc50f065/retry"
}

How the score is chosen

The judge goes through this scale in order and stops at the first line that fits. An important gap is something a careful buyer would not pay without knowing. A minor gap is something they would like to know but would still buy without. Every fix in review.fixes is one gap or warning sign, marked important, minor or warning, and the score must match them.

The scale
ScoreWhen
0The item is on your banned list, or the listing is plainly a scam or illegal. Only on evidence in the listing, the photos or the seller's words.
1You cannot tell what is for sale at all.
2You can tell the kind of item but not which one, like "iPhone" with no model.
3A real warning sign: a claim the photos or the listing itself contradict, a price far from what this item sells for with nothing to explain it, or payment terms that look like a scam.
4It is clear what the item is, but almost nothing else is said.
5Three or more important gaps.
6Two important gaps.
7One important gap.
8No important gaps, three or more minor ones.
9No important gaps, one or two minor ones.
10Nothing missing and nothing wrong.
  • If you opt out with requirePhotos: false, the judge treats missing photos of a physical item a buyer would want to see as an important gap. Tickets, services and bookings can use this opt-out when there is nothing to photograph.
  • A required field that is empty is always a gap. A clear statement from the seller fills a gap.
  • Things no listing can give are never gaps: independent authentication the seller does not claim, a valuation, the buyer's own inspection, a price comparison. They appear in questions for the buyer to ask.
  • Prices are read in your shop's currency and are never converted. A price in another currency is an important gap. A price far from what the item sells for, with nothing to explain it, is a warning sign.
  • A banned item always scores 0.

Your minimum score and your verdicts

Two settings decide how a score is shown. They are set once for your shop, not on each request. The full settings table is on Shop configuration.

  • minimumScore (default 7) is your pass line. passed is true when the score reaches it, the item is not banned, and no fix is marked important or warning. Minor advice alone does not prevent a pass. On the scale above, 7 means one important gap is left and everything else is there.
  • When reporting an Orbit listing as published, a latest Safeguard score below minimumScore with open seller questions is refused with 409 REQUEST_REFUSED. Answer every open seller question, wait for the review to finish, then report the outcome again.
  • verdicts are your own names for ranges of the scale, lowest first, each meaning block, review or pass. The standard three are High risk from 0 (block), Caution from 4 (review) and Looks good from 7 (pass). You can set 2 to 5 of your own. Show verdict.name to people and branch your code on verdict.meaning.
  • categoryVerdictStarts lets one category start its ranges at other scores, like {"Art": [0, 6, 9]}, for items that need stricter or looser reading.
  • When a fix is marked important or warning, the verdict uses the highest non-pass range the score reaches, with your category starts and your own names. Verdicts never change the score or fixes. Otherwise they follow the score, while minimumScore remains a separate pass line. If you set minimumScore to 8 while Looks good starts at 7, a score of 7 shows Looks good with passed false. Pick one of them as your gate and use the other for wording.
  • Safeguard decides nothing for you. block, review and pass are hints for your own code, such as hold back, send to a person or publish.
  • Do not build logic that expects the exact same score to come back each time. Branch on verdict.meaning or passed, not on one exact score, and expect a listing that sits right on a line to land on either side of it now and then.
Safeguard settings
FieldTypeRequiredWhat it is
enabledbooleanNoSafeguard on or off. Default true.
minimumScoreintegerNoThe score a listing needs for passed, 0 to 10. Default 7.
webSearchbooleanNoWhether the AI may search the web (at most 2 searches a review). Default true.
bannedItemsstring[]NoUp to 50 plain descriptions, each 1 to 200 characters, of what may not be listed. Plainly illegal items and scams are always banned.
instructionsstringNoWhat your buyers care about, up to 2,000 characters. null clears it. It cannot change the scale.
voicestringNoOne of PCIFIC's six voices: standard, warm, upbeat, formal, concise or reassuring. null uses your shop's voice.
orbitDefaultbooleanNoReview every Orbit listing that does not say otherwise. Default false.
verdictsobject[]No2 to 5 verdicts, lowest first, each {name, from, meaning}. null puts back the standard three: High risk from 0 (block), Caution from 4 (review), Looks good from 7 (pass).
categoryVerdictStartsobjectNoA category's own start scores, one for each verdict, like {"Art": [0, 6, 9]}. Only categories in your latest published listing fields. {} or null for none.

Answering the seller's questions

When the judge finds a gap only the seller can fill, it adds a question to sellerQuestions with an id, the field the answer fills, and that field's list of allowed values (options) when it has a short one. Show all of them on one screen and send all the answers in one request.

How the seller's answers are used
  1. Show every question togetherA done review lists up to 6 sellerQuestions. Keep the seller's answers in the page until you send them.
  2. Send all the answers oncePOST /api/v1/safeguard/reviews/{reviewId}/answers with {"answers": [{"id": "q1", "answer": "91%"}]}. It answers 202 with the same reviewId.
  3. Safeguard reads the answersThe judge works new facts into the description in the listing's own voice and updates the title and other relevant published fields the answer settles. Every change includes the seller's exact words as proof. It reviews the updated listing without asking again for facts already answered.
  4. Read the review againThe same reviewId is working, then done. It has the new score, the updated listing, changes, and any questions still open.
  • Answers go in only while the review is done. Sending them while it works is REVIEW_NOT_READY.
  • Each answer is at most 1,000 characters. A blank answer is stored as "(no answer)". It counts as answered, so that question is closed.
  • A question you leave out of the request is stored as "(no answer)" too. This is why you send every answer in one request.
  • The seller can send answers 3 times for one review. A fourth is refused with TOO_MANY_ANSWER_ROUNDS: start a new review with the updated listing.
  • An answer can change several relevant fields, even when its question named no field. Fixed options use the same published field and option descriptions Orbit reads.
  • Every update is returned in changes, including description and title updates. Its questionId and quote name the new answer; supportingAnswers lists other contributing answer ids and quotes, including earlier answers. Unchanged values are left out. The seller or shop decides what to accept and save.
  • A price answer changes the whole price, as {amount, currency}, and only to the amount the seller said.

Retrying a failed review

What to do when a review fails
  1. Read the reviewGET returns status failed with HTTP 200. error holds error, code and tryThis. retry names the call that tries again.
  2. Show the seller a short messageKeep their listing in your page. A failure is rarely their fault.
  3. Retry the same reviewPOST /api/v1/safeguard/reviews/{reviewId}/retry. It uses the same listing and answers, answers 202, and the clock of 30 minutes starts again.
  4. Read the review againIt is working, then done or failed. If it fails again with the same code, contact PCIFIC support with the reviewId.

FAQ and rewrites

Add "faq": true to also get 3 to 6 questions a buyer would ask on the listing page, each answered only from the listing, the photos and the seller's words, never from the web or a guess. A question the listing does not answer comes back with answer null and needsSeller true, so you can ask the seller. The review keeps web search when enabled. A separate FAQ writer sees only the listing, the photos and the seller's words, so FAQ answers never use web pages.

Add "rewrite": true to get suggested new wording for /title and /description, written in the listing style you set in the Orbit settings and kept within each field's limits in your schema. Name the fields yourself with "rewrite": ["/title"] (1 to 5 field paths, each once, none inside a list). Every suggestion has before and after, and the listing itself is not changed. When the AI flags the listing, rewrite is [] and rewriteSkippedReason explains why, including the flag reason. banned.kind is banned_item for an item on your banned list or illegal to sell, or scam for clear deception. banned.reason is required and points to the evidence in plain words. Spelling, grammar, formatting and missing details alone are never scam evidence. Send "review": false with either to get only the FAQ or the rewrite.

Only a rewrite of the title
curl -X POST https://business.pcific.ai/api/v1/safeguard/reviews \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"category":"Phones","listing":{"title":"iPhone 13 128GB, good condition, works fine","description":"Selling my old phone. No box. Collection preferred.","brand":"Apple","model":"iPhone 13","storage_gb":128,"condition":"Good","price":{"amount":31500,"currency":"GBP"}},"requirePhotos":false,"review":false,"rewrite":["/title"]}'
What you read back when it is done
{
  "status": "done",
  "reviewId": "1f9380fa-a135-4b3d-ab70-1dc1fc50f065",
  "banned": {
    "isBanned": false,
    "reason": null
  },
  "rewrite": [
    {
      "field": "/title",
      "before": "iPhone 13 128GB, good condition, works fine",
      "after": "Apple iPhone 13 128GB, Good Condition"
    }
  ],
  "listing": {
    "title": "iPhone 13 128GB, good condition, works fine",
    "description": "Selling my old phone. No box. Collection preferred.",
    "brand": "Apple",
    "model": "iPhone 13",
    "storage_gb": 128,
    "condition": "Good",
    "price": {
      "amount": 31500,
      "currency": "GBP"
    }
  },
  "pcific": {
    "release": "2.0.0",
    "category": "Phones",
    "schemaVersion": 1,
    "prices": {
      "/price": {
        "amount": 31500,
        "currency": "GBP",
        "display": "£315.00"
      }
    },
    "warnings": []
  },
  "expiresAt": "2026-10-19T10:30:00Z"
}

Reviewing an Orbit listing

Orbit writes a listing from a seller's photos and notes. Safeguard can review it two ways, and in both it is told where Orbit got each value, so it can say plainly when Orbit added a fact nobody gave.

  • As Orbit writes it: send "safeguard": true (and "faq": true for a FAQ) with POST /api/v1/listings. The finished listing comes back with the review in pcific.safeguard, which has the same shape as a review here, with its own reviewId. Answer its questions or retry it with that reviewId. Setting orbitDefault to true reviews every Orbit listing that does not say otherwise. Asking for either while Safeguard is off is refused by Orbit with SAFEGUARD_OFF (409).
  • Later: send "orbitRunId" with the runId of the finished Orbit listing to POST /api/v1/safeguard/reviews. Its listing, category, photos and what the seller told Orbit come with it, so you need not send them. If you do send listing, category or images they replace Orbit's, and sellerNotes is added to what the seller told Orbit. An Orbit listing is deleted 30 minutes after its last change.
Review a finished Orbit listing
curl -X POST https://business.pcific.ai/api/v1/safeguard/reviews \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orbitRunId":"8c7a6f0e-2b4d-4e51-a9c3-5d1e7f2b9a40"}'

Errors

Every error is JSON with error (what went wrong), code (a fixed name your code can branch on), tryThis (the next step) and sometimes problems (each exact place at fault, as a path like /images/0 with its own next step). 4xx means fix the request. 5xx means a problem on PCIFIC's side: wait and try again.

Most errors also have a docs link to their own section on Safeguard errors, with the usual causes, how to fix it and a request that works. That page starts with the mistakes integrations make most often.

Errors reach you in two places. Refused straight away: the call itself answers with an HTTP error, no review is started, and nothing is saved. Failed later: the review was accepted (202), then went wrong while it worked. Then GET answers HTTP 200 with status failed and the error inside, so read status before you read anything else.

Refused when you start a review

Errors from POST /api/v1/safeguard/reviews
CodeHTTPWhy it happensHow to fix
SAFEGUARD_TURNED_OFF403Safeguard is switched off for this account. Starting a review, sending answers and retrying are all refused while it is off.Switch it on in the dashboard, or send {"enabled": true} to PUT /api/v1/safeguard/settings with a test key that has the settings permission.
MODEL_TIER_INVALID400modelTier is not standard or ultra-fast.Send modelTier: standard (the default) or ultra-fast, or leave it out.
LISTING_REQUIRED400The request has no listing to review: "listing" is missing or empty, and there is no orbitRunId. If you sent "item" instead, the next step asks if you meant "listing".Send the listing in your own field names, like "listing": {"title": "iPhone 14 128GB", "description": "..."}, or the runId of a finished Orbit listing in "orbitRunId".
CATEGORY_REQUIRED400The request does not say the listing's category, so Safeguard does not know its fields.Send the category name as it is in your schema, like "category": "Phones".
SAFEGUARD_NO_SCHEMA409Your business has no published listing fields, so Safeguard does not know the listing's fields.Upload a category with PUT /api/v1/schema-draft/categories/{category}, then publish with POST /api/v1/schema-versions. See Set up your listing fields.
CATEGORY_NOT_FOUND422The latest version of your published listing fields has no category with this name.Check the name against GET /api/v1/schema-versions/{version} and send it as written there.
PHOTOS_REQUIRED422The review request has no photos in images. No work starts and no credits are spent.Upload at least one photo with POST /api/v1/images and send its id in images, or set requirePhotos: false. Keeping photos on is strongly recommended: without them results are less reliable and can vary more.
PHOTOS_NOT_FOUND422A photo id in "images" is unknown, belongs to another business, or has expired (photos are kept for 30 minutes). problems has one entry for each, like /images/1.Upload each photo listed again with POST /api/v1/images, and use the new ids.
CURRENCY_NOT_IN_SCHEMA422A price field in the category lists the currencies it allows, and your business's currency is not one of them. Prices are never converted.Add your currency to that field's currency options and publish the listing fields again, or change the currency setting with PUT /api/v1/orbit/settings.
INVALID_LISTING400A listing field has the wrong type, is outside its fixed allowed values, or has an unreadable price. problems lists each affected field under /listing with its own next step. No work starts and no credits are spent. Required fields are judged, not validated: a missing required field is accepted (202), then flagged by the judge with seller questions where needed.Correct each field listed in problems, then send the listing again.
NOTHING_TO_DO400The request switched the review off ("review": false) and asked for neither a FAQ nor a rewrite.Leave "review" out (it is on by default), or send "faq": true or "rewrite": true.
REWRITE_NO_FIELDS422"rewrite": true rewrites /title and /description, but this category has neither field.Name the fields to rewrite by their paths, like "rewrite": ["/name", "/details"].
UNKNOWN_FIELD422A path in "rewrite" is not a field of this category's schema. problems has one entry for each, like /rewrite/0.Use field paths from your schema, like /title.
ORBIT_LISTING_NOT_FOUND404There is no Orbit listing with this orbitRunId for your business, or it was deleted: Orbit listings are kept for 30 minutes after their last change.Send the listing itself in "listing", with its "category", instead of orbitRunId.
ORBIT_LISTING_NOT_READY409The Orbit listing is still working or waiting for the seller's answers, so there is nothing to review yet.Read it with GET /api/v1/listings/{runId} and review it once it is listed.
ORBIT_LISTING_UNREADABLE500The saved Orbit listing could not be read back. This is a problem on PCIFIC's side.Send the listing itself in "listing" with its "category" instead of orbitRunId. If it happens again, contact PCIFIC support with the runId.

Refused when you answer or retry

SAFEGUARD_TURNED_OFF applies here too. Reading a review with an id that is unknown, belongs to another business or is older than 30 minutes is NOT_FOUND (404): start a new review.

Errors from the answers and retry calls
CodeHTTPWhy it happensHow to fix
REVIEW_NOT_READY409The call does not fit where the review is. Answers are taken only when the review is done, and a retry only when it failed. A review that is still working takes neither.Read the review with GET /api/v1/safeguard/reviews/{reviewId} and do what its status says.
NO_OPEN_QUESTIONS409The review has no questions waiting for the seller. Either it asked none, or they were all answered.To review a changed listing, start a new review with POST /api/v1/safeguard/reviews and the updated listing.
TOO_MANY_ANSWER_ROUNDS409The seller has already sent answers 3 times for this review, the most allowed.Start a new review of the updated listing (its "listing" field) with POST /api/v1/safeguard/reviews.
UNKNOWN_QUESTION422An answer's id is not one of the review's open questions. problems has one entry for each, like /answers/0/id.Answer each question by the "id" it came with in "sellerQuestions". A question already answered is not open any more.

Failed later, found when you read the review

These sit in error.code of a failed review. The HTTP column is the kind of fault, because the GET itself answers 200. SAFEGUARD_TURNED_OFF and CURRENCY_NOT_IN_SCHEMA can also appear here if the setting changes while a review waits.

Errors inside a failed review
CodeHTTPWhy it happensHow to fix
SAFEGUARD_BUSY503Safeguard was too busy to start this review within 120 seconds, so it was not done.Retry the review in a minute with POST /api/v1/safeguard/reviews/{reviewId}/retry. If it keeps happening, contact PCIFIC support with the reviewId.
SAFEGUARD_TIMED_OUT504The review took longer than 180 seconds.Retry it with POST /api/v1/safeguard/reviews/{reviewId}/retry. If it keeps happening, contact PCIFIC support with the reviewId.
SAFEGUARD_UNFINISHED422The AI did not send a complete review. Either it used 6 steps without finishing, or its answer was sent back 3 times for something wrong, such as a score outside 0 to 10 or a field that is not in your schema.Retry it with POST /api/v1/safeguard/reviews/{reviewId}/retry. If it fails again, contact PCIFIC support with the reviewId.
SAFEGUARD_MODEL_FAILED502Safeguard's AI service did not answer. This is a problem on PCIFIC's side.Retry the review in a moment with POST /api/v1/safeguard/reviews/{reviewId}/retry. If it keeps happening, contact PCIFIC support.
AI_PROVIDERS_BLOCKED503Every AI model that could answer is from a provider that PCIFIC has blocked for your business.Contact PCIFIC support to allow another AI provider for your business, then retry the review.
BYO_PROVIDER_ERROR502Your business uses its own AI model (Bring Your Own) and that model could not answer. The message identifies the failed AI connection.Check with your provider that your key, its credits and the model are working, then retry the review. See Bring your own AI.
SAFEGUARD_STOPPED500Work on the review stopped. There are three causes: the review stopped partway 3 times, the category could not be read back from your schema, or the review's saved details could not be read back. This is a problem on PCIFIC's side. The last cause can also answer a call to answers or retry straight away.Retry the review once with POST /api/v1/safeguard/reviews/{reviewId}/retry. If it stops again, start a new review, and contact PCIFIC support with the reviewId.
SAFEGUARD_NOT_SAVED500Only for a review Orbit started. The review of the finished listing could not be saved, so it cannot be read back, answered or retried. The listing itself is finished. It shows under pcific.safeguard in the Orbit listing.Review the listing again with POST /api/v1/safeguard/reviews and {"orbitRunId": "<runId>"}. If it happens again, contact PCIFIC support with the runId.

Refused when you upload photos

These come from POST /api/v1/images, which is Orbit's address. See the Orbit page for the photo rules, including iPhone photos (HEIC).

Errors from the photo upload
CodeHTTPWhy it happensHow to fix
ORBIT_TURNED_OFF403Photo uploads follow Orbit's switch, not Safeguard's. Orbit is switched off for this account.Switch Orbit on in the dashboard, or send {"enabled": true} to PUT /api/v1/orbit/settings.
NO_PHOTOS400The form has no photos in it.Send each photo as a file field of a multipart/form-data body, for example with curl -F "photo=@front.jpg".
NOT_A_FORM400The photos were not sent as a multipart/form-data form.Send each photo as a file field, for example with curl -F. Do not turn the photos into base64 text.
TOO_MANY_PHOTOS422One upload has more than 12 photos.Send the 12 photos that show the item best.
PHOTOS_REFUSED422No photos were saved because some could not be used. problems has one entry for each, like /images/2, with the reason.Fix or leave out each photo listed, then send them all again.
PHOTOS_TOO_LARGE413The photos together are over the limit for one upload, or the form was cut short.Send fewer photos per upload (several uploads are fine), or send each photo as the phone took it rather than a RAW export.
PHOTO_STORAGE_FAILED502The photos could not be stored. This is a problem on PCIFIC's side.Send the photos again in a moment. If it keeps happening, contact PCIFIC support.

The website checks body size first. Rust then checks retry headers, authentication, permission and the request fields. Network and key limits can refuse a request before later checks. Once a published category is identified, photo and listing-field problems are reported together, each with a next step. A 4xx refusal does not spend your Idempotency-Key: correct the request and send it again with the same key. A successful 202 replay returns the original reviewId ticket. Poll that reviewId for the current result, even if the ticket still says working. No second review is started.

Errors every address can give (a missing or revoked key, KEY_NOT_ALLOWED, INVALID_JSON, INVALID_FIELD, NOT_FOUND, RATE_LIMITED, TEST_DAILY_CAP_REACHED, OUT_OF_CREDITS and the rest) are on the Errors, limits and retries page. The same page explains the Idempotency-Key header, which POST /reviews, /answers and /retry all accept so that a repeated request is not done twice.

Limits

Safeguard limits
WhatLimit
Request body256 KB
listing50,000 characters as JSON
sellerNotes5,000 characters
Photos in a review12, each kept for 30 minutes after upload
Each seller answer1,000 characters
Rounds of seller answers3 for one review
Time to startA review that cannot start within 120 seconds fails with SAFEGUARD_BUSY
Time to finish180 seconds, then SAFEGUARD_TIMED_OUT. Most reviews take 5 to 60 seconds
How long a review is kept30 minutes after its last change
Web searchesAt most 2 for one review, and none for a review that writes a FAQ
In a resultUp to 10 fixes, 6 questions, 6 sellerQuestions, 6 sources, 3 to 6 FAQ entries, a summary of up to 600 characters
Rewrite1 to 5 field paths, none inside a list
Settings50 banned items of up to 200 characters, instructions up to 2,000 characters, 2 to 5 verdicts with names up to 40 characters

The request limit, the daily cap on test keys and your spend limits are on the Errors, limits and retries page and on API keys and permissions. Poll every 3 to 5 seconds, not in a tight loop. Starting a review, sending answers and retrying count as requests that start work.

Words used on this page

  • Review: one run of Safeguard on one listing. It has a reviewId.
  • Polling: asking again on a timer until the answer is ready.
  • 202: the HTTP status for "accepted, still working".
  • Schema or listing fields: the files that describe your listing's fields, which Safeguard reads.
  • Minor units: pence or cents, so 31500 with GBP is £315.
  • Gap: something a careful buyer needs that the listing does not say. Important gaps stop a purchase, minor gaps do not.
  • Verdict: your own name for a range of scores, with a meaning of block, review or pass.
  • Banned item: something on your own banned list, or plainly illegal or a scam. It always scores 0.
  • Seller question: a question only the seller can answer, such as battery health.
  • FAQ: frequently asked questions and answers, shown on the listing page.
  • Test key and live key: see API keys and permissions.
  • Idempotency-Key: a unique value you choose for each piece of work, so a repeated request is not done twice.