# Create products, one at a time and in batches

Create a product from the console, create a whole run, import a file, and read the refusals before they cost you a production run.

Source: https://docs.sealtrust.io/en/creer-des-produits/

---

By the end of this page, you will know how to create a single product, create a complete
run from a model, import a file of several hundred rows, and read every refusal the
console displays. You will also know what becomes permanent at the moment of creation.

## Three objects, three roles

- A **product model** describes an item in your catalog: its name, its internal
  reference, its category, its cover photo.
- A **production batch** groups the units manufactured together, under a batch code and a
  date.
- A **product** is the unit. It is the one that carries a printed code or a chip, and it
  is the one that gets verified.

This page is about creating products. The model and the batch stay optional to get there.
They change what the person who scans will see.

> [!INFO] A passport does not force you to serialize
> The ESPR regulation allows three levels for a digital product passport: the model, the
> batch and the unit (recital 33). Creating one unit per item sold is therefore not a
> general obligation. A model passport covers all the units that share the same product
> code. Only certain batteries are concerned at the unit level (regulation
> 2023/1542).

## What to prepare

### The brand and its number

Every product belongs to a brand. The brand selector of the console shows the name
followed by its number, in the form `Exemple SAS (#12)`. When your account gives access to
only one brand, the selector is locked on it. Note this number: file imports ask for it in
the `brand_id` column.

### The category

It is required for every product, and you choose it from the list the console offers. It
feeds the "Category" attribute of the token metadata.

### The product model

Optional, and it is what decides the image. With no model linked, the token will carry no
cover image. With a model that has no image, likewise. The image used is the cover image
of the model; failing that, the first media of image type attached to that model.

Models are created in **Catalog > Product models**. The list shows an ID column: that is
the number the imports expect in `product_model_id`.

### The production batch

Optional in order to create a product. It is created in **Catalog > Production
batches**. The batch reference, the brand and the product model are required. The
quantity, the manufacturing site, the country of origin and the notes are optional. The
form has two steps, and the first one does not validate without these three values. The
batch list also shows an ID column, to be carried over into `product_batch_id`.

### What your plan allows

Two checks run, and the moment changes according to the **authentication
method**, that is, what the unit will physically carry, a printed code, a chip, or both.
This is the label the console uses in its forms, and your import files name it
`auth_method`.

- **The authentication method your plan allows.** A brand whose plan does not include NFC
  receives a 403 refusal carrying the code `AUTH_METHOD_NOT_ALLOWED`, with the list of
  allowed methods.
- **The product quota.** It is counted by month, over the billing period. Exceeding it
  returns a 403 refusal carrying the code `QUOTA_EXCEEDED`, with the number already
  created, the maximum and the period.

For a `qr` unit, the server checks both at the moment of creation. For a chip unit, it
checks both later, at the moment the chip is encoded and the token minted. Preparing 500
NFC units can therefore succeed, and the `AUTH_METHOD_NOT_ALLOWED` or `QUOTA_EXCEEDED`
refusal appear only at encoding.

## Choosing the authentication method

A product carries only one of the three following values.

| Value | What the unit carries | What happens at creation |
| --- | --- | --- |
| `qr` | a printed code | from the "Single product" tab and from the CSV import, the unit goes to minting right away; from a run created out of a model, nothing is minted and you mint the batch afterwards |
| `nfc` | a chip | nothing is minted, the unit waits for its chip to be encoded |
| `nfc+qr` | a chip and a printed code | nothing is minted, the unit waits for its chip to be encoded |

> [!INFO] QR alone is a complete mode
> A `qr` product is verified, carries a certificate and carries its passport, exactly like
> a chip product. Choose `nfc` or `nfc+qr` when you also want the reading of a chip on the
> physical product.

> [!DANGER] The authentication method becomes permanent
> The method chosen is written into the token metadata, under the
> "Authentication" attribute. The hash of that metadata is written on the chain
> at the moment of minting, and the contract exposes no function to replace it. A unit
> created as `nfc` while it carries only a printed code will keep that mention forever.

## Create a product one at a time

Open **Catalog > Create product**, "Single product" tab.

| Field | Required | What it does |
| --- | --- | --- |
| Product name | yes | becomes the `name` field of the metadata |
| Brand | yes | attaches the unit and feeds the "Brand" attribute |
| Category | yes | feeds the "Category" attribute |
| Product model | no | supplies the cover image |
| Authentication | yes | `nfc`, `qr` or `nfc+qr`, default value `nfc` |

