Trade Trust Credentials
Issue and verify a verifiable credential through core-engine.
Hand back the signed credential from the matching prepare call and get the finished one.
AWS API Gateway key, required only on the api-sandbox and api-v2 servers. It is checked by the gateway, before the request reaches this service, so it does not substitute for the bearer token - those two headers are read by different layers. Omit it when port-forwarding to a pod.
OAuth2 bearer token from the shared BaaS auth server.
Every endpoint in this subset requires one. Note the security filter runs BEFORE routing, so an unauthenticated request to a path that does not exist also answers 401, not 404 - a 404 is only visible to an authenticated caller.
Request for POST /tradetrust/credentials/complete — hand back the signed credential from
the matching prepare call and get the finished one.
preparationId is single-use and expires (core-engine's pending store has a ~15 minute
TTL); a stale one is a 404, mapped by CustomExceptionHandler's generic core-engine bucket.
OK
Standard BaaS response envelope. Mirrors the shape used across the microservices so callers get a consistent contract on L2 as on L1.
POST /z2/tradetrust/credentials/complete HTTP/1.1
Host: api-sandbox.zetrix.com
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 92
{
"preparationId": "text",
"signedVerifiableCredential": {
"ANY_ADDITIONAL_PROPERTY": "anything"
}
}OK
{
"success": true,
"errorCode": "text",
"message": "text",
"data": {
"verifiableCredential": {
"ANY_ADDITIONAL_PROPERTY": "anything"
}
}
}Issue a credential in one call: prepare -> core-engine signs -> complete. Sends the issuer's private key to core-engine — see this class's javadoc.
AWS API Gateway key, required only on the api-sandbox and api-v2 servers. It is checked by the gateway, before the request reaches this service, so it does not substitute for the bearer token - those two headers are read by different layers. Omit it when port-forwarding to a pod.
OAuth2 bearer token from the shared BaaS auth server.
Every endpoint in this subset requires one. Note the security filter runs BEFORE routing, so an unauthenticated request to a path that does not exist also answers 401, not 404 - a 404 is only visible to an authenticated caller.
Request for POST /tradetrust/credentials/issue — the same build as
{@link PrepareCredentialReqDto PrepareCredentialReqDto}, plus the issuer's key pair for delegated signing
(BT-2620's chosen signing model: prepare -> core-engine's /credentials/sign
-> complete, in one call).
This sends the issuer's private key to core-engine. See {@link MultikeyDto MultikeyDto} and the
SDK's signRemote javadoc for the custody note.
(?i)^(revocation|suspension)$OK
Standard BaaS response envelope. Mirrors the shape used across the microservices so callers get a consistent contract on L2 as on L1.
POST /z2/tradetrust/credentials/issue HTTP/1.1
Host: api-sandbox.zetrix.com
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 414
{
"issuerDid": "text",
"credentialSubject": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"type": [
"text"
],
"context": [
"text"
],
"validFrom": "text",
"expirationDate": "text",
"statusPurpose": "text",
"renderMethod": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"qrCode": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"keyPair": {
"id": "text",
"type": "text",
"controller": "text",
"publicKeyMultibase": "text",
"secretKeyMultibase": "text",
"@context": "text"
}
}OK
{
"success": true,
"errorCode": "text",
"message": "text",
"data": {
"verifiableCredential": {
"ANY_ADDITIONAL_PROPERTY": "anything"
}
}
}Build the unsigned credential and the spec for signing it. The caller signs, then calls complete.
AWS API Gateway key, required only on the api-sandbox and api-v2 servers. It is checked by the gateway, before the request reaches this service, so it does not substitute for the bearer token - those two headers are read by different layers. Omit it when port-forwarding to a pod.
OAuth2 bearer token from the shared BaaS auth server.
Every endpoint in this subset requires one. Note the security filter runs BEFORE routing, so an unauthenticated request to a path that does not exist also answers 401, not 404 - a 404 is only visible to an authenticated caller.
Request for POST /tradetrust/credentials/prepare — build the unsigned credential and the
spec for signing it. issuerDid and credentialSubject are required; everything
else defaults server-side in core-engine.
statusPurpose attaches a status-list entry, which is what later makes the credential
revocable; a plain string here (not the SDK's own enum) so this DTO — and the OpenAPI document
generated from it — stays independent of the SDK's type, per BT-2620's design.
(?i)^(revocation|suspension)$OK
Standard BaaS response envelope. Mirrors the shape used across the microservices so callers get a consistent contract on L2 as on L1.
POST /z2/tradetrust/credentials/prepare HTTP/1.1
Host: api-sandbox.zetrix.com
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 282
{
"issuerDid": "text",
"credentialSubject": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"type": [
"text"
],
"context": [
"text"
],
"validFrom": "text",
"expirationDate": "text",
"statusPurpose": "text",
"renderMethod": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"qrCode": {
"ANY_ADDITIONAL_PROPERTY": "anything"
}
}OK
{
"success": true,
"errorCode": "text",
"message": "text",
"data": {
"preparationId": "text",
"unsignedVerifiableCredential": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"signingSpec": {
"mandatoryPointers": [
"text"
],
"cryptoSuite": "text",
"expectedIssuerDid": "text"
}
}
}Revoke, un-revoke or suspend one credential — revoked chooses the direction and purpose chooses the list, so this one route covers all four.
The caller signs the mutation; this service only forwards it and never handles issuer key material. See {@link RevokeCredentialReqDto RevokeCredentialReqDto} for the exact string to sign.
AWS API Gateway key, required only on the api-sandbox and api-v2 servers. It is checked by the gateway, before the request reaches this service, so it does not substitute for the bearer token - those two headers are read by different layers. Omit it when port-forwarding to a pod.
OAuth2 bearer token from the shared BaaS auth server.
Every endpoint in this subset requires one. Note the security filter runs BEFORE routing, so an unauthenticated request to a path that does not exist also answers 401, not 404 - a 404 is only visible to an authenticated caller.
Request for POST /tradetrust/credentials/revoke — flip one status-list bit, authorized by
the issuer's own signature.
This service cannot revoke on your behalf, by design. core-engine only ever
verifies a status mutation, never signs one, so the authorization has to come from whoever holds
the issuer key. This route is a pass-through: it carries nonce, timestamp and
signature to core-engine and touches no key material at all — unlike
/credentials/issue, no {@link MultikeyDto MultikeyDto} appears here and none may ever be added.
Sign this exact string with the issuer's P-256 key, SHA-256 prehashed, as a 64-byte compact
r‖s pair, hex-encoded:
STATUS-MUTATE|{issuerDid}|{purpose}|{index}|{revoked}|{nonce}|{timestamp}
The Java SDK's CredentialsClient.statusMutationMessage(...) builds it, and
Signatures.derToCompactRs converts a JCA signature to the wire form — SHA256withECDSA
already prehashes and emits DER, and both mistakes yield a well-formed signature that fails with
only "signature invalid" to go on.
One route covers all four operations. revoked chooses the direction, purpose
chooses the list: revocation + true revokes, revocation + false
un-revokes, and suspension does the same to the suspension list. revoked is boxed
and required so an omitted field is a 400 rather than a silent un-revoke.
Two server-side rules make a naive retry fail. timestamp must be within ~60s of
core-engine's clock, and it must be strictly newer than the last accepted mutation for this
index — so a retry has to be re-signed with a fresh timestamp, never replayed.
Which list to write to. Same two values prepare's statusPurpose accepts.
(?i)^(revocation|suspension)$The credential's own credentialStatus.statusListIndex. Read it off the credential
rather than guessing: an unallocated index can be written, and a credential later issued into
it would be born revoked.
Any unique value; it is signed, so it cannot be swapped after the fact.
ISO-8601, and part of the signed message — not a header this service adds.
Hex-encoded 64-byte compact r‖s P-256 signature over the canonical message above.
OK
Standard BaaS response envelope. Mirrors the shape used across the microservices so callers get a consistent contract on L2 as on L1.
POST /z2/tradetrust/credentials/revoke HTTP/1.1
Host: api-sandbox.zetrix.com
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 115
{
"issuerDid": "text",
"purpose": "text",
"index": 1,
"revoked": true,
"nonce": "text",
"timestamp": "text",
"signature": "text"
}OK
{
"success": true,
"errorCode": "text",
"message": "text",
"data": {
"issuerId": "text",
"purpose": "text",
"index": 1,
"revoked": true
}
}Have core-engine sign a prepared credential without completing it — the middle step of prepare -> sign -> complete, for callers who want to see the unsigned document first. Sends the issuer's private key to core-engine, like issue.
AWS API Gateway key, required only on the api-sandbox and api-v2 servers. It is checked by the gateway, before the request reaches this service, so it does not substitute for the bearer token - those two headers are read by different layers. Omit it when port-forwarding to a pod.
OAuth2 bearer token from the shared BaaS auth server.
Every endpoint in this subset requires one. Note the security filter runs BEFORE routing, so an unauthenticated request to a path that does not exist also answers 401, not 404 - a 404 is only visible to an authenticated caller.
Request for POST /tradetrust/credentials/sign — have core-engine apply the issuer's key to
a credential built by an earlier prepare, without completing it.
This is the middle step of prepare -> sign -> complete, exposed on its own.
/credentials/issue already runs all three in one call; the reason to take them separately
is to see the unsigned document (and its signingSpec) before anything is signed, and to
keep the complete call under your own control. The result still has to go to
complete — signing alone does not produce a finished credential.
This sends the issuer's private key to core-engine, exactly as issue does — see
{@link MultikeyDto MultikeyDto}. If that is not acceptable, do not use this route: sign the document from
prepare yourself and call complete directly. That path exists precisely so key
custody can stay on your side.
Requires ALLOW_ISSUER_SIGNING=true on core-engine, which defaults to off. Where it is
off this route answers 400 no matter how well-formed the request is — the same condition that
gates issue and ebl/mint.
preparationId is single-use and expires (~15 minutes), and keyPair.controller
must equal the issuerDid the preparation was created for; core-engine rejects a mismatch
rather than signing with the wrong identity.
OK
Standard BaaS response envelope. Mirrors the shape used across the microservices so callers get a consistent contract on L2 as on L1.
POST /z2/tradetrust/credentials/sign HTTP/1.1
Host: api-sandbox.zetrix.com
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 156
{
"preparationId": "text",
"keyPair": {
"id": "text",
"type": "text",
"controller": "text",
"publicKeyMultibase": "text",
"secretKeyMultibase": "text",
"@context": "text"
}
}OK
{
"success": true,
"errorCode": "text",
"message": "text",
"data": {
"signedVerifiableCredential": {
"ANY_ADDITIONAL_PROPERTY": "anything"
}
}
}The signed Bitstring Status List credential for one issuer and purpose — the whole list, not one credential's state. To check a single credential use /verify, which resolves revocation itself.
AWS API Gateway key, required only on the api-sandbox and api-v2 servers. It is checked by the gateway, before the request reaches this service, so it does not substitute for the bearer token - those two headers are read by different layers. Omit it when port-forwarding to a pod.
OAuth2 bearer token from the shared BaaS auth server.
Every endpoint in this subset requires one. Note the security filter runs BEFORE routing, so an unauthenticated request to a path that does not exist also answers 401, not 404 - a 404 is only visible to an authenticated caller.
OK
Standard BaaS response envelope. Mirrors the shape used across the microservices so callers get a consistent contract on L2 as on L1.
GET /z2/tradetrust/credentials/status/{issuerDid}/{purpose} HTTP/1.1
Host: api-sandbox.zetrix.com
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
OK
{
"success": true,
"errorCode": "text",
"message": "text",
"data": {
"statusListCredential": {
"ANY_ADDITIONAL_PROPERTY": "anything"
}
}
}Check signature, issuer binding and (if declared) revocation. Read the response's valid AND trusted fields — never valid alone.
AWS API Gateway key, required only on the api-sandbox and api-v2 servers. It is checked by the gateway, before the request reaches this service, so it does not substitute for the bearer token - those two headers are read by different layers. Omit it when port-forwarding to a pod.
OAuth2 bearer token from the shared BaaS auth server.
Every endpoint in this subset requires one. Note the security filter runs BEFORE routing, so an unauthenticated request to a path that does not exist also answers 401, not 404 - a 404 is only visible to an authenticated caller.
Request for POST /tradetrust/credentials/verify — check signature, issuer binding and
(if declared) revocation. Open on core-engine's side, but this service still requires its own
caller auth like every other route (no addition to SecurityConfig.PUBLIC_PATHS).
trustedIssuers is optional; when supplied, the response's trusted field
reports whether the credential's issuer is on it.
OK
Standard BaaS response envelope. Mirrors the shape used across the microservices so callers get a consistent contract on L2 as on L1.
POST /z2/tradetrust/credentials/verify HTTP/1.1
Host: api-sandbox.zetrix.com
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 89
{
"verifiableCredential": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"trustedIssuers": [
"text"
]
}OK
{
"success": true,
"errorCode": "text",
"message": "text",
"data": {
"valid": true,
"issuer": "text",
"trusted": true,
"status": "ACTIVE",
"checks": [
{
"type": "text",
"status": "text",
"reason": "text"
}
]
}
}Last updated