Orbit listing generation

Photos and a few words in, a finished listing in your own fields out.

Orbit turns what a seller says and the photos they take into a finished listing, written into your own listing fields (the facts a listing in your shop carries, such as storage, colour or mileage, set up on the Set up your listing fields page).

What a listing means, and what it does not

  • It is a draft for the seller to check. Show it to the seller and let them change it before it goes live.
  • Orbit is not a fraud check. It describes what the seller says and what the photos show. It cannot tell a real item from a fake, and it never calls an item genuine unless the seller does, and then only as the seller's words. Do not use it to decide whether an item is real.
  • Orbit reads photos to help describe the item. It does not promise to notice everything in them. When photos and notes show different items, Orbit asks the seller first. If the answer does not resolve it, the run fails with ORBIT_PHOTOS_DONT_MATCH.
  • Every value says where it came from (pcific.sources), so you can show the seller's own words and the web pages used.
  • complete: false means the seller was asked for a required field and could not say. The business must fill it in before publishing.
  • Orbit never converts a price from one currency to another. A price is the seller's own, or an estimate based on real prices from at least two different web pages. With fewer sources, Orbit returns no price rather than inventing one.

How you can use it

Where Orbit fits
WhoWhat Orbit does for them
ShopsThe seller types a sentence and adds photos on the create listing page. Orbit fills your listing fields, asks the few things only the seller knows, and the seller checks the result instead of filling a long form.
ClassifiedsA photo-first sell flow in any category. Orbit picks the category from your listing fields and writes the title and description in your house style.
Resale shops and refurbishersBulk listing: photograph each item at intake, start one listing per item and read them back as they finish. Staff answer the condition questions, and every listing reads the same way.
Car dealersAn advert from a few photos and the dealer's notes, in your own fields (make, model, mileage). Fields only the seller can answer, such as mileage and service history, are asked and never guessed.
Rental and property listingsA host photographs the place and writes a few lines. Orbit fills your fields from what the host says and what the photos show, and lists what it could not fill.
InsuranceAn inventory or claim description of an item or asset from the owner's photos and notes, in the insurer's own fields. The sources and the unfilled list show what the owner said and what they could not say. Orbit does not value or authenticate the item.

Before you start

  • An API key (a secret password your server sends with each request) with the orbit permission. Photos need orbit or safeguard. Reading and changing Orbit's settings needs a test key with the settings permission. See API keys and permissions.
  • Orbit switched on. It is on by default. Switched off, every call that starts work is refused with 403 ORBIT_TURNED_OFF.
  • Published listing fields. Orbit writes only into fields you define, and refuses with 409 ORBIT_NO_SCHEMA until you publish them.
  • Your currency. It is GBP unless you change it with the currency setting. Every price field in your listing fields must allow it (422 CURRENCY_NOT_IN_SCHEMA otherwise).
  • Optional: the pricing setting, if you want the price check, and Safeguard switched on, if you want the review or the buyer FAQ. The settings are on the Shop configuration page.
  • Your server makes the calls, never the seller's browser, so the key stays secret.

How it works

How a listing runs
  1. Upload the photosPOST /api/v1/images with the seller's photos. You get an id for each one at once.
  2. Start the listingPOST /api/v1/listings with the notes, details and photo ids. It answers 202 with a runId straight away.
  3. PollGET /api/v1/listings/{runId} every second (polling means asking again on a timer) while processing is true, even after the draft appears.
  4. Answer the questions, if there are anyIf the status is questions, show them all to the seller and send every answer in one request. Then poll again.
  5. Read the listingWhen the status is listing, item holds the draft. Keep polling while processing is true. When Safeguard is on, wait for parts.safeguard passed and publishReady true before publishing.
  6. Report what happenedAfter the seller publishes or gives up, POST /api/v1/listings/{runId}/outcome.

