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
- What this API does
- The base address and the /v1 prefix
- Authenticating a call
- Getting a key
- Revoking a key
- Authentication refusals
- Your first call
- The permissions attached to a key
- The three limits
- The rate limit, in calls per minute
- The daily quota of the key
- The monthly quota of your plan
- Replaying a call without doubling its effect
- Pagination
- What to remember before opening the reference
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.
| Use | Endpoints |
|---|---|
| Create products in batches, and track the progress of the processing | 2 |
| Report a sale to the end customer | 1 |
| Manage your notification subscriptions | 5 |
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.
POST https://api.sealtrust.io/v1/partner/mint/batch
POST https://api.sealtrust.io/partner/mint/batchUse 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.
Authorization: Bearer votre_clefThat 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
| Code | What happened | What to do |
|---|---|---|
| 401 | the Authorization header is missing | add it, the response also carries WWW-Authenticate: Bearer |
| 401 | the header does not start with Bearer followed by a space | correct the form of the header |
| 401 | the key is malformed or unknown | send the full secret back exactly as the console displayed it, with no space and no line break |
| 403 | the key has been revoked | create a new key in the console |
| 403 | the key has reached its expiry date | create a new key, the old one will never become valid again |
| 403 | the key does not have the permission required by this endpoint | the 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"import { SealTrustClient } from "@sealtrust-io/sdk";
const client = new SealTrustClient({
apiKey: "st_test_0000000000000000000000000000000000000000000000",
});
const page = await client.webhooks.list();
console.log(page.total);
console.log(page.items);import requests
response = requests.get(
"https://api.sealtrust.io/v1/partner/webhooks",
headers={
"Authorization": "Bearer st_test_0000000000000000000000000000000000000000000000"
},
timeout=30,
)
print(response.status_code)
print(response.json())Response, HTTP code 200, for a brand that has no subscription yet:
{
"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.
| Permission | What it opens |
|---|---|
mint:batch | submit a batch of products, and read the progress of a batch |
sellout:write | report a sale to the end customer |
webhooks:read | list your subscriptions, and read a single one |
webhooks:write | create, 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.
| Header | What it contains |
|---|---|
X-RateLimit-Limit | the applicable limit, in calls per window |
X-RateLimit-Remaining | what is left to you in the current window |
X-RateLimit-Reset | the timestamp, in seconds since 1970, at which the window restarts |
X-RateLimit-Scope | key 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.
| Header | What it contains |
|---|---|
X-Quota-Limit | the daily quota of the key |
X-Quota-Remaining | what is left for today |
X-Quota-Reset | the 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:
{
"detail": {
"code": "FEATURE_NOT_AVAILABLE",
"feature": "api_access"
}
}Your plan does not include notification subscriptions:
{
"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:
{
"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.
Idempotency-Key: lot-exemple-0001This 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.
| Parameter | Type | Default value | Description |
|---|---|---|---|
skip | integer | 0 | number of elements to skip, starting from 0 |
limit | integer | 20 | number 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-Afterpoints to the rate,X-Quota-Limitpoints 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
finishedwhen 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-Keyas 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".
- POST
/partner /mint /batch - GET
/partner /mint /batch /status /{job_id} - POST
/partner /sellout - POST
/partner /webhooks - GET
/partner /webhooks - GET
/partner /webhooks /{webhook_id} - PUT
/partner /webhooks /{webhook_id} - DELETE
/partner /webhooks /{webhook_id}
The Errors page gathers the codes common to the whole API.
Was this page helpful?
Your answer opens a pre-filled email in your mail app, addressed to contact@sealtrust.io. You read it over before sending it.