Skip to navigation

Digitise and import shades

Browse, review and bulk-add shades through the API alone, in the order an automation or an AI agent should call it.

Everything the Platform’s digitisation screens do is a public call. This page is the order to call it in, written so a script or an AI agent can follow it without guessing. An AI agent can also use these steps as tools, through the MorphiaOne MCP server or the digitisation skill: see Connect an AI agent.

The rules that keep a run safe

  • List before you create. Every shade you add is unique by its external_ref (your SKU). Read GET /v1/catalogue/variants?external_ref=... first; a taken reference answers 409 EXTERNAL_REF_TAKEN.
  • Every write carries an Idempotency-Key. Derive it from the row, not from a clock, so a rerun replays the first answer instead of creating a duplicate.
  • A SKU’s reference and GTIN are fixed at creation. To change a shade you already have, post a new revision of it. A revision never carries external_ref or gtin.
  • Review what you accept. Accepting and staging send the candidate_sha256 you read; a candidate that changed since is refused.
  • Colours are upper-case hex, #RRGGBB.

The key you need

The read calls below need digitisation.read. Writes need digitisation.write, digitisation.review and the catalogue.write scope. Create the key in the Platform, under API keys. A key without the scope is refused 403 INSUFFICIENT_SCOPE. If your account’s review policy is on (Settings → Review policy), no key can accept or reject, and a key stages only what a member accepted: those calls answer 403 HUMAN_REVIEW_REQUIRED, and the candidate’s review_url is where a person decides.

Browse what exists

Three list calls return the same page shape, data, has_more and next_cursor, newest first. Send next_cursor back as cursor with the same filters to continue; a cursor from another filter or account is 400 INVALID_CURSOR. limit is 1 to 100.

CallFilters
GET /v1/digitisation/evidenceoriginal_upload_sha256: was this file already retained?
GET /v1/digitisation/assessmentsstatus, repeatable: queued, running, succeeded, failed, cancelled
GET /v1/digitisation/candidatesreview_status, repeatable: pending, accepted, rejected; assessment_id

The review queue is the pending candidates:

curl -s "$MORPHIAONE_BASE_URL/v1/digitisation/candidates?review_status=pending&limit=20" \
-H "Authorization: Bearer $MORPHIAONE_API_KEY"

An unknown or repeated parameter is 400 INVALID_REQUEST naming it, never ignored.

In the SDKs

Each list call has a list_all_* form that follows the cursors for you. The morphiaone digitisation candidates command prints the same page as JSON.

for candidate in client.digitisation.list_all_candidates(review_status="pending"):
print(candidate.id, candidate.review_status)
page = client.digitisation.list_assessments(status=["queued", "running"], limit=20)

From a photo to a staged shade

  1. Retain the photo: POST /v1/digitisation/evidence with asset_id (an upload) or url (a public https image the API fetches). When someone else has the photo, POST /v1/uploads answers a single-use upload_url they send it to with no API key; GET /v1/uploads/{upload_id} answers its asset_id once it is stored.
  2. Measure regions: POST /v1/digitisation/assessments answers 202 queued; poll GET /v1/digitisation/assessments/{assessment_id} until it is settled.
  3. Freeze one region for review: POST /v1/digitisation/candidates.
  4. Review it in the queue above, then accept or reject by its digest.
  5. Stage the accepted candidate into the catalogue as a draft revision.

Approving and publishing the draft is a separate step with its own scope, so nothing renders in a try-on until you do.

Add many shades from a table

There is no bulk route. Loop over your rows with the rule above: create the variant, and on 409 EXTERNAL_REF_TAKEN find the live variant by external_ref and post a revision. Keep one result per row and carry on after a failure, so one bad row never stops the file. The SDKs do exactly this and return created, revised or failed for each row.

rows = [
{
"display_color": "#c27461",
"applications": [{"application_id": "lips", "category": "lipstick"}],
"external_ref": "SKU-1",
},
]
for result in client.catalogue.import_shades(rows):
print(result.row["external_ref"], result.outcome, result.error)

Machine-readable sources

An agent should read the contract rather than this page: the OpenAPI document, llms.txt and the generated scope table list every route, parameter and scope.