Orbit works in the background, so no call waits for the AI. Starting a listing answers at once with its runId (the listing's id, your ticket). You read the listing back with that id. A listing is kept for 30 minutes after its last change.

What the status means
statusWhat it meansWhat you do
workingOrbit is working, or waiting its turn.Read again every second while processing is true.
questionsOrbit needs answers only the seller has.Show the questions, then POST the answers.
listingThe draft is ready. Optional checks may still be running.Show item to the seller. Keep polling while processing is true and wait for Safeguard to pass before publishing when it is on.
failedThe work failed. error says why and retry says how to try again.Follow error.tryThis. A failed listing is not charged.
cancelledYou cancelled it.Nothing more happens.
  • Category. Orbit uses the category you send. Otherwise it searches your categories by plain words, using each category's description and keywords. If the item is not something you sell, it asks the seller what they meant to list, and if they insist it fails with ORBIT_NO_CATEGORY.
  • Questions. If the request is too vague to fill the required fields, Orbit asks everything at once, before it researches. A field you mark x-pcific-ask-seller in your listing fields is always asked of the seller, never guessed.
  • Verification. Orbit searches to verify checkable brand, model, storage, official specifications and release year. It never searches for condition, the seller's own description, asking price or generic care advice. It skips search when nothing checkable is present, such as a complete plain unbranded shirt. If a source disagrees, Orbit asks the seller with the source URL and keeps their information until they answer. The model decides using these instructions, with at most 4 searches.
  • Checking. Your listing fields decide what is right. Orbit adds no rules of its own. A value that does not fit them is sent back to be fixed, and after 6 refused tries the listing fails with ORBIT_UNFINISHED.
  • Your listing style. The listingStyle setting says how titles and descriptions are written. The about setting tells Orbit what you sell.

Quickstart

Use a test key (it spends no credits). The examples set it as $PCIFIC_TEST_KEY. Each step shows the real reply.

Step 1: upload the photos

If a photo decoder stops unexpectedly, a successful upload can contain accepted images plus problems naming each failed photo, its original position and what to try next. Keep the returned image ids and upload only the failed photos again. If ordinary photo validation refuses the request with 422, none of that request's photos are saved; fix the listed problems and send all photos again.

PCIFIC cleans each uploaded fileName before storing or returning it: folder names, control characters and .. are removed. If nothing remains, it uses upload.jpg, upload.png or upload.webp according to the Content-Type.

Send the photos as a multipart form (a form that carries files). Use JPEG, PNG or WebP, up to 12 photos, 15 MB each and 60 MB in all. iPhone HEIC photos are refused: see iPhone photos (HEIC) below.

Upload two photos
curl -X POST https://business.pcific.ai/api/v1/images \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -F "photo=@front.jpg" \
  -F "photo=@back.jpg"
Reply, one entry for each photo (shortened to one)
{
  "images": [
    {
      "id": "3f1c2a9e-5b7d-4e21-9c0a-6d8f4b1e7a52",
      "width": 4032,
      "height": 3024,
      "preview": "https://photos.pcific.example/preview.webp?signature=...",
      "listing": {
        "url": "https://photos.pcific.example/listing.webp?signature=...",
        "format": "webp"
      },
      "expiresAt": "2026-10-15T10:30:00Z",
      "fileName": "front.jpg"
    }
  ]
}

The preview and listing links are signed links that stop working at expiresAt. Photos are kept for 30 minutes after upload and for 30 minutes after the listing that uses them finishes. Save the copy you want to keep.

iPhone photos (HEIC)

PCIFIC accepts JPEG, PNG and WebP only. iPhones save photos as HEIC, and PCIFIC does not convert HEIC on its servers: decoding it takes a lot of memory and is expensive, so a HEIC photo is refused with PHOTOS_REFUSED.

  • Keep the photo picker's accept list to image/jpeg, image/png and image/webp. iPhone Safari, and every browser on iPhone (they all use Safari's engine), usually converts photos picked from the Photos library to JPEG by itself.
  • That is not guaranteed. Photos picked through Browse or the Files app often arrive as HEIC, and the behaviour has varied between iOS versions. As a safety net, convert the photo in the seller's browser before you upload it, with the snippet below.
  • If the conversion fails (desktop Chrome, for example, cannot read HEIC), show the seller a clear message asking them to save the photo as JPEG and try again.
Convert a picked photo to JPEG in the browser
// Turns a picked photo, HEIC included, into a JPEG of at most 2,500 pixels on its longest side.
// Resolves to null when this browser cannot read the file.
function toJpeg(file) {
  return new Promise((resolve) => {
    const url = URL.createObjectURL(file);
    const image = new Image();
    image.onload = () => {
      const scale = Math.min(1, 2500 / Math.max(image.naturalWidth, image.naturalHeight));
      const canvas = document.createElement("canvas");
      canvas.width = Math.round(image.naturalWidth * scale);
      canvas.height = Math.round(image.naturalHeight * scale);
      canvas.getContext("2d").drawImage(image, 0, 0, canvas.width, canvas.height);
      URL.revokeObjectURL(url);
      canvas.toBlob((blob) => resolve(blob), "image/jpeg", 0.9);
    };
    image.onerror = () => {
      URL.revokeObjectURL(url);
      resolve(null);
    };
    image.src = url;
  });
}

Step 2: start the listing

These responses illustrate the Phones schema published in the listing-fields guide. Use the image ids returned by your upload in images, then use your returned runId in every read and follow-up call. Your ids, timestamps, wording and questions will differ.

Start a listing
curl -X POST https://business.pcific.ai/api/v1/listings \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"notes":"iPhone 13, good condition, works fine. Selling my old phone. No box. Collection preferred. Asking £315.","details":{"price":{"amount":31500,"currency":"GBP"}},"images":["3f1c2a9e-5b7d-4e21-9c0a-6d8f4b1e7a52"]}'
Reply, 202 Accepted
{
  "status": "working",
  "processing": true,
  "deadlineAt": "2026-10-15T10:01:00Z",
  "parts": {
    "listing": "pending",
    "price": "off",
    "safeguard": "off",
    "faq": "off"
  },
  "publishReady": false,
  "runId": "8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75",
  "tryThis": "Read where it is with GET /api/v1/listings/8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75 in a few seconds.",
  "warnings": []
}

Keys that are not Orbit fields are ignored and named in warnings. This call takes an Idempotency-Key header, so a request you send again after a dropped connection starts only one listing (see Errors, limits and retries).

