Partner API, overview

Base address, key authentication, permissions, call limits, replaying a call without doubling its effect, pagination and versioning. The page to read before opening the reference.

On this page

By the time you leave this page, you will know how to authenticate a call to the partner API, choose the right address, read the headers that tell you how many calls you have left, replay a request without doubling its effect, and recognize the three limits that can refuse you. This is the page to read once, before opening the reference endpoint by endpoint.

#What this API does

The key-authenticated partner API is there to drive SealTrust from your own system, without going through the console. It has eight endpoints, split across three uses.

UseEndpoints
Create products in batches, and track the progress of the processing2
Report a sale to the end customer1
Manage your notification subscriptions5

Submitting a batch only creates products identified by QR. The server itself sets, on each line of the batch, the identification method and the technical identifier of the item. Your lines carry neither of them, and you cannot ask for any others.

There is no endpoint for listing your products, nor for reading a single one with an API key. You read a product through the public verification endpoints, which require no key.

The partner portal, at the address /partner-portal, is a different surface. It authenticates with the session of a repairer or recycler partner account, and an API key gives no access to it.

#The base address and the /v1 prefix

You call the API at https://api.sealtrust.io.

Every endpoint described on this site exists at two addresses that call exactly the same code: with the /v1 prefix, and with no prefix at all.

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

Use the /v1 form for any new integration.

The reference pages on this site title each endpoint with its path without a prefix, for example POST /partner/mint/batch. Add /v1 in front of that path when you write your call.

The addresses without a prefix are permanent aliases. We attach no end-of-service date to them, and we send no removal announcement header on those responses. If your integration already calls them, it will keep working.

#Authenticating a call

You send your key in the Authorization header, in Bearer format.

HTTP
Authorization: Bearer votre_clef

That is the only authentication mode of this API. There is no URL parameter, no cookie, and no request signature to compute.

#Getting a key

You create your keys from your brand console, under Settings then the Developers tab. The tab stays visible whatever your plan. If your plan does not include API access, the screen is shown locked and you cannot create any key there.

Three points matter at creation time.

  • The full secret is displayed only once, in the window that follows creation. Copy it at that moment. No screen and no endpoint lets you read it again afterwards. We keep only a fingerprint of it.
  • A key belongs to a single brand. We attach every operation made with it to that brand, and to no other.
  • A key always expires. You choose a lifetime at creation. If you do not choose one, it is 365 days. Note the date in your calendar: on the day, your calls stop.

The console then displays the first characters of each key, so that you can recognize it in the list without ever showing its secret again. It also displays the date of last use. We refresh that date at most once a minute, so it can lag one minute behind your last call.

#Revoking a key

You revoke a key from the same page, and the revocation takes effect immediately. The first call that follows is refused. Revoking a key frees a slot if your plan limits the number of active keys.

#Authentication refusals

CodeWhat happenedWhat to do
401the Authorization header is missingadd it, the response also carries WWW-Authenticate: Bearer
401the header does not start with Bearer followed by a spacecorrect the form of the header
401the key is malformed or unknownsend the full secret back exactly as the console displayed it, with no space and no line break
403the key has been revokedcreate a new key in the console
403the key has reached its expiry datecreate a new key, the old one will never become valid again
403the key does not have the permission required by this endpointthe message names the missing permission, see the permissions attached to a key

#Your first call

Check that a key works with the list of your notification subscriptions. This endpoint changes nothing and consumes no quota. It requires the webhooks:read permission.

curl -i https://api.sealtrust.io/v1/partner/webhooks \
  -H "Authorization: Bearer st_test_0000000000000000000000000000000000000000000000"

Response, HTTP code 200, for a brand that has no subscription yet:

JSON
{
  "items": [],
  "total": 0
}

The TypeScript SDK gives you the body of the response, never its headers. To read the limit headers described in The rate limit, in calls per minute, call the API over direct HTTP from your server, or go through curl -i. A browser cannot read them: the only header we expose to the browser is X-Total-Count.

#The permissions attached to a key

Every key carries a list of permissions. A missing permission gets the call refused with a 403, and the refusal message names the permission that is missing.

Four permissions govern access to the endpoints of this API.

PermissionWhat it opens
mint:batchsubmit a batch of products, and read the progress of a batch
sellout:writereport a sale to the end customer
webhooks:readlist your subscriptions, and read a single one
webhooks:writecreate, modify and delete a subscription

#The three limits

Three different counters can refuse a call. They are not alike and they are not read in the same place. Treat them separately.

#The rate limit, in calls per minute

We count your calls over a fixed 60-second window. This limit applies to all eight endpoints, including reading the status of a batch and deleting a subscription.

Two counters run at the same time: one for your key, one for the sum of all the keys of your brand. The limit is the same on both sides.

Your limit comes from a value set on your account, or failing that from your plan. When neither of the two is defined, you get 120 calls per window. Do not guess this value: you read it on every response.

Every response that passes the limit carries the following four headers.

HeaderWhat it contains
X-RateLimit-Limitthe applicable limit, in calls per window
X-RateLimit-Remainingwhat is left to you in the current window
X-RateLimit-Resetthe timestamp, in seconds since 1970, at which the window restarts
X-RateLimit-Scopekey or brand, depending on which of the two counters is the more constraining

