Method POST/partner/mint/batch

Send a batch of product rows, in JSON or in CSV, up to 500 rows per call. Scope mint:batch. The products created by this path are identified by QR.

On this page

You send a batch of product rows, and you receive a batch identifier. We answer as soon as we accept the batch, before any processing. You then follow the progress with GET /v1/partner/mint/batch/status/{job_id}. Read the warning below before reading that status.

Full address:

HTTP
POST https://api.sealtrust.io/v1/partner/mint/batch

The same endpoint also answers without the /v1 prefix, at https://api.sealtrust.io/partner/mint/batch. The two addresses call the same code. Use the /v1 form for a new integration.

#Authorization

Send your API key in the Authorization header, in the Bearer format. The key must carry the mint:batch scope. A key that does not carry this scope receives a 403 whose message names the missing scope.

Three conditions come on top of the scope of the key.

  • The plan of your brand must include API access. Otherwise you receive a 403 carrying the code FEATURE_NOT_AVAILABLE.
  • The plan of your brand must allow identification by QR. That is the method the server sets on every row of this endpoint. A plan that does not allow it receives a 403 carrying the code AUTH_METHOD_NOT_ALLOWED.
  • All the rows of the batch must carry the brand number of the key. A single row carrying another number makes the whole batch refused with a 403.

#Rate limit

We measure the rate over a fixed 60-second window. We first apply the value set on your account, if we have set one. Otherwise the one of your plan. In the absence of both, the fallback value is 120 calls per window.

Two counters are layered, with the same cap: one per key, one for the sum of all the keys of your brand. Creating additional keys therefore does not increase the total rate allowed.

As soon as we recognize your key, the response carries four headers. They describe the more constraining of the two counters. An authentication refusal comes before the counting and carries none of these headers.

HeaderContent
X-RateLimit-Limitthe cap applied over the window
X-RateLimit-Remainingwhat is left to you in the current window
X-RateLimit-Resetthe end-of-window timestamp, in seconds
X-RateLimit-Scopekey or brand, the counter used as the reference

A rate refusal returns 429. It carries these four headers, describing this time the counter that refused, and it adds Retry-After, expressed in seconds remaining in the current window. This value is never lower than 1.

Two other caps can refuse the same call: the daily quota of your key, which is consumed per item, and the monthly product quota of your plan. You will find them in the error table, further down.

#Path and query parameters

This endpoint has neither path parameter nor query parameter.

#Headers

NameTypeRequiredDescription
AuthorizationstringyesBearer followed by your API key.
Content-Typestringyesapplication/json for a batch in JSON, multipart/form-data for a CSV file.
Idempotency-KeystringnoYour own call identifier. We remember it for 24 hours. See the section on replaying.

#Request body

You send the batch in one of the two formats. The content is the same in both cases.

  • JSON: send a list of objects. A lone object, a number or a string at the top level makes the request fail with a 400.
  • CSV: send a file as multipart/form-data, under the form field named exactly file. Encode the file in UTF-8. You may leave the byte order mark at the head of the file, we remove it. A file encoded otherwise makes the call fail with a 500. In a spreadsheet, choose the "CSV UTF-8" export. We strip the edge spaces of the cells before validating.

You can compress the CSV file in gzip or in zlib, we decompress it. If the decompressed content exceeds about 20 MiB, you receive a 413.

A batch has at most 500 items. We refuse an empty batch with a 400.

#Fields of a row

A row accepts five fields, and only five.

NameTypeRequiredDescription
product_namestringyesThe name of the product.
brand_idintegeryesThe number of your brand. It must be identical on every row and match the brand of the key.
category_idintegeryesThe number of the category of the product. The category must exist.
metadata_uristringyesThe address of the metadata of the product.
external_refstringnoYour own reference. We keep it on the row of the batch. No partner endpoint returns it to you: the batch status does not contain it, and the product created does not carry it, no product field takes it up. You find it again only in the 400 error report of a CSV submission, which names the refused row.