Step 3: show the draft and poll while processing

Read the listing
curl https://business.pcific.ai/api/v1/listings/8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75 \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY"
Reply while Orbit works
{
  "status": "working",
  "runId": "8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75",
  "processing": true,
  "deadlineAt": "2026-10-15T10:01:00Z",
  "parts": {
    "listing": "pending",
    "price": "off",
    "safeguard": "off",
    "faq": "off"
  },
  "publishReady": false,
  "tryThis": "Read this again in a few seconds."
}

Read every second while processing is true. The draft returns as soon as it is written, with status listing even while optional checks continue. Every response includes parts.listing (pending, ready or failed), parts.price (off, pending, ready or failed), parts.safeguard (off, pending, passed, flagged or failed), parts.faq (off, pending, ready or failed), deadlineAt and publishReady. When Safeguard is on, show and edit the draft but wait to publish until parts.safeguard is passed and publishReady is true. Fill required fields in pcific.unfilled too. Queue wait, drafting, checks and worker recovery share one 3-minute total processing ceiling. A 30-second idle timer resets on each AI answer or completed step; a stuck run stops with ORBIT_TIMED_OUT. Human answer time is excluded from the total ceiling. Answers resume the remaining budget, and retries keep the same ceiling. Near the ceiling Orbit finishes with known facts and lists missing fields in pcific.unfilled, with pcific.complete false. Failed work is not charged. A listing can be read for 30 minutes after its last change; after that GET answers 404 NOT_FOUND.

Step 4: answer the questions, if Orbit asks

If Orbit needs the storage or condition confirmed, the reply to the same call can be:

Reply when Orbit has questions
{
  "status": "questions",
  "processing": false,
  "deadlineAt": null,
  "parts": {
    "listing": "pending",
    "price": "off",
    "safeguard": "off",
    "faq": "off"
  },
  "publishReady": false,
  "runId": "8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75",
  "expiresAt": "2026-10-15T10:30:00Z",
  "questions": [
    {
      "id": "q1",
      "question": "How much storage does it have?",
      "field": "/storage_gb",
      "options": [
        "64",
        "128",
        "256",
        "512",
        "1024"
      ]
    },
    {
      "id": "q2",
      "question": "What condition is it in?",
      "field": "/condition",
      "options": [
        "New",
        "Like new",
        "Good",
        "Fair",
        "For parts"
      ]
    }
  ],
  "warnings": []
}
Send every answer in one request
curl -X POST https://business.pcific.ai/api/v1/listings/8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75/answers \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"answers":[{"id":"q1","answer":"128"},{"id":"q2","answer":"Good"}]}'

That answers 202 with the same body as step 2. Go back to step 3.

Step 5: read the finished listing

A draft while optional checks continue
{
  "status": "listing",
  "processing": true,
  "deadlineAt": "2026-10-15T10:01:00Z",
  "parts": {
    "listing": "ready",
    "price": "pending",
    "safeguard": "pending",
    "faq": "off"
  },
  "publishReady": false,
  "runId": "8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75",
  "item": {
    "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,
    "complete": true,
    "unfilled": [],
    "sources": {
      "/title": {
        "from": "ai"
      },
      "/storage_gb": {
        "from": "seller"
      },
      "/description": {
        "from": "ai"
      },
      "/brand": {
        "from": "seller"
      },
      "/model": {
        "from": "seller"
      },
      "/condition": {
        "from": "seller",
        "quote": "good condition"
      },
      "/price": {
        "from": "seller"
      }
    },
    "prices": {
      "/price": {
        "amount": 31500,
        "currency": "GBP",
        "display": "£315.00"
      }
    },
    "images": [
      {
        "id": "3f1c2a9e-5b7d-4e21-9c0a-6d8f4b1e7a52",
        "width": 4032,
        "height": 3024,
        "preview": "https://photos.pcific.example/preview.webp?signature=...",
        "listing": {
          "url": "https://photos.pcific.example/listing.webp?signature=...",
          "format": "webp"
        },
        "expiresAt": "2026-10-15T10:30:00Z"
      }
    ],
    "warnings": [],
    "price": {
      "status": "pending"
    },
    "safeguard": {
      "status": "pending"
    }
  }
}
Reply when the listing is ready
{
  "status": "listing",
  "processing": false,
  "deadlineAt": "2026-10-15T10:01:00Z",
  "parts": {
    "listing": "ready",
    "price": "off",
    "safeguard": "off",
    "faq": "off"
  },
  "publishReady": true,
  "runId": "8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75",
  "item": {
    "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,
    "complete": true,
    "unfilled": [],
    "sources": {
      "/title": {
        "from": "ai"
      },
      "/storage_gb": {
        "from": "seller"
      },
      "/description": {
        "from": "ai"
      },
      "/brand": {
        "from": "seller"
      },
      "/model": {
        "from": "seller"
      },
      "/condition": {
        "from": "seller",
        "quote": "good condition"
      },
      "/price": {
        "from": "seller"
      }
    },
    "prices": {
      "/price": {
        "amount": 31500,
        "currency": "GBP",
        "display": "£315.00"
      }
    },
    "images": [
      {
        "id": "3f1c2a9e-5b7d-4e21-9c0a-6d8f4b1e7a52",
        "width": 4032,
        "height": 3024,
        "preview": "https://photos.pcific.example/preview.webp?signature=...",
        "listing": {
          "url": "https://photos.pcific.example/listing.webp?signature=...",
          "format": "webp"
        },
        "expiresAt": "2026-10-15T10:30:00Z"
      }
    ],
    "warnings": []
  }
}

