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

This endpoint has no limit of its own. It shares a general counter with the other routes that have no limit of their own, counted per calling network address over a 60 second window.

Handle the 429 code in your client and honor the Retry-After header it carries. The value of the general counter can change without notice, so do not hard-code any number in your code.

The response carries three headers that describe this counter.

HeaderContents
X-RateLimit-Limitthe limit the general counter announces for the window
X-RateLimit-Remainingwhat is left to you in the current window
X-RateLimit-Resetthe timestamp of the end of the window, in seconds

This document changes only when the brand rotates its signing keys, which is rare. Keep a copy on your side. Refresh it when a credential carries a kid that your copy does not contain.

#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.

#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.

#When the id field does not match the address called

A brand can carry its identity on its own domain name. The document's id field then equals did:web:<its domain>, and the reference document is found at https://<its domain>/.well-known/did.json.

In that case, take the id field of the credential you are verifying as your starting point, apply the did:web translation rule, and go and fetch the document at the resulting address. Never build the address yourself from the brand identifier.

#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. 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