The creation button stays inactive as long as the name, the brand and the category are not
filled in.

### Preview before you create

The "Preview what will be written on-chain" button shows the exact metadata the token will
carry and the cover image. The address of the contract appears in the metadata shown,
under the "Contract" attribute. The console sends nothing and mints nothing during this
preview.

Four warnings can appear. The server writes them in English and the console displays them
as they are.

- "No product model linked", no model linked, the token will carry no cover image;
- "The linked model has no image", the linked model has no image, same
  consequence;
- "No category", no category, the "Category" attribute would stay empty;
- "No active contract in the registry", no active contract in the registry, minting would
  fail.

Your browser loads the image, so a dead address shows up as a broken image. This is the
last moment when you can still see an image error.

### What happens next

For a `qr` product, the minting is queued and the console shows "Minting in progress on
blockchain". The code can then be downloaded as a PNG image from **Catalog > Products**,
QR column.

For an `nfc` or `nfc+qr` product, the console creates a batch whose items are waiting for
encoding, with the status `ready_to_scan`. No token exists yet at this stage. Depending on
the profile of your account, the console also shows a link to the encoding screen, or
hides that link.

> [!ATTENTION] The checks on a chip product happen later
> For a `qr` product, the server checks the authentication method allowed and the monthly
> quota of your plan at the moment of creation. For a chip product, it checks both at the
> moment the chip is encoded and the token minted.
> Preparing 500 NFC units can therefore succeed, and the
> `AUTH_METHOD_NOT_ALLOWED` or `QUOTA_EXCEEDED` refusal appear only at encoding.

> [!INFO] Physical encoding carried out by SealTrust
> Depending on the profile of your account, the console hides the buttons that trigger the
> physical encoding and the minting of a batch, because SealTrust carries out the
> encoding. Those hidden buttons include "Mint batch from this model" on the record of a
> model, as well as "Mint the batch (grouped)" and "Prepare the QR codes" on the page of a
> batch. Creating a run from a model is therefore among the hidden actions, including for
> a `qr` run. **Catalog > Create product** stays available, with its three tabs, and you
> follow the progress in the catalog.

## Create several products at once

The "Batch (multiple products)" tab shows a table. Each row is a unit, with its name, its
brand, its category, its model and its authentication method. "Add row" adds one, the
trash icon removes one.

The "Create batch" button stays inactive as long as a row does not have its name, its
brand and its category.

This tab sends all the rows to the same place. If a single row carries NFC, the whole
table goes into the encoding queue, including the `qr` rows, and the console mints
nothing. To mix authentication methods in the same submission, go through the "From CSV"
tab, which separates the rows.

What you type is kept in the browser. If your session expires during the preparation, the
active tab and the rows in progress are restored after you sign in again.

## Create a run from a model

This path creates the production batch, the units, their names and their references in one
go.

Depending on the profile of your account, the button described below does not appear,
including for a `qr` run. In that case create your units from
**Catalog > Create product**, which stays available.

Open **Catalog > Product models**, open the model, then "Mint batch from this
model". The button stays inactive as long as the model has no category.

| Field | Required | Detail |
| --- | --- | --- |
| Quantity | yes | from 1 to 10,000 |
| Production date | no | the same day by default |
| Batch code | yes | prefilled in the form `SKU-AAAAMMJJ-HHMM` |
| Manufacturing site | no | free text |
| Authentication method | yes | `nfc`, `qr` or `nfc+qr` |
| Sealed batch | no | offered only when the authentication method includes NFC |

The console creates the production batch, then the units requested. Each unit takes the
name of the model followed by its rank on three digits, for example `Sac Exemple #001`,
and an external reference formed from the batch code followed by the same rank, for
example `SAC-20260820-1030-001`.

On this path, creating a run from a model mints nothing, whatever the authentication
method chosen, including `qr`. The two other paths behave differently: the "Single
product" tab and the CSV import mint `qr` units as soon as they are created.

For a `qr` run, the console drops you on the page of the batch. There you mint the batch
with "Mint the batch (grouped)", you wait for the confirmation, then you prepare the codes
to print with "Prepare the QR codes". These are two distinct buttons, minting being
asynchronous, and the second one appears only once units have been minted. These two
buttons are hidden as well, depending on the profile of your account.