item is exactly your listing fields filled in, nothing added, so you can save it as it is. Everything PCIFIC adds is inside pcific. The listing can be read again for 30 minutes.

Step 6: report what the seller did

The seller published it
curl -X POST https://business.pcific.ai/api/v1/listings/8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75/outcome \
  -H "Authorization: Bearer $PCIFIC_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event":"published","changes":[{"field":"title","percentChanged":20}],"finalListing":{"title":"iPhone 13 128GB, good condition, collection preferred","description":"Selling my old phone. No box. Collection preferred.","brand":"Apple","model":"iPhone 13","storage_gb":128,"condition":"Good","price":{"amount":31500,"currency":"GBP"}}}'
Reply
{
  "recorded": true
}

Request fields

POST /api/v1/listings. The body is a JSON object (text in a fixed shape). At least one of notes, details or images is needed.

Send prices in details and finalListing as whole numbers in the smallest currency unit, such as 4500 pence for £45. Decimal points, exponents and text amounts are refused with 400 INVALID_FIELD.

Request body
FieldTypeRequiredWhat it is
notesstringNoThe seller's own words about the item, up to 5,000 characters. The shop's notesMinLength and notesMaxLength apply even with details or images. With both unset, at least 4 characters are needed only without details or images. Their facts win over guesses. null is the same as leaving it out.
detailsobjectNoCustomer-defined: what your form already has from the seller, keyed by your own field names exactly as you send them, with any JSON values. Counts as the seller's words. null or {} is the same as leaving it out.
imagesstring[]NoPhoto ids from POST /api/v1/images, at most 12. Photos are kept for 30 minutes. null is the same as leaving it out.
categorystringNoThe category's name in your newest published listing fields. Leave it out, or send null, and Orbit finds it.
pricebooleanNoLeave it out, or send null, to follow the pricing setting. false skips the price check for this listing. true needs the pricing setting on (PRICING_OFF otherwise).
safeguardbooleanNotrue has Safeguard review the finished listing. Leave it out, or send null, to follow Safeguard's orbitDefault setting. true needs Safeguard on (SAFEGUARD_OFF otherwise).
faqbooleanNotrue has Safeguard write a buyer FAQ for the finished listing. Off unless asked for. Needs Safeguard on. null is the same as leaving it out.
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.

Response fields

Starting a listing, sending answers, retrying

All three answer 202 with the same body.

Reply to a call that starts work
FieldTypeRequiredWhat it is
statusstringYesworking, or the current saved status on retry. Poll while processing is true.
runIdstringYesThe listing's id. Read it with GET /api/v1/listings/{runId}.
processingbooleanYesKeep polling while true, even with a ready draft.
deadlineAtstringYesUTC processing deadline; null while waiting for the seller.
partsobjectYesStatus of listing, price, Safeguard and FAQ.
publishReadybooleanYesDraft ready and Safeguard passed when enabled. Fill pcific.unfilled too.
itemobjectNoA kept draft on retry, in your listing fields.
pcificobjectNoThe kept draft's sources, price and checks on retry.
tryThisstringYesWhat to do next.
warningsstring[]YesOne line for each key in the body that is not an Orbit field. Those keys were ignored.

Reading a listing

GET /api/v1/listings/{runId} always answers 200 for a listing that exists. Which fields are there depends on status.

