Method GET/passport/{identifier}/vc

Retrieve the signed credential of a passport, in SD-JWT-VC format, filtered to the access tier asked for. Public endpoint at the public and end_user tiers.

On this page

You retrieve the passport of a product as a digital credential signed by the brand. When you leave this page, you will know how to ask for that credential at the access tier that concerns you, how to read the six fields of the response, and where to find the public key that lets you check its signature without trusting us.

Full address:

HTTP
GET https://api.sealtrust.io/v1/passport/{identifier}/vc

The same endpoint also answers without the /v1 prefix, at https://api.sealtrust.io/passport/{identifier}/vc. Both addresses call the same code. Use the /v1 form for a new integration.

The response is a JSON object of six fields. The passport itself sits in a single field, as a character string signed in the SD-JWT-VC format. To read the passport data as directly usable JSON, call GET /v1/passport/{identifier}.

#Authorization

None for the public and end_user tiers. This endpoint is public at those two tiers.

A partner API key opens nothing here. The tiers that ask for an identity are opened with a user account session token, never with an API key.

Four values of the access_tier parameter require an account session, which you present through the Authorization: Bearer <session token> header or through the session cookie set at sign-in.

Tier asked forWhat it takes
publicnothing
end_usernothing
repairera session, and an active repairer accreditation on the brand of the product, or access to that brand, or the authority role
recyclera session, and an active recycler accreditation on the brand of the product, or access to that brand, or the authority role
upstreama session with access to the brand of the product, or the authority role. No accreditation opens this tier.
authoritya session carrying the market surveillance authority role

A session with access to the brand of the product opens the three professional tiers on the products of that brand.

The access tier rules are the same as those of the GET /v1/passport/{identifier} endpoint. This path therefore never gives access to more fields than the JSON read.

One difference separates the two paths. This endpoint only serves the passports whose visibility is public. A passport reserved for the owner or reserved for the brand is never returned here, not even to its owner.

#The origin check

Your server-to-server calls go through as they are. Two situations give a 403.

A call issued by a web page opened on a domain that is not ours carries an Origin or Referer header that we refuse. So do not call this endpoint from the browser of a visitor, call it from your server.

A call that carries the session cookie without an Origin or Referer header is refused as well. The session cookie only holds from a page served by one of our domains. From a server or from the command line, present the token in Authorization: Bearer.

#Rate limit

60 calls per 60-second slice, counted per calling network address. The window is fixed.

That counter is shared by every path that starts with /passport. The calls you address to one of them eat into the budget of the others. The /v1 prefix does not create a second budget: /v1/passport/EXEMP1E00001/vc and /passport/EXEMP1E00001/vc fill the same counter.

Every accepted response carries three headers.

HeaderContent
X-RateLimit-Limitthe limit applied over the window, here 60
X-RateLimit-Remainingwhat is left to you in the current window
X-RateLimit-Resetthe timestamp of the end of the window, in seconds since January 1, 1970

Going over returns 429, with those three headers and Retry-After. On this endpoint, Retry-After is the duration of the window, that is 60 seconds.

#Path and query parameters

NameTypeRequiredDescription
identifierstringyesThe product whose credential you want. Three forms are accepted, see below.
access_tierstringnoThe access tier asked for. Defaults to public. Six accepted values, listed further down.

No header is required in the request.

#The three accepted forms of identifier

FormWhat it looks likeWhere you find it
Serial number12 characters, digits and uppercase letters. The letters I, L, O and U never appear in it.Printed on the product, it is what its QR code carries
Token identifierA run of digits, often very longReturned by our responses in the token_id field
Identifier hash0x followed by 64 hexadecimal charactersReturned by our responses in the uid_hash field

The identifier hash exists for a QR-only product just as for a product with an NFC chip. The server draws it at random for a QR product, it derives it from the identifier of the chip for an NFC product.

The server recognizes the form from the way it is written. It looks up a value that starts with 0x and is exactly 66 characters long as an identifier hash. It looks up any other value first as a token identifier. It tries the serial number last, when the first two lookups have returned nothing.

The server recognizes the identifier hash whatever the case. It also recognizes the serial number whatever the case, and it canonicalizes it the way the QR resolver does: it reads the letters I and L as a 1, the letter O as a 0. You can therefore send it a number copied by hand from a label.