For a run that carries NFC, the console drops you on the encoding screen. There the units
wait for their chip, and the minting happens at encoding.

> [!DANGER] A sealed batch cannot be undone
> Checking "Sealed batch" switches the opening detection on for every chip of the run.
> This is permanent on the chip. The tags must be NTAG 424 DNA TT with an intact tab.

## Import a file

The "From CSV" tab reads the file in your browser, shows you what it understood, then
sends the accepted rows.

### The columns

| Column | Required | Content |
| --- | --- | --- |
| `product_name` | yes | the name of the unit, not empty |
| `brand_id` | yes | the number of your brand, a whole number |
| `category_id` | yes | the number of the category, a whole number |
| `external_ref` | no | your own reference |
| `metadata_uri` | no | the address of your metadata |
| `product_model_id` | no | the number of the model, a whole number |
| `product_batch_id` | no | the number of the production batch, a whole number |
| `auth_method` | no | `nfc`, `qr` or `nfc+qr` |
| `owner_email` | no | read by the import, with no effect on the product created |

A minimal example file:

```csv title="produits.csv"
product_name,brand_id,category_id,product_model_id,auth_method
Sac Exemple 001,12,3,45,qr
Sac Exemple 002,12,3,45,qr
Sac Exemple 003,12,3,45,nfc+qr
```

> [!ATTENTION] The `owner_email` column designates nobody
> The file reader accepts this column, and the creation records no recipient from it.
> Filling this column therefore attaches no unit to an end customer.

### How the file is read

- The first line is the header and counts as line 1. The numbers shown in the refusals
  are the ones your spreadsheet shows.
- The reader strips the spaces at the edges of the values.
- The reader ignores entirely empty lines, with no message.
- When `external_ref` is empty, the reader fills it with the line number on four
  digits, for example `csv-0002`.
- When `metadata_uri` is empty, the server generates the metadata and posts it on
  IPFS.
- An empty `auth_method` takes the value of the selector shown above the preview, and the
  preview then marks that row as coming from the selector. That selector offers only
  `nfc` and `qr`. To obtain `nfc+qr`, write the value in the file.
- `auth_method` ignores case and spaces. The only separators accepted are the comma and
  the plus sign: `QR`, `NFC+QR`, `nfc,qr` and `qr + nfc` are understood, `nfc/qr` and
  `nfc qr` make the row refused.

### What makes the whole file refused

- A required column missing from the header. The console names the missing columns and
  imports nothing.
- The same column name twice, once the spaces are removed, for example `brand_id` and
  ` brand_id`. Nothing is imported, because one of the two cells would be kept and the
  other lost with no message.

The console shows separately the columns the import does not use, under their own label.
The file stays usable. That list serves to spot an `authmethod` written without the
underscore, which would otherwise be ignored silently.

### What makes a row refused

The console does not import a refused row, and the corresponding product will not exist.
The message carries the line number, the column at fault and the value written in the
file.

| Message | Cause |
| --- | --- |
| `product_name` is empty | the name cell is empty |
| `brand_id` is required and must be a whole number | empty cell or non-integer value |
| `category_id` is required and must be a whole number | empty cell or non-integer value |
| `product_model_id` must be a whole number when the column is filled | non-integer value |
| `product_batch_id` must be a whole number when the column is filled | non-integer value |
| `auth_method` is not one of the accepted values | word outside the three accepted methods, for example `rfid` |
| this row has fewer cells than the header, so the last columns do not exist on it | the row is too short |
| this row has more cells than the header, and the surplus cell would be lost | the row is too long |

The console never replaces an unreadable value with a guess. The result would go into the
token metadata, whose hash is written on the chain at minting, with no way back.

### The minting mode

Before the submission, the console offers you two choices for the units that go to direct
minting.

- **One transaction per product.** This is the default mode and the one that has always
  been used. If a minting fails, it fails alone.
- **Group up to 50 products per transaction.** This mode divides the chain fees. It
  has never run on a real batch in production. Try it on a small batch before using it on
  a run.

This choice commits only this import and changes no setting.

### After the submission

The chip rows go into the encoding queue, the `qr` rows go into the minting queue. The
console shows the count of each. The server refuses a submission of more than 10,000 rows
intended for direct minting, and asks you to split the file.