Reply when you read a listing
FieldTypeRequiredWhat it is
statusstringYesworking, questions, listing, failed or cancelled.
runIdstringYesThe listing's id.
processingbooleanYesKeep polling while true, even when status is listing.
deadlineAtstringYesUTC processing deadline, including queue wait. Null while waiting for the seller.
partsobjectYeslisting: pending/ready/failed; price: off/pending/ready/failed; safeguard: off/pending/passed/flagged/failed; faq: off/pending/ready/failed.
parts.listingstringYespending, ready or failed.
parts.pricestringYesoff, pending, ready or failed.
parts.safeguardstringYesoff, pending, passed, flagged or failed.
parts.faqstringYesoff, pending, ready or failed.
publishReadybooleanYesDraft ready and Safeguard passed when enabled. Fill pcific.unfilled before publishing.
tryThisstringNoWith working: what to do next.
expiresAtstringNoWith questions: when the listing stops waiting for answers (30 minutes after the questions).
questionsobject[]NoWith questions: [{id, question, field, options}]. field is the path of the field the answer fills, and options are that field's allowed values from your listing fields. Both are left out when there are none.
warningsstring[]NoWith questions: the ignored keys of the request, as in the 202.
itemobjectNoWith a ready draft, including a failed run with a kept draft: your listing fields filled in, nothing added, so it can be saved as it is.
pcificobjectNoWith listing: everything PCIFIC adds, see the next table.
errorobjectNoWith failed: {error, code, tryThis} like any error body.
retrystringNoWith failed: the call that tries again, written POST /api/v1/listings/{runId}/retry.
Inside pcific, when status is listing
FieldTypeRequiredWhat it is
releasestringYesThe Orbit release that wrote the listing. Only 2.0.0 exists.
categorystringYesThe category Orbit used.
schemaVersionintegerYesThe version of your listing fields it used.
completebooleanYesfalse when unfilled names required fields the seller could not give. The business must fill them before publishing.
unfilledobject[]Yes[{field, reason}]: required fields left empty because the seller was asked and could not say.
sourcesobjectYesWhere each value came from, keyed by field path like /storage: {from: seller, photo, web or ai}. A seller-only field also has the seller's own words in quote. A web value has the page in link.
pricesobjectYesEach price field written out, keyed by field path: {amount, currency, display}. amount is a whole number in the smallest unit.
priceobjectNoWhen the price check is on: {status: pending, priced, no_price or not_available, ...}. See the price check table.
imagesobject[]YesThe listing's photos, each as in the photo table.
warningsstring[]YesThe ignored keys of the request.
safeguardobjectNoOnly when safeguard or faq was asked for: {status: pending} until its result is saved, then the review as GET /api/v1/safeguard/reviews/{reviewId} shows it, with its reviewId. If Safeguard failed, {status: failed, error, retry}; the listing still comes back.
modelTierobjectNoOnce the price check and any Safeguard review have finished, which count towards it. 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.

Prices are whole numbers in minor units (the smallest unit of a currency, so 31500 pence is 315 pounds), with the currency code. Fields can be added at any time; ignore what you do not use.

Inside pcific.price, when the price check ran
FieldTypeRequiredWhat it is
statusstringYespending, priced, no_price or not_available. Only priced has prices.
currencystringNoWith priced: your currency.
quickSaleintegerNoWith priced: the price for a quick sale, in the smallest unit.
recommendedintegerNoWith priced: the recommended price.
patientintegerNoWith priced: the price for a patient seller. quickSale is at most recommended, and recommended at most patient.
sourcesobject[]NoWith priced: [{link, price, brand?, model?, reference?, variant?}], at least 2 different search result pages with prices in your currency. If a download fails, the search summary can supply the amount. Reported identifiers that differ from the listing's known fields never count. Missing listing identifiers are not inferred or compared.
reasonstringNoWith no_price or not_available: why there is no price.
A priced result
{
  "status": "priced",
  "currency": "GBP",
  "quickSale": 29000,
  "recommended": 31500,
  "patient": 34000,
  "sources": [
    {
      "link": "https://shop-one.example/iphone-13-128gb",
      "price": 30500
    },
    {
      "link": "https://shop-two.example/apple-iphone-13",
      "price": 32500
    }
  ]
}
  • The price check needs the pricing setting on. It looks for real prices of the exact item in the same condition and needs at least 2 different web pages with a real price. With fewer it answers no_price with the reason. When priced, it returns an estimate based on real prices from those pages, not a guaranteed sale price.
  • A comparable with a different known brand, model, reference or variant never counts toward the two-page minimum. Orbit compares the reported identifiers with the listing fields. An absent identifier is not inferred or compared.
  • When a price page cannot be downloaded, its search summary can supply the amount in your currency. Researched listing facts still need downloaded-page quotes.
  • A price in another currency never counts and is never converted.
  • The check runs beside drafting and Safeguard. The draft never waits for pricing. Keep polling while processing is true. A completed no_price is ready with no recommendation; not_available means the check failed. Each finished part is saved for the next poll.

Follow-up actions

When Orbit asks the seller
  1. Orbit stops and asksThe notes were too vague to fill the required fields. The status becomes questions, with up to 6 questions.
  2. You show every question togetherUse options for a drop-down when a question has them. Otherwise show a text input so grouped questions can collect every requested detail. Do not ask one at a time.
  3. You send every answer in one requestPOST /api/v1/listings/{runId}/answers answers 202 and Orbit carries on.
  4. Poll againThe next result is the listing, or a second round of questions (there are at most 2 rounds).
Calls on a listing
CallWhat it doesWhen it works
POST /api/v1/listings/{runId}/answersSends the seller's answers: {answers: [{id, answer}]}. Up to 1,000 characters each. A question you leave out is recorded as no answer. An answer may be "I don't know". Answers 202.Only while status is questions. Each question can be answered once.
POST /api/v1/listings/{runId}/retryTries a failed listing again from where its last stretch of work started: the request, or the seller's answers (the seller is not asked again). Answers 202.Only while status is failed.
POST /api/v1/listings/{runId}/cancelFree before AI work starts. Once AI work starts, cancellation charges the usual listing price, including the own-key rate when applicable, and counts towards spending limits. Stops the listing and its Safeguard review now, and deletes its photos unless another listing still going, or a Safeguard review, uses them. Answers {status: cancelled, runId}.While status is working, questions or failed.
POST /api/v1/listings/{runId}/outcomeTells PCIFIC what the seller did: {event: published, changes, finalListing} or {event: abandoned}. Kept for 30 days, once per listing, and only when Orbit is on full monitoring. Otherwise it returns 409 MONITORING_OFF. An unknown runId returns 404.For a listing Orbit finished. See Monitoring and privacy.
GET /api/v1/images/{id}One photo with fresh links.While the photo is kept.
DELETE /api/v1/images/{id}Deletes a photo and its copies now. Answers 204.While the photo is kept.
GET and PUT /api/v1/orbit/settingsReads or changes Orbit's settings. The table is on Shop configuration.A test key with the settings permission changes them.