In CSV, the header must contain at least the four required columns. The order of the columns is free.

The server ignores no field. Any field this table does not name makes the row fail, and therefore the batch, with a 400.

#Replaying a call without doubling its effect

You can send an Idempotency-Key header. It is optional. We remember it for 24 hours.

  • Replay the same idempotency key with the same batch, and you receive the response of the first call, without a second processing starting.
  • Replay the same idempotency key with a different batch, and you receive a
    1. This behavior dates from 08/20/2026. Before that, the second batch was never processed and nothing reported it.
  • Replay an idempotency key whose call is still being processed, and you also receive a 409. This lock expires on its own after 60 seconds.

We compare two batches on their content once read and normalized. The order of the rows, the order of the columns, the choice between CSV and JSON and the empty cells make no difference.

#Example request

A batch of two items, in JSON.

curl -i -X POST https://api.sealtrust.io/v1/partner/mint/batch \
  -H "Authorization: Bearer st_test_0000000000000000000000000000000000000000000000" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lot-exemple-0001" \
  -d '[
    {
      "product_name": "Sac Exemple 001",
      "brand_id": 12,
      "category_id": 3,
      "metadata_uri": "ipfs://exemple-metadonnees-0001",
      "external_ref": "EX-0001"
    },
    {
      "product_name": "Sac Exemple 002",
      "brand_id": 12,
      "category_id": 3,
      "metadata_uri": "ipfs://exemple-metadonnees-0002",
      "external_ref": "EX-0002"
    }
  ]'

The same batch in CSV, sent as multipart/form-data. The lot.csv file contains:

lot.csv
product_name,brand_id,category_id,metadata_uri,external_ref
Sac Exemple 001,12,3,ipfs://exemple-metadonnees-0001,EX-0001
Sac Exemple 002,12,3,ipfs://exemple-metadonnees-0002,EX-0002

The TypeScript SDK does not expose CSV submission. The TypeScript tab below therefore calls the endpoint directly.

curl -i -X POST https://api.sealtrust.io/v1/partner/mint/batch \
  -H "Authorization: Bearer st_test_0000000000000000000000000000000000000000000000" \
  -H "Idempotency-Key: lot-exemple-0001" \
  -F "file=@lot.csv;type=text/csv"

#Example response

HTTP code 200.

JSON
{
  "job_id": "0000a1b2c3d4e5f6",
  "status": "queued",
  "items_count": 2,
  "brand_id": 12
}

The response has four fields and nothing else.

FieldTypeDescription
job_idstringThe identifier of the batch. Pass it to GET /v1/partner/mint/batch/status/{job_id}.
statusstringAlways queued on this response.
items_countintegerThe number of rows accepted in the batch.
brand_idintegerThe number of your brand.

status: "queued" means that we have accepted the batch and that it is waiting for its processing. We have processed nothing at that moment. Follow what comes next with GET /v1/partner/mint/batch/status/{job_id}.

#Errors

The body of an error response carries a detail field. Depending on the case, this field contains a sentence or an object.

A refusal that carries a sentence, here the empty batch, HTTP code 400:

JSON
{
  "detail": "Batch vide (aucun item)"
}

A refusal that carries an object, here an incomplete CSV header, HTTP code 400:

