> For the complete documentation index, see [llms.txt](https://docs.zetrix.com/en/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zetrix.com/en/developer-resources/blockchain-as-a-services-baas/tradetrust-api/trade-trust-credentials.md).

# Trade Trust Credentials

Issue and verify a verifiable credential through core-engine.

## Complete a signed credential

> Hand back the signed credential from the matching \<code>prepare\</code> call and get the finished one.

```json
{"openapi":"3.1.0","info":{"title":"myeg-z2-service - TradeTrust","version":"0.0.1"},"tags":[{"name":"TradeTrust Credentials","description":"Issue and verify a verifiable credential through core-engine."}],"servers":[{"url":"https://api-sandbox.zetrix.com/z2","description":"Testnet / UAT, through AWS API Gateway. Needs X-API-Key in addition to the bearer token."}],"security":[{"bearerAuth":[],"apiKeyAuth":[]},{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"OAuth2 bearer token from the shared BaaS auth server.\n\nEvery 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.","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"CompleteCredentialReqDto":{"type":"object","description":"Request for <code>POST /tradetrust/credentials/complete</code> — hand back the signed credential from\n the matching <code>prepare</code> call and get the finished one.\n\n <p><code>preparationId</code> is single-use and expires (core-engine's pending store has a ~15 minute\n TTL); a stale one is a 404, mapped by <code>CustomExceptionHandler</code>'s generic core-engine bucket.","properties":{"preparationId":{"type":"string","minLength":1},"signedVerifiableCredential":{"type":"object","additionalProperties":{},"minProperties":1}},"required":["preparationId","signedVerifiableCredential"]},"ResponseWrapperCompletedCredentialRespDto":{"type":"object","description":"Standard BaaS response envelope. Mirrors the shape used across the microservices so\n callers get a consistent contract on L2 as on L1.","properties":{"success":{"type":"boolean"},"errorCode":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/CompletedCredentialRespDto"}}},"CompletedCredentialRespDto":{"type":"object","description":"Response of <code>POST /tradetrust/credentials/complete</code> (and <code>/issue</code>, which ends at the\n same step) — the assembled, signed credential.","properties":{"verifiableCredential":{"type":"object","additionalProperties":{}}}}}},"paths":{"/tradetrust/credentials/complete":{"post":{"tags":["TradeTrust Credentials"],"summary":"Complete a signed credential","description":"Hand back the signed credential from the matching <code>prepare</code> call and get the finished one.","operationId":"complete","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteCredentialReqDto"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseWrapperCompletedCredentialRespDto"}}}}}}}}}
```

## Issue a credential in one call

> Issue a credential in one call: \<code>prepare\</code> -\&gt; core-engine signs -\&gt; \<code>complete\</code>.\
> &#x20;Sends the issuer's private key to core-engine — see this class's javadoc.

```json
{"openapi":"3.1.0","info":{"title":"myeg-z2-service - TradeTrust","version":"0.0.1"},"tags":[{"name":"TradeTrust Credentials","description":"Issue and verify a verifiable credential through core-engine."}],"servers":[{"url":"https://api-sandbox.zetrix.com/z2","description":"Testnet / UAT, through AWS API Gateway. Needs X-API-Key in addition to the bearer token."}],"security":[{"bearerAuth":[],"apiKeyAuth":[]},{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"OAuth2 bearer token from the shared BaaS auth server.\n\nEvery 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.","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"IssueCredentialReqDto":{"type":"object","description":"Request for <code>POST /tradetrust/credentials/issue</code> — the same build as\n {@link PrepareCredentialReqDto PrepareCredentialReqDto}, plus the issuer's key pair for <b>delegated signing</b>\n (BT-2620's chosen signing model: <code>prepare</code> -&gt; core-engine's <code>/credentials/sign</code>\n -&gt; <code>complete</code>, in one call).\n\n <p><b>This sends the issuer's private key to core-engine.</b> See {@link MultikeyDto MultikeyDto} and the\n SDK's <code>signRemote</code> javadoc for the custody note.","properties":{"issuerDid":{"type":"string","minLength":1},"credentialSubject":{"type":"object","additionalProperties":{},"minProperties":1},"type":{"type":"array","items":{"type":"string"}},"context":{"type":"array","items":{"type":"string"}},"validFrom":{"type":"string"},"expirationDate":{"type":"string"},"statusPurpose":{"type":"string","pattern":"(?i)^(revocation|suspension)$"},"renderMethod":{"type":"object","additionalProperties":{}},"qrCode":{"type":"object","additionalProperties":{}},"keyPair":{"$ref":"#/components/schemas/MultikeyDto"}},"required":["credentialSubject","issuerDid","keyPair"]},"MultikeyDto":{"type":"object","description":"The issuer's Multikey key pair, <b>including its secret</b> — the payload for delegated signing\n (BT-2620: <code>POST /credentials/sign</code> via the SDK's <code>signRemote</code>/<code>issueRemote</code>).\n\n <p>All six fields are required; core-engine's schema rejects a partially built key with a 400\n rather than a partial success, so this DTO enforces the same shape locally via <code>@NotBlank</code>.\n\n <p><b>Carries a private key</b>, exactly like <code>SignBlobReqDto</code>: never logged, never\n persisted, and masked in {@link com.myeg.z2.tradetrust.dto.req.MultikeyDto#toString #toString()} so it cannot leak through request logging, an\n exception message, or a debugger. Every endpoint that accepts this DTO must be POST — a key in a\n query string ends up in access logs.","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","minLength":1},"controller":{"type":"string","description":"Must equal the <code>issuerDid</code> the preparation was created for.","minLength":1},"publicKeyMultibase":{"type":"string","minLength":1},"secretKeyMultibase":{"type":"string","minLength":1},"@context":{"type":"string","minLength":1}},"required":["@context","controller","id","publicKeyMultibase","secretKeyMultibase","type"]},"ResponseWrapperCompletedCredentialRespDto":{"type":"object","description":"Standard BaaS response envelope. Mirrors the shape used across the microservices so\n callers get a consistent contract on L2 as on L1.","properties":{"success":{"type":"boolean"},"errorCode":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/CompletedCredentialRespDto"}}},"CompletedCredentialRespDto":{"type":"object","description":"Response of <code>POST /tradetrust/credentials/complete</code> (and <code>/issue</code>, which ends at the\n same step) — the assembled, signed credential.","properties":{"verifiableCredential":{"type":"object","additionalProperties":{}}}}}},"paths":{"/tradetrust/credentials/issue":{"post":{"tags":["TradeTrust Credentials"],"summary":"Issue a credential in one call","description":"Issue a credential in one call: <code>prepare</code> -&gt; core-engine signs -&gt; <code>complete</code>.\n Sends the issuer's private key to core-engine — see this class's javadoc.","operationId":"issue","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueCredentialReqDto"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseWrapperCompletedCredentialRespDto"}}}}}}}}}
```

## Prepare a credential for signing

> Build the unsigned credential and the spec for signing it. The caller signs, then calls \<code>complete\</code>.

```json
{"openapi":"3.1.0","info":{"title":"myeg-z2-service - TradeTrust","version":"0.0.1"},"tags":[{"name":"TradeTrust Credentials","description":"Issue and verify a verifiable credential through core-engine."}],"servers":[{"url":"https://api-sandbox.zetrix.com/z2","description":"Testnet / UAT, through AWS API Gateway. Needs X-API-Key in addition to the bearer token."}],"security":[{"bearerAuth":[],"apiKeyAuth":[]},{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"OAuth2 bearer token from the shared BaaS auth server.\n\nEvery 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.","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"PrepareCredentialReqDto":{"type":"object","description":"Request for <code>POST /tradetrust/credentials/prepare</code> — build the unsigned credential and the\n spec for signing it. <code>issuerDid</code> and <code>credentialSubject</code> are required; everything\n else defaults server-side in core-engine.\n\n <p><code>statusPurpose</code> attaches a status-list entry, which is what later makes the credential\n revocable; a plain string here (not the SDK's own enum) so this DTO — and the OpenAPI document\n generated from it — stays independent of the SDK's type, per BT-2620's design.","properties":{"issuerDid":{"type":"string","minLength":1},"credentialSubject":{"type":"object","additionalProperties":{},"minProperties":1},"type":{"type":"array","items":{"type":"string"}},"context":{"type":"array","items":{"type":"string"}},"validFrom":{"type":"string"},"expirationDate":{"type":"string"},"statusPurpose":{"type":"string","pattern":"(?i)^(revocation|suspension)$"},"renderMethod":{"type":"object","additionalProperties":{}},"qrCode":{"type":"object","additionalProperties":{}}},"required":["credentialSubject","issuerDid"]},"ResponseWrapperPreparedCredentialRespDto":{"type":"object","description":"Standard BaaS response envelope. Mirrors the shape used across the microservices so\n callers get a consistent contract on L2 as on L1.","properties":{"success":{"type":"boolean"},"errorCode":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PreparedCredentialRespDto"}}},"PreparedCredentialRespDto":{"type":"object","description":"Response of <code>POST /tradetrust/credentials/prepare</code> — the unsigned document plus its\n signing spec.\n\n <p><code>preparationId</code> is single-use and expires (~15 minutes); pass it unmodified to\n <code>complete</code> or <code>issue</code> alongside the signed document.","properties":{"preparationId":{"type":"string"},"unsignedVerifiableCredential":{"type":"object","additionalProperties":{}},"signingSpec":{"$ref":"#/components/schemas/SigningSpecRespDto"}}},"SigningSpecRespDto":{"type":"object","description":"Exactly how core-engine expects a prepared document to be signed — part of\n {@link PreparedCredentialRespDto PreparedCredentialRespDto}. Pass <code>mandatoryPointers</code> to the signer verbatim; core-engine\n derives it from what it actually built, so reconstructing it silently drops fields from the base proof.","properties":{"mandatoryPointers":{"type":"array","items":{"type":"string"}},"cryptoSuite":{"type":"string"},"expectedIssuerDid":{"type":"string"}}}}},"paths":{"/tradetrust/credentials/prepare":{"post":{"tags":["TradeTrust Credentials"],"summary":"Prepare a credential for signing","description":"Build the unsigned credential and the spec for signing it. The caller signs, then calls <code>complete</code>.","operationId":"prepare","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrepareCredentialReqDto"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseWrapperPreparedCredentialRespDto"}}}}}}}}}
```

## Revoke or suspend a credential

> Revoke, un-revoke or suspend one credential — \<code>revoked\</code> chooses the direction and\
> &#x20;\<code>purpose\</code> chooses the list, so this one route covers all four.\
> \
> &#x20;\<p>The caller signs the mutation; this service only forwards it and never handles issuer key\
> &#x20;material. See {@link RevokeCredentialReqDto RevokeCredentialReqDto} for the exact string to sign.

```json
{"openapi":"3.1.0","info":{"title":"myeg-z2-service - TradeTrust","version":"0.0.1"},"tags":[{"name":"TradeTrust Credentials","description":"Issue and verify a verifiable credential through core-engine."}],"servers":[{"url":"https://api-sandbox.zetrix.com/z2","description":"Testnet / UAT, through AWS API Gateway. Needs X-API-Key in addition to the bearer token."}],"security":[{"bearerAuth":[],"apiKeyAuth":[]},{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"OAuth2 bearer token from the shared BaaS auth server.\n\nEvery 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.","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"RevokeCredentialReqDto":{"type":"object","description":"Request for <code>POST /tradetrust/credentials/revoke</code> — flip one status-list bit, authorized by\n the issuer's own signature.\n\n <p><strong>This service cannot revoke on your behalf, by design.</strong> core-engine only ever\n verifies a status mutation, never signs one, so the authorization has to come from whoever holds\n the issuer key. This route is a pass-through: it carries <code>nonce</code>, <code>timestamp</code> and\n <code>signature</code> to core-engine and touches no key material at all — unlike\n <code>/credentials/issue</code>, no {@link MultikeyDto MultikeyDto} appears here and none may ever be added.\n\n <p>Sign this exact string with the issuer's P-256 key, SHA-256 prehashed, as a 64-byte compact\n <code>r‖s</code> pair, hex-encoded:\n\n <pre>STATUS-MUTATE|{issuerDid}|{purpose}|{index}|{revoked}|{nonce}|{timestamp}</pre>\n\n <p>The Java SDK's <code>CredentialsClient.statusMutationMessage(...)</code> builds it, and\n <code>Signatures.derToCompactRs</code> converts a JCA signature to the wire form — <code>SHA256withECDSA</code>\n already prehashes and emits DER, and both mistakes yield a well-formed signature that fails with\n only \"signature invalid\" to go on.\n\n <p>One route covers all four operations. <code>revoked</code> chooses the direction, <code>purpose</code>\n chooses the list: <code>revocation</code> + <code>true</code> revokes, <code>revocation</code> + <code>false</code>\n un-revokes, and <code>suspension</code> does the same to the suspension list. <code>revoked</code> is boxed\n and required so an omitted field is a 400 rather than a silent un-revoke.\n\n <p>Two server-side rules make a naive retry fail. <code>timestamp</code> must be within ~60s of\n core-engine's clock, and it must be strictly newer than the last accepted mutation for this\n <code>index</code> — so a retry has to be re-signed with a fresh timestamp, never replayed.","properties":{"issuerDid":{"type":"string","minLength":1},"purpose":{"type":"string","description":"Which list to write to. Same two values <code>prepare</code>'s <code>statusPurpose</code> accepts.","minLength":1,"pattern":"(?i)^(revocation|suspension)$"},"index":{"type":"integer","format":"int32","description":"The credential's own <code>credentialStatus.statusListIndex</code>. Read it off the credential\n rather than guessing: an unallocated index can be written, and a credential later issued into\n it would be born revoked.","minimum":0},"revoked":{"type":"boolean"},"nonce":{"type":"string","description":"Any unique value; it is signed, so it cannot be swapped after the fact.","minLength":1},"timestamp":{"type":"string","description":"ISO-8601, and part of the signed message — not a header this service adds.","minLength":1},"signature":{"type":"string","description":"Hex-encoded 64-byte compact <code>r‖s</code> P-256 signature over the canonical message above.","minLength":1}},"required":["index","issuerDid","nonce","purpose","revoked","signature","timestamp"]},"ResponseWrapperStatusUpdateRespDto":{"type":"object","description":"Standard BaaS response envelope. Mirrors the shape used across the microservices so\n callers get a consistent contract on L2 as on L1.","properties":{"success":{"type":"boolean"},"errorCode":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/StatusUpdateRespDto"}}},"StatusUpdateRespDto":{"type":"object","description":"Response of <code>POST /tradetrust/credentials/revoke</code> — the status-list entry's state after the\n write, echoed back from core-engine.\n\n <p>Every field is an echo of what was applied, not a fresh read, so treat it as confirmation that\n the mutation was accepted rather than as the list's current contents. The bit is visible to\n verifiers on the next <code>GET</code> of the status list, which core-engine signs fresh on each\n request — there is no cache to wait out.","properties":{"issuerId":{"type":"string"},"purpose":{"type":"string"},"index":{"type":"integer","format":"int32"},"revoked":{"type":"boolean"}}}}},"paths":{"/tradetrust/credentials/revoke":{"post":{"tags":["TradeTrust Credentials"],"summary":"Revoke or suspend a credential","description":"Revoke, un-revoke or suspend one credential — <code>revoked</code> chooses the direction and\n <code>purpose</code> chooses the list, so this one route covers all four.\n\n <p>The caller signs the mutation; this service only forwards it and never handles issuer key\n material. See {@link RevokeCredentialReqDto RevokeCredentialReqDto} for the exact string to sign.","operationId":"revoke","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeCredentialReqDto"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseWrapperStatusUpdateRespDto"}}}}}}}}}
```

## Sign a prepared credential

> Have core-engine sign a prepared credential without completing it — the middle step of\
> &#x20;\<code>prepare\</code> -\&gt; sign -\&gt; \<code>complete\</code>, for callers who want to see the unsigned\
> &#x20;document first. Sends the issuer's private key to core-engine, like \<code>issue\</code>.

```json
{"openapi":"3.1.0","info":{"title":"myeg-z2-service - TradeTrust","version":"0.0.1"},"tags":[{"name":"TradeTrust Credentials","description":"Issue and verify a verifiable credential through core-engine."}],"servers":[{"url":"https://api-sandbox.zetrix.com/z2","description":"Testnet / UAT, through AWS API Gateway. Needs X-API-Key in addition to the bearer token."}],"security":[{"bearerAuth":[],"apiKeyAuth":[]},{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"OAuth2 bearer token from the shared BaaS auth server.\n\nEvery 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.","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"SignCredentialReqDto":{"type":"object","description":"Request for <code>POST /tradetrust/credentials/sign</code> — have core-engine apply the issuer's key to\n a credential built by an earlier <code>prepare</code>, without completing it.\n\n <p>This is the middle step of <code>prepare</code> -&gt; sign -&gt; <code>complete</code>, exposed on its own.\n <code>/credentials/issue</code> already runs all three in one call; the reason to take them separately\n is to see the unsigned document (and its <code>signingSpec</code>) before anything is signed, and to\n keep the <code>complete</code> call under your own control. The result still has to go to\n <code>complete</code> — signing alone does not produce a finished credential.\n\n <p><b>This sends the issuer's private key to core-engine</b>, exactly as <code>issue</code> does — see\n {@link MultikeyDto MultikeyDto}. If that is not acceptable, do not use this route: sign the document from\n <code>prepare</code> yourself and call <code>complete</code> directly. That path exists precisely so key\n custody can stay on your side.\n\n <p>Requires <code>ALLOW_ISSUER_SIGNING=true</code> on core-engine, which defaults to off. Where it is\n off this route answers 400 no matter how well-formed the request is — the same condition that\n gates <code>issue</code> and <code>ebl/mint</code>.\n\n <p><code>preparationId</code> is single-use and expires (~15 minutes), and <code>keyPair.controller</code>\n must equal the <code>issuerDid</code> the preparation was created for; core-engine rejects a mismatch\n rather than signing with the wrong identity.","properties":{"preparationId":{"type":"string","minLength":1},"keyPair":{"$ref":"#/components/schemas/MultikeyDto"}},"required":["keyPair","preparationId"]},"MultikeyDto":{"type":"object","description":"The issuer's Multikey key pair, <b>including its secret</b> — the payload for delegated signing\n (BT-2620: <code>POST /credentials/sign</code> via the SDK's <code>signRemote</code>/<code>issueRemote</code>).\n\n <p>All six fields are required; core-engine's schema rejects a partially built key with a 400\n rather than a partial success, so this DTO enforces the same shape locally via <code>@NotBlank</code>.\n\n <p><b>Carries a private key</b>, exactly like <code>SignBlobReqDto</code>: never logged, never\n persisted, and masked in {@link com.myeg.z2.tradetrust.dto.req.MultikeyDto#toString #toString()} so it cannot leak through request logging, an\n exception message, or a debugger. Every endpoint that accepts this DTO must be POST — a key in a\n query string ends up in access logs.","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","minLength":1},"controller":{"type":"string","description":"Must equal the <code>issuerDid</code> the preparation was created for.","minLength":1},"publicKeyMultibase":{"type":"string","minLength":1},"secretKeyMultibase":{"type":"string","minLength":1},"@context":{"type":"string","minLength":1}},"required":["@context","controller","id","publicKeyMultibase","secretKeyMultibase","type"]},"ResponseWrapperSignedCredentialRespDto":{"type":"object","description":"Standard BaaS response envelope. Mirrors the shape used across the microservices so\n callers get a consistent contract on L2 as on L1.","properties":{"success":{"type":"boolean"},"errorCode":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/SignedCredentialRespDto"}}},"SignedCredentialRespDto":{"type":"object","description":"Response of <code>POST /tradetrust/credentials/sign</code> — the signed (and derived) credential.\n\n <p>Deliberately a separate type from {@link CompletedCredentialRespDto CompletedCredentialRespDto} despite the identical\n shape: that one is a finished credential, this one is <strong>not</strong>. Pass this document,\n unmodified, to <code>complete</code> together with the same <code>preparationId</code> to get the finished\n one. Nothing has been recorded anywhere until you do.","properties":{"signedVerifiableCredential":{"type":"object","additionalProperties":{}}}}}},"paths":{"/tradetrust/credentials/sign":{"post":{"tags":["TradeTrust Credentials"],"summary":"Sign a prepared credential","description":"Have core-engine sign a prepared credential without completing it — the middle step of\n <code>prepare</code> -&gt; sign -&gt; <code>complete</code>, for callers who want to see the unsigned\n document first. Sends the issuer's private key to core-engine, like <code>issue</code>.","operationId":"sign","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignCredentialReqDto"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseWrapperSignedCredentialRespDto"}}}}}}}}}
```

## Get the status list

> The signed Bitstring Status List credential for one issuer and purpose — the whole list, not\
> &#x20;one credential's state. To check a single credential use \<code>/verify\</code>, which resolves\
> &#x20;revocation itself.

```json
{"openapi":"3.1.0","info":{"title":"myeg-z2-service - TradeTrust","version":"0.0.1"},"tags":[{"name":"TradeTrust Credentials","description":"Issue and verify a verifiable credential through core-engine."}],"servers":[{"url":"https://api-sandbox.zetrix.com/z2","description":"Testnet / UAT, through AWS API Gateway. Needs X-API-Key in addition to the bearer token."}],"security":[{"bearerAuth":[],"apiKeyAuth":[]},{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"OAuth2 bearer token from the shared BaaS auth server.\n\nEvery 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.","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"ResponseWrapperStatusListRespDto":{"type":"object","description":"Standard BaaS response envelope. Mirrors the shape used across the microservices so\n callers get a consistent contract on L2 as on L1.","properties":{"success":{"type":"boolean"},"errorCode":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/StatusListRespDto"}}},"StatusListRespDto":{"type":"object","description":"Response of <code>GET /tradetrust/credentials/status/{issuerDid</code>/{purpose}} — the signed Bitstring\n Status List credential for one issuer and purpose.\n\n <p>This is the document verifiers fetch to decide whether a credential has been revoked. It is\n signed by core-engine's own status-responder identity, deliberately separate from any issuer key:\n the issuer authorizes each individual bit flip, core-engine signs the aggregate.\n\n <p>Reading it is not how you check one credential — <code>POST /credentials/verify</code> already\n resolves revocation for you. Fetch this when you want the whole picture, e.g. to show an issuer\n everything they have revoked.","properties":{"statusListCredential":{"type":"object","additionalProperties":{}}}}}},"paths":{"/tradetrust/credentials/status/{issuerDid}/{purpose}":{"get":{"tags":["TradeTrust Credentials"],"summary":"Get the status list","description":"The signed Bitstring Status List credential for one issuer and purpose — the whole list, not\n one credential's state. To check a single credential use <code>/verify</code>, which resolves\n revocation itself.","operationId":"statusList","parameters":[{"name":"issuerDid","in":"path","required":true,"schema":{"type":"string"}},{"name":"purpose","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseWrapperStatusListRespDto"}}}}}}}}}
```

## Verify a credential

> Check signature, issuer binding and (if declared) revocation. Read the response's\
> &#x20;\<code>valid\</code> AND \<code>trusted\</code> fields — never \<code>valid\</code> alone.

```json
{"openapi":"3.1.0","info":{"title":"myeg-z2-service - TradeTrust","version":"0.0.1"},"tags":[{"name":"TradeTrust Credentials","description":"Issue and verify a verifiable credential through core-engine."}],"servers":[{"url":"https://api-sandbox.zetrix.com/z2","description":"Testnet / UAT, through AWS API Gateway. Needs X-API-Key in addition to the bearer token."}],"security":[{"bearerAuth":[],"apiKeyAuth":[]},{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"OAuth2 bearer token from the shared BaaS auth server.\n\nEvery 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.","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"VerifyCredentialReqDto":{"type":"object","description":"Request for <code>POST /tradetrust/credentials/verify</code> — check signature, issuer binding and\n (if declared) revocation. Open on core-engine's side, but this service still requires its own\n caller auth like every other route (no addition to <code>SecurityConfig.PUBLIC_PATHS</code>).\n\n <p><code>trustedIssuers</code> is optional; when supplied, the response's <code>trusted</code> field\n reports whether the credential's issuer is on it.","properties":{"verifiableCredential":{"type":"object","additionalProperties":{},"minProperties":1},"trustedIssuers":{"type":"array","items":{"type":"string"}}},"required":["verifiableCredential"]},"ResponseWrapperVerifyCredentialRespDto":{"type":"object","description":"Standard BaaS response envelope. Mirrors the shape used across the microservices so\n callers get a consistent contract on L2 as on L1.","properties":{"success":{"type":"boolean"},"errorCode":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/VerifyCredentialRespDto"}}},"VerifyCredentialRespDto":{"type":"object","description":"Response of <code>POST /tradetrust/credentials/verify</code> — returned for both outcomes; a failed\n verification is a value (<code>valid: false</code>), never an error.\n\n <p><b>Read <code>valid</code> AND <code>trusted</code> — never <code>valid</code> alone.</b> <code>valid</code> means\n cryptographically genuine, issuer-bound and not revoked/suspended; it says nothing about whether\n the issuer should be accepted. <code>trusted</code> answers that, and is only populated when the\n request supplied <code>trustedIssuers</code> — otherwise it stays <code>null</code>, which is not the same\n as <code>false</code>.","properties":{"valid":{"type":"boolean"},"issuer":{"type":"string"},"trusted":{"type":"boolean"},"status":{"type":"string","description":"<code>null</code> when the credential carries no <code>credentialStatus</code> entry.","enum":["ACTIVE","REVOKED","SUSPENDED","UNKNOWN"]},"checks":{"type":"array","items":{"$ref":"#/components/schemas/VerifyCheckRespDto"}}}},"VerifyCheckRespDto":{"type":"object","description":"One step of a verification, e.g. <code>signature</code>, <code>status</code>, <code>trusted-issuer</code>.","properties":{"type":{"type":"string"},"status":{"type":"string","description":"<code>&quot;passed&quot;</code> or <code>&quot;failed&quot;</code>."},"reason":{"type":"string","description":"Why the check failed; <code>null</code> when it passed."}}}}},"paths":{"/tradetrust/credentials/verify":{"post":{"tags":["TradeTrust Credentials"],"summary":"Verify a credential","description":"Check signature, issuer binding and (if declared) revocation. Read the response's\n <code>valid</code> AND <code>trusted</code> fields — never <code>valid</code> alone.","operationId":"verify_1","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyCredentialReqDto"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseWrapperVerifyCredentialRespDto"}}}}}}}}}
```