The three calls that start work (start, answers, retry) count towards a test key's daily cap. Cancelling and photo uploads do not. Safeguard can review a finished Orbit listing by its runId (see the Safeguard page).

Errors

Every error is JSON with error (what went wrong), code (a fixed name to branch on), tryThis (the next step) and sometimes problems (the exact fields at fault, as paths like /images/0). A 4xx error means fix the request. A 5xx error is a problem on PCIFIC's side, so try again.

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

Orbit's errors appear in three places. Refused straight away: the call answers with an HTTP error and nothing starts. Failed later: the listing was accepted with 202, then fails, and you see it when you read the listing, where status is failed and error holds the same body. Photo errors: ordinary validation refuses the upload and saves no photos. A decoder worker failure can instead return accepted images and per-photo problems with a successful upload. The errors every address can return (UNAUTHORIZED, KEY_NOT_ALLOWED, RATE_LIMITED, OUT_OF_CREDITS and the rest) are on the Errors, limits and retries page.

Refused straight away by POST /api/v1/listings
CodeHTTPWhy it happensHow to fix
INVALID_JSON400The body is not a JSON object.Send a JSON object with the header "Content-Type: application/json".
MODEL_TIER_INVALID400modelTier is not standard or ultra-fast.Send modelTier: standard (the default) or ultra-fast, or leave it out.
ORBIT_TURNED_OFF403Orbit is switched off for this account.Switch it on in the dashboard, or send {"enabled": true} to PUT /api/v1/orbit/settings with a test key that has the settings permission.
ORBIT_NO_SCHEMA409This account has no published listing fields, so Orbit has no fields to fill.Upload a file for each category with PUT /api/v1/schema-draft/categories/{category}, then publish with POST /api/v1/schema-versions.
INVALID_FIELD400A field has the wrong type or is too long: notes over 5,000 characters or not text, details not an object, images not a list of photo ids (or more than 12), category not text, price, safeguard or faq not true or false, or a price amount in details written as text, a decimal or an exponent. problems names each one.Fix the field the message names, then send the request again. Send images as the ids POST /api/v1/images gave you. Send price amounts as whole numbers in pence, such as 4500 for £45.
PHOTOS_NOT_FOUND422A photo id in images is unknown, belongs to another business, or has expired. problems names each one at /images/{n}.Photos are kept for 30 minutes after upload. Upload each photo listed again with POST /api/v1/images and use the new ids.
NOTHING_TO_LIST400notes, details and images are all empty, so there is nothing to list.Send at least the seller's words in notes, for example "iPhone 17 Pro", or photo ids in images.
NOTES_TOO_SHORT400notes is shorter than the business notesMinLength, even with photos or details. With both notes limits unset, notes under 4 characters are refused only without details or images. problems names /notes and the limit.Send enough notes to meet the limit in the error. With both limits unset, send at least 4 characters or add photos or details.
NOTES_TOO_LONG400notes exceeds this business's notesMaxLength. The error names /notes, its length and the business's limit, even with photos or details.Shorten notes to the business's notesMaxLength and send the request again.
PRICING_OFF409The request has price: true, but the price check is switched off for this account.Switch it on with {"pricing": true} to PUT /api/v1/orbit/settings, or leave price out.
SAFEGUARD_OFF409The request has safeguard: true or faq: true, but Safeguard is switched off for this account.Switch it on with {"enabled": true} to PUT /api/v1/safeguard/settings, or leave the field out.
CATEGORY_NOT_FOUND422category is not a category in your newest published listing fields.Check the name against GET /api/v1/schema-versions/{version}, or leave category out and Orbit will find it.
CURRENCY_NOT_IN_SCHEMA422A price field in this category does not allow your business's currency. Prices are never converted, so no listing could fit.Add your currency to that price field's currency options and publish your listing fields again, or change currency with PUT /api/v1/orbit/settings.
TEST_DAILY_CAP_REACHED429A test key's AI requests reached today's daily cap. The count starts again at midnight UTC.Wait until midnight UTC, or raise the daily cap for test keys on the dashboard API keys page.
Refused by the follow-up calls (answers, retry, cancel, outcome)
CodeHTTPWhy it happensHow to fix
ORBIT_PROCESSING_EXPIRED409This runId has reached deadlineAt, so it has no processing time left and cannot be retried. No credit is reserved for the refused retry.Start a new listing with POST /api/v1/listings using the seller's notes, details and unexpired photo ids.
INVALID_FIELD400Answers call: answers is missing or empty, an answer has no id or answer text, an answer is over 1,000 characters, or one question is answered twice.Send {"answers": [{"id": "q1", "answer": "256GB"}]} with one entry per question. An answer may be "I don't know".
INVALID_FIELD400Outcome call: event is not published or abandoned, changes is not a list of at most 500 {field, percentChanged} entries, finalListing is over 200 KB or sent with abandoned, or abandoned has changes.Fix the field named in problems. Send changes and finalListing only with published.
ORBIT_TURNED_OFF403Orbit is switched off for this account.Switch it on in the dashboard, or send {"enabled": true} to PUT /api/v1/orbit/settings with a test key that has the settings permission.
NOT_FOUND404There is no listing with this id. Listings are deleted 30 minutes after their last change, and a listing waiting for answers 30 minutes after its questions.Start the listing again with POST /api/v1/listings, and use the runId it answers with.
LISTING_NOT_READY409The call does not fit where the listing is. Answers need status questions, retry needs failed, and cancel needs working, questions or failed. Sending the same answers twice at once also lands here for the second one.Read the listing with GET /api/v1/listings/{runId} and do what its status says.
UNKNOWN_QUESTION422An answer's id is not one of the listing's open questions. problems names it at /answers/{n}/id.Answer each question by the id it came with in the questions list. A question can be answered once.
ORBIT_STOPPED500Work on this listing stopped partway 3 times, or its saved progress could not be read back. This is a problem on PCIFIC's side.Retry with POST /api/v1/listings/{runId}/retry. If it stops again, or the listing cannot be retried, start a new one with POST /api/v1/listings and contact PCIFIC support with the runId.
OUTCOME_ALREADY_REPORTED409This listing's outcome was already reported.Report each listing's outcome once. Nothing more is needed for this one.
MONITORING_OFF409Orbit full monitoring is off for this business, so the listing outcome cannot be recorded.Choose full monitoring for Orbit on the dashboard Privacy page, then start a new listing and report its outcome.