This endpoint only resolves the products still in the catalog of the brand. An item destroyed on chain, replaced by a later version or withdrawn without replacement answers 404.

#The six values of access_tier

These tiers are different audiences, with no hierarchy between them. A recycler is not above a repairer. Each of the three professional tiers inherits the public tier and the end user tier, then adds what its trade calls for.

ValueWhat the credential reveals
publicproduct identification, ESPR compliance, REACH and CE marking, recyclability percentage, recycled content percentage, labels, general battery specification. These fields appear in the clear in the signed token, no disclosure is attached.
end_userenvironmental impact, full circularity, primary material, certified organic material, durability, energy efficiency, carbon footprint
repairerbill of materials, dismantling instructions, repairability index, battery state of health
recyclermaterial composition, substances of concern, dismantling instructions, battery state of health
upstreammaterial composition, substances of concern, manufacturing, supply chain
authorityevery field of the document

A brand can replace these rules with its own. The table above describes what applies in the absence of rules of the brand's own. The same rules apply here and on the JSON read.

The server refuses any other value with 422. It returns the tier actually served in the access_tier field of the response and in the X-DPP-Access-Tier header. Read one of the two rather than assuming it.

#Request body

None. This request has no body.

#Example request

Public credential of the product whose printed number is EXEMP1E00001.

curl -s "https://api.sealtrust.io/v1/passport/EXEMP1E00001/vc?access_tier=public"

The three tabs call the same address with the same values. The curl tab writes the raw response to standard output. The TypeScript and Python tabs extract the same fields from it, in the same order.

#Example response

HTTP status code 200.

The values below are fictitious. The signed token and the disclosure segments are shortened, a real token is several thousand characters long.

JSON
{
  "passport_id": 1,
  "issuer": "did:web:api.sealtrust.io:brand:4242",
  "vct": "https://schema.sealtrust.io/vct/digital-product-passport",
  "access_tier": "public",
  "format": "dc+sd-jwt",
  "sd_jwt_vc": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImRjK3NkLWp3dCIsImtpZCI6ImRpZDp3ZWI6YXBpLnNlYWx0cnVzdC5pbzpicmFuZDo0MjQyI2tleS0xIn0.RVhFTVBMRV9DSEFSR0VfVVRJTEU.RVhFTVBMRV9TSUdOQVRVUkU~"
}
FieldTypePresenceDescription
passport_idintegeralwaysThe identifier of the passport version this credential relates to.
issuerstringalwaysThe did:web decentralized identifier of the brand that signed. It is what leads to the public verification key. Always filled in on this endpoint.
vctstringalwaysThe identifier of the credential template. Is https://schema.sealtrust.io/vct/digital-product-passport in the absence of a value recorded on the passport.
access_tierstringalwaysThe tier actually served.
formatstringalwaysAlways dc+sd-jwt. It is the media type of the selective disclosure credential.
sd_jwt_vcstringalwaysThe credential itself. See below.

#Response headers worth knowing

HeaderContent
X-DPP-Access-Tierthe tier actually served
Cache-Controlno-store, max-age=0, whatever the tier served. Do not put this response behind any shared cache.

#Reading the sd_jwt_vc field

The content of sd_jwt_vc is a run of segments separated by the ~ character. The last segment is always empty, so the string ends with a ~.

Texte
<jeton signé>~<divulgation>~<divulgation>~

The first segment is a signed token in three parts, separated by dots. The header and the payload are encoded in base64url, without padding. You decode them without a key.

The header carries three values.

ValueContent
algES256. The signature is an ECDSA signature on curve P-256.
typdc+sd-jwt
kidThe identifier of the key that signed, in the form <brand did>#key-<version number>.

The payload carries the following fields.

FieldContent
issThe did:web identifier of the issuing brand. It holds the same value as the issuer field of the response.
vctThe identifier of the credential template.
iatThe issuance date, in seconds since January 1, 1970.
@context["https://www.w3.org/ns/credentials/v2", "https://schema.sealtrust.io/dpp/v1"]
type["VerifiableCredential", "DigitalProductPassport"]
issuerA repeat of iss, expected by the verifiable credentials data model.
validFromThe issuance date in ISO 8601 format, to the second, in universal time.
credentialSubjectThe passport data. The public fields appear in the clear in it. The others are replaced by hashes, under the _sd key.
credentialSchemaAn object with two keys, id which repeats vct, and type which is JsonSchema.
productThe identity of the product: uid_hash, token_id and name. Never masked.
brandThe identity of the brand: name, lei_code, eori_number, website_url, postal_address, contact_email. Never masked. The values that are not filled in are absent.