JSON
{
  "detail": {
    "error": "Colonnes manquantes",
    "missing": ["category_id", "metadata_uri"]
  }
}
CodeConditionWhat to do
400The JSON body sent is malformed.Check that you are sending valid JSON, or a CSV file as multipart/form-data.
400The JSON body is not a list at the top level.Wrap your object in a list, even for a single item.
400One or more JSON rows are invalid. detail is {"error": "JSON invalide", "rows": [...]}, each entry carrying the index of the row at fault and the reason.Correct the rows reported. The first object of the list carries index 1. No row of the batch was processed.
400The CSV file is empty or has no header line. detail is {"error": "CSV vide ou sans en-têtes"}.Add the header line.
400A required column is missing from the CSV header. detail is {"error": "Colonnes manquantes", "missing": [...]}.Add the columns named in missing. The file was not read beyond its header.
400One or more CSV rows are invalid. detail is {"error": "CSV invalide", "rows": [...]}, each entry carrying the line of the file, its external_ref and the reason.Correct the rows reported. The first data line carries number 2.
400The gzip file sent is unreadable.Send the file again, or send it without compression.
400The batch is empty.Send at least one row.
400The batch exceeds 500 rows. The message gives the number received and the cap.Split your submission into several calls of 500 rows at most.
400One or more category_id values match no category. The message lists the numbers that cannot be found.Correct the category numbers. The list of categories is visible in the console.
401The Authorization header is missing. The response also carries WWW-Authenticate: Bearer.Add the header.
401The Authorization header does not start with Bearer followed by a space. The response also carries WWW-Authenticate: Bearer.Correct the form of the header.
401The value sent after Bearer is empty or is shorter than 40 characters.Send the complete secret, with no space and no line break.
401The key sent is unknown.Check that you are using a key of this environment, and that it has not been recreated.
403The key is no longer active, because it was revoked. The message gives its state.Create a new key in the console.
403The key has reached its expiration date.Create a new key. The old one will never become valid again.
403The key does not carry the mint:batch scope. The message names the missing scope.Create a key carrying this scope. The scopes of an existing key cannot be modified.
403The plan of your brand does not include API access. detail is {"code": "FEATURE_NOT_AVAILABLE", "feature": "api_access"}.Contact us to open this right on your plan. Trying again will change nothing.
403The plan of your brand does not allow identification by QR, which is the method set by this endpoint. detail carries the code AUTH_METHOD_NOT_ALLOWED.Contact us to open QR on your plan. Trying again will change nothing.
403At least one row carries a brand_id different from the one of the key. The message gives the expected number and the number of rows at fault.Correct the rows. No row of the batch was processed.
403The batch would exceed the monthly product quota of your plan. detail is {"code": "QUOTA_EXCEEDED", "resource": "products", "current": ..., "additional": ..., "max": ..., "period": "monthly"}.Wait for the next period, reduce the size of the batch, or contact us to change plan. This refusal does not consume the daily quota of your key.
409The idempotency key has already been used for a different batch in the last 24 hours.Use a new idempotency key for this batch. This batch was not processed.
409A call carrying the same idempotency key is being processed.Wait for the response of the first call, then read its result again. The lock expires after 60 seconds.
413The compressed file exceeds about 20 MiB once decompressed.Split your file. A batch of 500 rows stays well below this cap.
429The rate cap of the key is reached. Retry-After and the X-RateLimit-* family come with the response, with X-RateLimit-Scope: key.Wait the number of seconds given by Retry-After, then try again.
429The rate cap of the brand is reached, across all keys. X-RateLimit-Scope is brand.Wait the number of seconds given by Retry-After. Creating an additional key does not raise this cap.
429The daily quota of the key is reached. The X-Quota-Limit, X-Quota-Remaining and X-Quota-Reset headers come with the response.Wait for midnight universal time to pass, or have the quota of the key raised. The quota is consumed per item: a batch of 100 rows consumes 100 of it.
500The CSV file sent is not encoded in UTF-8.Save the file again in UTF-8. In a spreadsheet, choose the "CSV UTF-8" export.
500Your JSON list contains an element that is not an object, for example a string or a number.Send a list in which every element is an object carrying the fields described above.
500A category_id outside the bounds the database accepts.Send category numbers visible in the console.
500The brand attached to your key cannot be found.Contact us giving the time of the call.
500An unexpected error occurred during the processing of your call.Try again. If the error persists, contact support giving the time of the call.
503The call-capping service is momentarily unavailable. We then refuse the call.Try again in a few moments. No row was processed.

#See also

Your answer opens a pre-filled email in your mail app, addressed to contact@sealtrust.io. You read it over before sending it.

Suggest a correctionReport a problem