Orbit photo uploads and PixelTrace checks and camera uploads share a photo capacity limit per server: 16 requests and 128 MB of uploaded bytes, with at most 2 requests and 64 MB per business across its shops and keys. These limits include receiving and processing. Content-Length reserves bytes before the body is read; without it the call reserves its maximum body size. Actual streamed bytes are counted too. Uploads must arrive within 120 seconds and wait at most 20 seconds for a photo worker. Public demos use separate capacity: 2 requests, 16 MB and one photo worker. Demo traffic cannot take paid photo places. A busy refusal does not save photos. Wait Retry-After seconds before sending the same upload again.

Photo upload errors (POST /api/v1/images)
CodeHTTPWhy it happensHow to fix
PHOTO_UPLOAD_BUSINESS_BUSY429Your business has reached its photo request or byte capacity across Orbit and PixelTrace.Wait the 20 seconds in Retry-After, then send the photo upload again. Send fewer uploads at the same time.
PHOTO_UPLOAD_BUSY503The server's photo capacity is full, or a photo worker was not available within 20 seconds.Wait the 20 seconds in Retry-After, then send the photo upload again.
PHOTO_UPLOAD_TIMEOUT408The photo upload body did not finish arriving within 120 seconds.Check your connection, then send the photo upload again. Send fewer photos per upload if needed.
ORBIT_TURNED_OFF403Orbit is switched off for this account.Switch it on in the dashboard, or send {"enabled": true} to PUT /api/v1/orbit/settings with a test key that has the settings permission.
NOT_A_FORM400The body is not multipart/form-data.Send each photo as a file field of a multipart/form-data body, for example with curl -F "photo=@IMG_0001.jpg". Do not turn the photos into base64 text.
NO_PHOTOS400The form has no photos in it.Add at least one file field to the form.
TOO_MANY_PHOTOS422The form has more than 12 photos.Send the 12 photos that show the item best. Several uploads are fine.
PHOTOS_TOO_LARGE413The photos together are over 60 MB, 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.
PHOTOS_REFUSED422No photo was saved, because at least one could not be used: it is over 15 MB, over 60 megapixels, not a JPEG, PNG or WebP (iPhone HEIC and AVIF are refused), or damaged. problems names each one at /images/{n}.Fix or leave out each photo listed, then send them all again.
PHOTO_STORAGE_FAILED502PCIFIC could not store the photos. This is a problem on PCIFIC's side.Send the photos again in a moment. If it keeps happening, contact PCIFIC support.

GET and DELETE /api/v1/images/{id} answer 404 NOT_FOUND for a photo that is unknown or has expired. Upload it again.