A passport can cover a product model or one specific item. When the brand publishes a model passport, it holds for every item that shares the same product code, and the credential issued at that publication carries uid_hash and token_id at null in the product block: it designates no item in particular.

The following segments are the disclosures. Each one is an array of three elements encoded in base64url: a salt, the name of the field, its value. You decode them without a key. Their number depends on the tier asked for.

Texte
WyJFWEVNUExFMDAwMDAwMDAwMDAwMDAwMCIsInJlcGFpcmFiaWxpdHlfaW5kZXgiLDguMl0

That example segment decodes to ["EXEMPLE0000000000000000", "repairability_index", 8.2].

At the public tier, the response reveals nothing more than the fields always present in the signed document. The string is then reduced to the signed token followed by a ~. Each other tier adds the disclosures that concern it.

#Check the signature yourself

The issuer field carries a did:web identifier. It designates a public document that contains the public keys of the brand, expressed as JsonWebKey2020. The key to use is the one whose identifier matches the kid of the token header. That document contains only the keys that are not revoked, so a revoked key no longer appears in it.

An identifier of the form did:web:api.sealtrust.io:brand:4242 resolves to https://api.sealtrust.io/brand/4242/did.json. An identifier of the form did:web:id.exemple-sas.example resolves to https://id.exemple-sas.example/.well-known/did.json. A brand can host that document itself on its own domain, in which case the verification of its passports depends on none of our servers.

If you prefer the verification to be done for you, call GET /v1/passport/{identifier}/vc/verify.

#Errors

The body of an error response carries a detail field.

CodeConditionWhat to do
401You ask for authority without a session. detail is Authority-tier access requires authentication.Sign in, then present the session token in Authorization: Bearer.
401You ask for repairer, recycler or upstream without a session. detail is Professional-tier access requires authentication.Sign in, then present the session token in Authorization: Bearer. An API key will not do.
403You ask for authority with a session that does not carry that role. detail is Authority-tier access is restricted to market surveillance authorities.Ask for a tier that matches your situation.
403You ask for a professional tier without an active accreditation on the brand of the product, without access to that brand and without the authority role. detail starts with This tier is restricted to the product's brand.Ask the brand to accredit you, then ask again for the tier that matches your trade.
403The call carries an Origin or Referer header that does not designate one of our domains, which happens for any call issued from a web page hosted elsewhere. detail is Forbidden origin.Call this endpoint from your server, never from the browser of a visitor.
403The call carries a session cookie without an Origin or Referer header. detail is Origin or Referer header required.Present the token in Authorization: Bearer instead of the session cookie.
404No product in the catalog matches this identifier, under any of the three accepted forms. detail is Product not found.Check the identifier. An item destroyed on chain, replaced by a later version or withdrawn gives this same response.
404The product exists, but no passport published with public visibility is attached to it, neither directly nor through its model. detail is No published passport found for this product.Do not treat this response as a failure. This product has no public passport. A passport reserved for the owner or for the brand gives the same response.
404The passport exists and it is public, but no signed credential has been issued for it yet. detail starts with No VC issued for this passport yet.Read the passport in JSON with GET /v1/passport/{identifier}. Publishing a version of a passport issues its credential: ask the brand to republish the current version.
404The brand of the passport is not resolvable. detail is Brand not found.Report it to support. No action on your side changes this response.
422The value of access_tier is not one of the six accepted. detail is a list of objects that name the parameter at fault.Correct the value. The six accepted values are listed above.
429The limit of 60 calls per 60 seconds is reached for your network address, across every /passport path. detail is Rate limit exceeded: 60 requests per 60s.Wait the number of seconds indicated by Retry-After, then try again. Cache the response on your side.
500An unexpected error occurred while your call was being processed. detail is Internal Server Error. The response carries an X-Request-Id header.Try again. If the error persists, contact support quoting the value of X-Request-Id.

#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