## The batch creation endpoint of the API

> [!ATTENTION] This path creates only `qr` units
> Your row carries no authentication method. The server sets `qr` on every row of the
> batch, and it sets the technical identifier of each unit itself. A machine call has no
> chip in hand, so `nfc` and `nfc+qr` cannot be obtained by this path. To create chip
> units, go through the console.

Here is what this endpoint accepts and refuses today.

- Address: `POST https://api.sealtrust.io/v1/partner/mint/batch`.
- Scope required on the key: `mint:batch`. Your plan must include API access.
- Five fields per row, and only five: `product_name`, `brand_id`,
  `category_id`, `metadata_uri` are required, `external_ref` is optional. Any
  other field makes the request fail with a 400.
- 500 items at most per call. The API refuses an empty batch.
- All the rows must carry the brand number of the key. A single row from another brand
  makes the whole batch refused with a 403.
- No authentication method field, and no technical identifier field. The server sets both
  on every row: the method is `qr`, and the technical identifier is issued by the server.
  You therefore choose neither of them by this path. The "Authentication" attribute of the
  metadata takes the value "QR Code + Blockchain", and that method is permanent. To create
  `nfc` or `nfc+qr` units, go through the console.
- Your plan must allow the `qr` method. Otherwise the whole batch is refused with a 403
  carrying the code `AUTH_METHOD_NOT_ALLOWED`.

The response comes back as a 200 as soon as the batch is queued. It contains four fields.
None of them says that a unit is already created, the creation taking place afterwards,
outside your call.

```json title="200 OK"
{
  "job_id": "0000a1b2c3d4e5f6",
  "status": "queued",
  "items_count": 2,
  "brand_id": 12
}
```

You then read the state of the batch with
`GET https://api.sealtrust.io/v1/partner/mint/batch/status/0000a1b2c3d4e5f6`,
which requires the same `mint:batch` scope. The `status` field repeats, as is, the
value from the execution queue. You will most often see `queued`,
`started`, `finished` and `failed`. The queue can also return `deferred`,
`scheduled`, `stopped` and `canceled`. `unknown` means that no batch
accessible with your key matches that identifier.

Loop as long as the status is `queued` or `started`. Stop on any other value, `finished`,
`failed` and `unknown` included, and stop as well on a value you do not know. A loop that
starts again on everything it does not recognize never ends. A `finished` batch tells you
nothing about the number of products created.

> [!INFO] Replaying a submission without creating a duplicate
> The `Idempotency-Key` header is optional and remembered for 24 hours. Replaying the
> same key with the same batch returns the response of the first call, without minting
> again. Replaying the same key with a different batch returns a 409. The equality of the
> two batches is about content: the order of the rows, the order of the fields and the
> empty cells make no difference.

## Common errors

| What you see | Cause | What to do |
| --- | --- | --- |
| The file is refused for missing columns | the header does not have `product_name`, `brand_id` or `category_id` | add the columns named in the message |
| Nothing is imported and a column name is reported as a duplicate | the header names the same column twice once the spaces are removed | rename or remove the duplicate |
| A row is refused for an `auth_method` that is not accepted | a word outside the three accepted methods, for example `rfid` | write `nfc`, `qr` or `nfc+qr` |
| All the rows take the same authentication method although the file specifies several | the column is written `authmethod` and appears among the unused columns | rename the column `auth_method` |
| 403 refusal with the code `AUTH_METHOD_NOT_ALLOWED` | your plan does not allow the authentication method requested | create these units as `qr`, or change plan |
| 403 refusal with the code `QUOTA_EXCEEDED` | the monthly product quota is reached | wait for the next period or change plan |
| The preview announces that no image will be carried | no model linked, or a model with no image | link a model and give it a cover image before creating |
| The chip units are created but no token exists | this is the normal behavior, the minting happens at encoding | move on to encoding the chips |
| The units created by the API all carry `qr` although you wanted NFC | the batch creation endpoint of the API creates only `qr` units | create the chip units from the console |
| The API answers 400 naming `owner_email` or `contract_address` | these fields were removed from the API batch on 08/20/2026 | remove them from your rows |
| The API answers 409 on a replayed submission | the same idempotency key has already been used for a different batch | change idempotency key |