Failed later (read from a listing whose status is failed)
CodeHTTPWhy it happensHow to fix
SAFEGUARD_TIMED_OUT504The listing reached its total processing ceiling before its Safeguard check finished. The draft is returned with pcific.safeguard.status failed and must be checked before publishing.Keep the draft and request POST /api/v1/safeguard/reviews with its orbitRunId, then read that review before publishing.
ORBIT_TIMED_OUT504Orbit made no progress for 30 seconds, or could not start within its queue allowance. Incoming answer text, tool-call arguments and completed steps reset the idle timer. Provider keep-alive pings do not. Finished parts are kept. The separate 3-minute total processing ceiling returns a partial listing with missing-field fixes. Seller-answer waiting does not count; retry does not extend the ceiling.Retry with POST /api/v1/listings/{runId}/retry while processing time remains. After deadlineAt, start a new listing.
ORBIT_STOPPED500Work on this listing stopped partway 3 times, or its saved progress could not be read back. This is a problem on PCIFIC's side.Retry with POST /api/v1/listings/{runId}/retry. If it stops again, or the listing cannot be retried, start a new one with POST /api/v1/listings and contact PCIFIC support with the runId.
ORBIT_UNFINISHED422Orbit could not finish the listing within its 14 steps or 6 submissions, or reached its total processing ceiling without a usable category or a partial listing that fits your fields.Retry with POST /api/v1/listings/{runId}/retry. If it fails again, check that the category's listing fields allow what sellers describe, or start a new listing with more detail in the notes, such as the exact model and condition.
ORBIT_NO_CATEGORY422Orbit found no category for the item, or the seller confirmed an item your shop does not sell. The error names your main categories.If the item belongs in one of your categories, start a new listing with category set to it. If it is not something you sell, tell the seller.
ORBIT_PHOTOS_DONT_MATCH422The photos and notes still disagree after asking the seller. The error names the different items when known.Check the photos are of the item described, or update the description, then send it again.
ORBIT_MODEL_FAILED502None of the AI models that Orbit tries finished the answer. This is a problem on PCIFIC's side.Send the request again in a moment with a new POST /api/v1/listings. If it keeps happening, contact PCIFIC support.
AI_PROVIDERS_BLOCKED503Every AI model that could answer is from a provider PCIFIC has blocked for your business.Contact PCIFIC support to allow another AI provider for your business, then send the request again.
CURRENCY_NOT_IN_SCHEMA422The category Orbit chose has a price field that does not allow your business's currency. Prices are never converted.Add your currency to that price field's currency options and publish your listing fields again, or change currency with PUT /api/v1/orbit/settings.
A failed listing
{
  "status": "failed",
  "processing": false,
  "deadlineAt": "2026-10-15T10:01:00Z",
  "parts": {
    "listing": "failed",
    "price": "off",
    "safeguard": "off",
    "faq": "off"
  },
  "publishReady": false,
  "runId": "8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75",
  "error": {
    "error": "Orbit made no progress for 30 seconds while writing or checking this listing and stopped. Finished parts are kept.",
    "code": "ORBIT_TIMED_OUT",
    "tryThis": "Try it again with POST /api/v1/listings/8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75/retry. If it keeps happening, contact PCIFIC support with the listing's runId."
  },
  "retry": "POST /api/v1/listings/8d0e5f12-7a3c-4b96-8e41-c2d9a6f03b75/retry"
}

A listing that fails is not charged. A test key's daily count is given back when the failure was on PCIFIC's side. A business on Bring Your Own can also see the BYO errors on that page.

Limits

Orbit's limits
WhatLimit
Photos per upload and per listing12
Size of one photo / all photos in one upload15 MB / 60 MB
Photo type and size in pixelsJPEG, PNG or WebP, up to 60 megapixels (a 48 megapixel phone photo fits)
How long photos are kept30 minutes after upload, and 30 minutes after the listing using them finishes. Never deleted while a listing that is still going uses them
notes5,000 characters
One answer1,000 characters
Questions in one round / rounds of questions6 / 2
How long a listing waits for answers30 minutes
How long a listing can be read30 minutes after its last change
Total processing time, including queue wait and checks180 seconds total; seller-answer waiting is excluded. Retry cannot extend it.
Drafting / pricing / SafeguardDrafting uses the two clocks; pricing 35 seconds and Safeguard 30 seconds, inside the total ceiling
Idle timer30 seconds without an AI answer or completed step, then ORBIT_TIMED_OUT
Stopped worker detection12-second claim renewed every 4 seconds
Silent AI modelBackup after 8 seconds without answer content
Web searches for one listing / for one price check4 / 3
Tries to submit a listing that fits your fields6, then ORBIT_UNFINISHED
Web pages the price check needsAt least 2 different pages with a real price
A finished listing waiting for its price checkNone. Poll for updates
Requests a minute, daily cap on test keys, spend limitsSee Errors, limits and retries and API keys and permissions

Words used on this page

Words
WordMeaning
Listing fieldsThe facts a listing in your shop carries, set up once as a schema (a description of the fields and their types).
listingOne Orbit run, from the start call until its finished reply. It is read with its runId.
itemThe part of a finished reply that holds your listing fields, filled in. Save this part.
runIdThe id of one listing, returned when you start it.
PollingAsking again on a timer until the answer is ready.
SourceWhere a value came from: seller, photo, web or ai (Orbit's own wording, such as a title).
QuoteThe seller's own words, kept with a value that only the seller may give.
Minor unitsThe smallest unit of a currency, such as pence or cents, so 5500 GBP is 55 pounds.
Multipart formA web form that carries files, which is how photos are uploaded.
Idempotency-KeyA header that makes a request safe to send again.