These headers always describe the tighter of the two counters. Drive your pace from them, and slow down before reaching zero.

Going over returns a 429 with the same headers, plus Retry-After. Retry-After counts the seconds left in the current window, and is at least 1. Respect it. X-RateLimit-Scope tells you which of the two counters refused, which saves you from looking on the key side when it is the brand total that is full.

One last case, a rare one: if the service that keeps these counters is momentarily unavailable, all eight endpoints reply 503. A 503 means that the call did nothing at all. Try again later.

#The daily quota of the key

Every key can carry a daily quota. A key without a quota is unlimited on that side. The counter restarts from zero when midnight passes in universal time. Your time zone does not come into it.

This quota is counted per line submitted.

  • A batch of 100 lines consumes 100 units.
  • A sale report consumes 1 unit.
  • The six other endpoints consume nothing: the five notification subscription endpoints, and reading the progress of a batch.

Going over returns a 429 with three headers of its own.

HeaderWhat it contains
X-Quota-Limitthe daily quota of the key
X-Quota-Remainingwhat is left for today
X-Quota-Resetthe date of the last reset of the counter

The response also carries the four X-RateLimit-* headers of the rate check, which the call had just passed before being stopped by the quota.

#The monthly quota of your plan

Your plan sets a number of products that can be created per month, over your billing period. It also decides whether API access is open to you, and whether notification subscriptions are open to you.

Three 403 refusals follow from it, all carrying a code readable by your program.

Your plan does not include API access:

JSON
{
  "detail": {
    "code": "FEATURE_NOT_AVAILABLE",
    "feature": "api_access"
  }
}

Your plan does not include notification subscriptions:

JSON
{
  "detail": {
    "code": "FEATURE_NOT_AVAILABLE",
    "feature": "webhooks"
  }
}

That refusal only touches the creation and the modification of a subscription. You keep reading and deletion whatever your plan. You also keep switching a subscription off, on one condition: send is_active to false and nothing else. As soon as a second field accompanies that value, the call counts as a modification and the refusal applies.

You have reached the number of products of your billing period:

JSON
{
  "detail": {
    "code": "QUOTA_EXCEEDED",
    "resource": "products",
    "current": 4800,
    "additional": 300,
    "max": 5000,
    "period": "monthly"
  }
}

This one reads as follows: you have already created 4800 products in the period, you are asking for 300 more, the maximum is 5000. Split your batch or wait for the next period.

#Replaying a call without doubling its effect

Submitting a batch accepts an Idempotency-Key header. This header carries an idempotency key, that is, a value that guarantees that one and the same submission sent twice produces only one processing run. You choose its value, and you keep it for the time of your attempts.

HTTP
Idempotency-Key: lot-exemple-0001

This header answers the case where you get no response: network cut, timeout, restart on your side. You do not know whether the batch went out. Send the same request again with the same value, and you get the response of the first call without a second batch going into processing.

Three behaviors to know.

  • Same idempotency key, same batch: you get back the response of the first call. We do not start a second processing run.
  • Same idempotency key, different batch: the response is 409. We do not give you the response of the first call, because it does not describe what you have just sent.
  • Same idempotency key while the first call is still being processed: the response is 409 as well. Wait for the first call to finish, then try again.

We keep a value for 24 hours. After that delay, the same value becomes a fresh request again, and sending it again would put a second batch into processing. So never use a fixed value: draw a new value per batch, and keep it for the time of the attempts for that batch.

The comparison between two batches is made on their content once normalized. The order of the lines, the order of the columns, the format chosen between CSV and JSON and the cells left empty change nothing: we recognize two submissions of the same content as identical.

A resend that returns the cached response still consumes one call against your rate limit. It consumes neither your daily quota nor the monthly quota of your plan.

#Pagination

Only one endpoint of this API returns a list: GET /v1/partner/webhooks.

ParameterTypeDefault valueDescription
skipinteger0number of elements to skip, starting from 0
limitinteger20number of elements to return, between 1 and 100

The response contains two fields: items, the requested page, and total, the total number of subscriptions of your brand. So you walk the list by increasing skip by the value of limit until you cover total.

You receive the subscriptions from the most recent to the oldest.

#What to remember before opening the reference

  • The address is https://api.sealtrust.io, and the form to use is /v1.
  • The key goes in Authorization: Bearer, and nowhere else.
  • The secret is displayed only once. A key always expires.
  • A 401 speaks about the key itself. A 403 speaks about a permission, a status, or what your plan allows.
  • A 429 comes either from the rate or from the daily quota. Retry-After points to the rate, X-Quota-Limit points to the daily quota.
  • A 503 comes from the service that keeps the rate counters, and means that the call did nothing.
  • The status of a batch replies finished when the execution queue has finished, never to say that the lines succeeded.
  • A batch request sent again after a cut must carry the same Idempotency-Key as the first attempt.

Every endpoint has its own page, with its parameters, its real response and its full table of errors. You find them in the sidebar, arranged by task: "Create products", "Report a sale", "Receive events".

The Errors page gathers the codes common to the whole API.

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