Method GET/brand/{brand_id}/did.json

Retrieve a brand's identity document: the list of its public signing keys, in did:web format. Public endpoint.

On this page

You retrieve the public keys a brand uses to sign its digital product passports. By the end of this page, you will know how to request this document, find in it the key that signed a given credential, and understand what the absence of a key means.

Full address:

HTTP
GET https://api.sealtrust.io/v1/brand/{brand_id}/did.json

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

This document is what a verifier goes and fetches on its own. The signed credential of a passport carries an issuer identifier of the form did:web:api.sealtrust.io:brand:4242. The public rule of the did:web format translates that identifier into the address https://api.sealtrust.io/brand/4242/did.json, that is, this endpoint. Any standard did:web library performs that translation without knowing anything about SealTrust.

#Authorization

None, this is a public endpoint. You send no API key, no session and no origin header. The response is the same for everyone.

The document contains public keys only. No private key ever leaves our system, and none can be reconstructed from this document.

#Rate limit

600 calls per 60-second window, counted per calling network address. This counter is the brand DID document's own: the public keys served under /brand/ have theirs. The /v1 prefix does not create a second budget: /brand/{brand_id}/did.json and /v1/brand/{brand_id}/did.json share the same counter.

Every response carries three headers.

#Path and query parameters

NameTypeRequiredDescription
brand_idintegeryesThe brand's numeric identifier. It is the number that follows brand: in the issuer identifier of the signed credential.

This endpoint has no query parameter.

Here, the brand is named by its number, not by the public code brand_code that the other routes take. This number is part of the did:web identifier already signed into the issued credentials. A DID document must live at the address its own identifier names. So this address does not change.

#Headers

No header is required.

#Request body

None. This request has no body.

#Example request

Identity document of the brand whose identifier is 4242.

curl -i https://api.sealtrust.io/v1/brand/4242/did.json

#Example response

HTTP code 200. The body is JSON, served with the header Content-Type: application/json.

Here, the brand Exemple SAS has rotated its key once. Both versions remain published, so the credentials signed under the older one remain verifiable.

JSON
{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/jws-2020/v1"
  ],
  "id": "did:web:api.sealtrust.io:brand:4242",
  "verificationMethod": [
    {
      "id": "did:web:api.sealtrust.io:brand:4242#key-1",
      "type": "JsonWebKey2020",
      "controller": "did:web:api.sealtrust.io:brand:4242",
      "publicKeyJwk": {
        "kty": "EC",
        "crv": "P-256",
        "x": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
        "y": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      }
    },
    {
      "id": "did:web:api.sealtrust.io:brand:4242#key-2",
      "type": "JsonWebKey2020",
      "controller": "did:web:api.sealtrust.io:brand:4242",
      "publicKeyJwk": {
        "kty": "EC",
        "crv": "P-256",
        "x": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
        "y": "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
      }
    }
  ],
  "assertionMethod": [
    "did:web:api.sealtrust.io:brand:4242#key-1",
    "did:web:api.sealtrust.io:brand:4242#key-2"
  ],
  "authentication": [
    "did:web:api.sealtrust.io:brand:4242#key-1",
    "did:web:api.sealtrust.io:brand:4242#key-2"
  ]
}

The five top-level fields are always present.

FieldTypeDescription
@contextstring[]The two vocabularies that give the document's fields their meaning. Always these two values, in this order.
idstringThe brand's identifier, in did:web format. It is the value carried by the issuer field of a signed credential.
verificationMethodobject[]One entry per published public key, from the oldest version to the most recent.
assertionMethodstring[]The identifiers of the keys allowed to sign a credential. Repeats the same entries as verificationMethod, in the same order.
authenticationstring[]The identifiers of the keys allowed to prove control of this identifier. Repeats the same entries.

#An entry of verificationMethod

FieldTypeDescription
idstringThe key's identifier, of the form <brand identifier>#key-<version number>.
typestringAlways JsonWebKey2020.
controllerstringThe identifier of the brand that controls this key. Always equals the document's id field.
publicKeyJwkobjectThe public key itself, in JWK format.

publicKeyJwk carries four fields: kty equals EC, crv equals P-256, x and y are the two coordinates of the public point, encoded in base64url. These values are passed as they are to a JWS verification library.

#Finding the key that signed a credential

The header of a passport credential carries a kid field. That kid equals exactly one of the id values of verificationMethod, for example did:web:api.sealtrust.io:brand:4242#key-2. You look that value up in the list, you take the matching publicKeyJwk, and you check the signature with the ES256 algorithm.

If the kid is not in the list, the signature must be rejected.

#What the absence of a key means

A revoked key is removed from the document. Every credential signed under that version stops being verifiable, and that is the intended result.

A key replaced by a more recent version stays published. It is no longer used to sign new credentials, and the older ones keep verifying.

verificationMethod can be an empty list, with assertionMethod and authentication empty too. That means the brand has not published any signing key yet. No credential from that brand is verifiable then.

#The id field always matches the address called

This endpoint always returns a document whose id field equals did:web:api.sealtrust.io:brand:<brand_id>, exactly the identifier that the did:web translation rule maps to the address you have just called. You can therefore check that equality, and reject the document if it does not hold.

This stays true even when the brand otherwise carries its identity on its own domain name. In that case it signs its new credentials under did:web:<its domain>, but the credentials it had signed under the identifier above remain verifiable here, forever. That is deliberate: those credentials are on goods already sold, and nobody can re-sign them.

A brand can therefore hold two identity documents at once, at two different addresses, each carrying the identifier of its own address. Nothing stands in the way: every credential names the one that concerns it.

#The same path on a reseller's shared domain

A reseller may serve all of its client brands under one domain, for example passeports.revendeur.com. A client brand that signs under did:web:passeports.revendeur.com:brand:4242 has its identifier translated to https://passeports.revendeur.com/brand/4242/did.json, and this same endpoint answers.

On that domain, the document's id field then equals did:web:passeports.revendeur.com:brand:4242: always the identifier of the address called. And the domain only answers for the brands our database attaches to that reseller. For any other brand it returns 404, even when it exists.

On a brand's own domain, however, this path returns 404 for every identifier, the brand's own included. We issue no identifier of the form did:web:<brand domain>:brand:<id>, so no document served at that address could carry the identifier of the address called. A brand's identity on its own domain is read at /.well-known/did.json.

#Errors

The body of an error response carries a detail field. Every response from this endpoint, successful or in error, carries an X-Request-Id header.

CodeConditionWhat to do
404No brand carries this identifier, or the call arrives on the shared domain of a reseller that does not serve this brand, or on a brand's own domain. detail equals Brand not found.Check the number that follows brand: in the issuer identifier. Reject the signature: an issuer whose identity document cannot be found proves nothing.
422The value sent in the path is not an integer. detail carries the list of validation errors, with the name of the parameter at fault.Correct the identifier. A brand identifier is written in digits only.
422The identity document of the brand found cannot be built.Report it to support, giving the identifier you called. There is nothing you can correct on your side.
500An unexpected error occurred while processing your call. detail equals Internal Server Error.Retry. If the error persists, contact support, giving the value of X-Request-Id.

This endpoint returns neither 401 nor 403: it is public and reads no authorization.

#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