{
  "openapi": "3.1.0",
  "info": {
    "title": "myeg-z2-service - TradeTrust",
    "description": "TradeTrust subset of `openapi.json` - the Credentials and eBL endpoints of myeg-ms-z2, plus the `/tx/**` transaction lifecycle they hand off to, and the schema closure all of that reaches.\n\n**Getting an eBL write onto the chain - the full sequence.** Every eBL write (`transfer-holder`, `surrender`, `nominate`, the rejects) and `complete-mint` return an **unsigned** transaction: `{ to, data, value, chainId }`. Nothing has happened on chain yet. Four more calls finish it:\n\n1. `POST /tx/generate-blob` - `{ from, to, data, value }` from the unsigned transaction, returning the RLP-encoded `blob`. **This step is required and easy to miss.** `POST /tx/sign-blob` takes a `blob` string, *not* the `{to, data, value, chainId}` object the eBL route handed you, so passing the unsigned transaction straight to it fails. Ignore any description in this file that suggests going directly from an eBL route to `sign-blob`.\n2. `POST /tx/sign-blob` - `{ blob, privateKey }`. Or sign the blob yourself and skip this call, if you would rather the key never left your process.\n3. `POST /tx/submit` - `{ signedBlob }`, which broadcasts and returns the transaction hash.\n4. `GET /tx/status?hash=...` - poll until `SUCCESS`. A hash the node has not seen yet reads as `PENDING`, not 404.\n\n`POST /tx/estimate-fee` is here too, since the signing address pays the gas.\n\nDERIVED, NEVER HAND-EDITED: run `python scripts/gen-tradetrust-openapi.py` after regenerating `openapi.json`. Operation summaries and descriptions are carried across verbatim, Javadoc residue (`{@link ...}`, HTML entities) included, so this file cannot drift in wording from the spec the application is checked against.\n\n**Two things that catch callers out on these paths:**\n\n1. **Large integers travel as JSON strings, not numbers.** Token ids are `uint256`. Anything above 2^53 is silently rounded by `JSON.parse` itself, before your code runs - and a rounded token id addresses a different token. `chainId` stays numeric, being protocol-bounded.\n\n2. **A private key is never stored, logged or persisted** - but the `keyPair` on the one-call `credentials/issue` and `ebl/mint` bodies does reach this service and is forwarded to core-engine to sign, and Java cannot zero the String or the key material derived from it, so its disposal is garbage-collector dependent. Those two paths are a convenience form. The prepare/complete split is the one that keeps key custody on your side - `prepare` and `prepare-mint` take no `keyPair` at all: sign the returned credential yourself and hand the signature back to `complete` / `complete-mint`.\n\n**Errors** use a fixed envelope with HTTP 200 for business failures, so clients handle them uniformly. The two deliberate exceptions are transport facts rather than business outcomes: an unknown path is 404 and a wrong verb is 405, because gateways, load balancers and monitoring all read the status line for those.\n\nIn staging and production /v3/api-docs and the Swagger UI are disabled on purpose, so this file and openapi.json are the API documentation there.\n\n**Operation titles.** Every operation carries a short `summary` as its title, with the full javadoc text in `description`. The titles are the one part of this file NOT carried verbatim from `openapi.json`, where springdoc derives `summary` from a javadoc's first sentence - so it runs to 200 characters, carries HTML, and is absent entirely on the `/tx/**` writes.",
    "contact": {
      "name": "ZETRIX Developer",
      "email": "dev@zetrix.com"
    },
    "license": {
      "name": "Proprietary"
    },
    "version": "0.0.1"
  },
  "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": []
    }
  ],
  "tags": [
    {
      "name": "TradeTrust Credentials",
      "description": "Issue and verify a verifiable credential through core-engine."
    },
    {
      "name": "TradeTrust Finality",
      "description": "How final an L2 transaction is, via core-engine."
    },
    {
      "name": "TradeTrust eBL",
      "description": "Mint, verify, and move a transferable record (eBL) through core-engine."
    },
    {
      "name": "Transaction",
      "description": "Build, sign, submit and track a raw transaction, plus fee estimation. The blob/sign/submit split lets a caller keep key custody."
    }
  ],
  "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"
                }
              }
            }
          }
        }
      }
    },
    "/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"
                }
              }
            }
          }
        }
      }
    },
    "/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"
                }
              }
            }
          }
        }
      }
    },
    "/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"
                }
              }
            }
          }
        }
      }
    },
    "/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"
                }
              }
            }
          }
        }
      }
    },
    "/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"
                }
              }
            }
          }
        }
      }
    },
    "/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"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/complete-mint": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Complete and mint an eBL",
        "description": "Hand back the signed record and get the mint transaction — still unsent; see this class's javadoc.",
        "operationId": "completeMint",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblCompleteMintReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperEblCompleteMintRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/mint": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Mint an eBL in one call",
        "description": "Mint a transferable record in one call: <code>prepare-mint</code> -&gt; core-engine signs -&gt;\n <code>complete-mint</code>. One step still to do: sign and broadcast the returned\n <code>unsignedMintTx</code>. Sends the issuer's private key to core-engine — see\n {@link com.myeg.z2.tradetrust.dto.req.MultikeyDto com.myeg.z2.tradetrust.dto.req.MultikeyDto}.",
        "operationId": "mint",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblMintReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperEblCompleteMintRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/onboard-issuer": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Deploy a Token Registry",
        "description": "Deploy a brand-new Token Registry — where an eBL flow starts, since <code>prepare-mint</code> and\n <code>mint</code> both require a <code>tokenRegistry</code> and there is no default.\n\n <p>Unsigned like every other write: the paying address signs and broadcasts it, and needs gas.\n Afterwards read the new registry's address from <code>/onboard-issuer/registry</code> — it is not in\n the receipt's <code>contractAddress</code>.",
        "operationId": "onboardIssuer",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardIssuerReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/onboard-issuer/registry": {
      "get": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Get the deployed registry address",
        "description": "The address of the registry an <code>onboard-issuer</code> transaction deployed, decoded from the\n receipt's <code>Deployment</code> event. Poll while <code>PENDING</code>, exactly as with\n <code>/tx/status</code>.",
        "operationId": "registryAddress",
        "parameters": [
          {
            "name": "hash",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperRegistryAddressRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/prepare-mint": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Prepare an eBL for signing",
        "description": "Build the unsigned transferable record and its signing spec. The caller signs, then calls <code>complete-mint</code>.",
        "operationId": "prepareMint",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblPrepareMintReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperPreparedCredentialRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/verify": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Verify an eBL",
        "description": "Document integrity <strong>and</strong> on-chain state. Takes no <code>tokenRegistry</code> —\n unlike every other eBL route, it reads the registry and token id from inside the submitted\n credential. Read <code>valid</code> AND <code>trusted</code> — never <code>valid</code> alone.",
        "operationId": "verify",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyCredentialReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperEblVerifyRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/accept-surrender": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Accept a surrender",
        "description": "The issuer finalizes a returned record; it is burned and retired.",
        "operationId": "acceptSurrender",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblActionReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/endorsement-chain": {
      "get": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Get the endorsement chain",
        "description": "The record's full transfer history, oldest first.",
        "operationId": "endorsementChain",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tokenRegistry",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperEndorsementChainRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/nominate": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Nominate the next beneficiary",
        "description": "The beneficiary nominates who may take title next — step 1 of the two-step title transfer.",
        "operationId": "nominate",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblNewBeneficiaryReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/owner": {
      "get": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Get the current owner",
        "description": "The Title Escrow currently owning the token (or the registry, once surrendered).",
        "operationId": "owner",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tokenRegistry",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperEblOwnerRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/reject-surrender": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Reject a surrender",
        "description": "The issuer returns a surrendered record to the last holder, active again.",
        "operationId": "rejectSurrender",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblActionReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/reject-transfer-beneficiary": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Reject a beneficiary transfer",
        "description": "Decline an incoming beneficiary transfer.",
        "operationId": "rejectTransferBeneficiary",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblActionReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/reject-transfer-holder": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Reject a holder transfer",
        "description": "Decline an incoming holder transfer. Not the same as {@link com.myeg.z2.tradetrust.controller.EblController#rejectSurrender #rejectSurrender} — unrelated flows.",
        "operationId": "rejectTransferHolder",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblActionReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/reject-transfer-owners": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Reject a combined transfer",
        "description": "Decline an incoming combined (owners) transfer.",
        "operationId": "rejectTransferOwners",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblActionReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/surrender": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Surrender an eBL",
        "description": "The holder returns the record to the issuer — step 1 of 3 in the end-of-life flow.",
        "operationId": "surrender",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblActionReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/transfer-beneficiary": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Transfer the beneficiary",
        "description": "Transfer title to the goods. Irreversible once the returned transaction is broadcast.",
        "operationId": "transferBeneficiary",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblNewBeneficiaryReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/transfer-holder": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Transfer the holder",
        "description": "Hand possession of the record to another party. Irreversible once the returned transaction is broadcast.",
        "operationId": "transferHolder",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblTransferHolderReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/ebl/{tokenId}/transfer-owners": {
      "post": {
        "tags": [
          "TradeTrust eBL"
        ],
        "summary": "Transfer beneficiary and holder",
        "description": "Move beneficiary and holder together, atomically, in one transaction.",
        "operationId": "transferOwners",
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EblTransferOwnersReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperUnsignedTxRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tradetrust/finality/{txHash}": {
      "get": {
        "tags": [
          "TradeTrust Finality"
        ],
        "summary": "Get transaction finality",
        "description": "Confirmation depth and L1 settlement for one transaction. Read <code>confirmations</code> and\n <code>l1Settled</code>; <code>final</code> means only \"mined\" here.",
        "operationId": "finality",
        "parameters": [
          {
            "name": "txHash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperFinalityRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tx/estimate-fee": {
      "post": {
        "tags": [
          "Transaction"
        ],
        "operationId": "estimateFee",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateFeeReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperEstimateFeeRespDto"
                }
              }
            }
          }
        },
        "summary": "Estimate the transaction fee"
      }
    },
    "/tx/generate-blob": {
      "post": {
        "tags": [
          "Transaction"
        ],
        "operationId": "generateBlob",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateBlobReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperGenerateBlobRespDto"
                }
              }
            }
          }
        },
        "summary": "Build a signable transaction blob"
      }
    },
    "/tx/sign-blob": {
      "post": {
        "tags": [
          "Transaction"
        ],
        "operationId": "signBlob",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignBlobReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperSignBlobRespDto"
                }
              }
            }
          }
        },
        "summary": "Sign a transaction blob"
      }
    },
    "/tx/status": {
      "get": {
        "tags": [
          "Transaction"
        ],
        "summary": "Get transaction status",
        "description": "What became of a submitted transaction.\n\n <p>GET with a query parameter, matching L1's <code>GET /tx/status?hash=</code>. Safe as a GET\n because a transaction hash is public information — unlike a private key, it does no harm in\n an access log.\n\n <p>A hash the node has not seen returns <code>PENDING</code>, not 404: over JSON-RPC \"still in the\n mempool\" and \"never existed\" are indistinguishable, so reporting not-found would be a guess.",
        "operationId": "status",
        "parameters": [
          {
            "name": "hash",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperTxStatusRespDto"
                }
              }
            }
          }
        }
      }
    },
    "/tx/submit": {
      "post": {
        "tags": [
          "Transaction"
        ],
        "operationId": "submit",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitTxReqDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseWrapperSubmitTxRespDto"
                }
              }
            }
          }
        },
        "summary": "Submit a signed transaction"
      }
    }
  },
  "components": {
    "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"
        ]
      },
      "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": {}
          }
        }
      },
      "EblActionReqDto": {
        "type": "object",
        "description": "The two fields every eBL write that takes no extra parameter needs: <code>surrender</code>,\n <code>accept-surrender</code>, <code>reject-surrender</code>, <code>reject-transfer-holder</code>,\n <code>reject-transfer-beneficiary</code>, <code>reject-transfer-owners</code>. Extended by the writes that\n need more — see {@link EblTransferHolderReqDto EblTransferHolderReqDto}, {@link EblNewBeneficiaryReqDto EblNewBeneficiaryReqDto},\n {@link EblTransferOwnersReqDto EblTransferOwnersReqDto}.\n\n <p><code>tokenId</code> is not here — every eBL write takes it as a path variable, not a body field.",
        "properties": {
          "from": {
            "type": "string",
            "minLength": 1
          },
          "tokenRegistry": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "from",
          "tokenRegistry"
        ]
      },
      "EblCompleteMintReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tradetrust/ebl/complete-mint</code> — hand back the signed record and get the\n mint transaction. <code>beneficiary</code> and <code>holder</code> default to <code>from</code> server-side.",
        "properties": {
          "preparationId": {
            "type": "string",
            "minLength": 1
          },
          "signedVerifiableCredential": {
            "type": "object",
            "additionalProperties": {},
            "minProperties": 1
          },
          "from": {
            "type": "string",
            "description": "Must hold <code>MINTER_ROLE</code> on the registry.",
            "minLength": 1
          },
          "beneficiary": {
            "type": "string"
          },
          "holder": {
            "type": "string"
          }
        },
        "required": [
          "from",
          "preparationId",
          "signedVerifiableCredential"
        ]
      },
      "EblCompleteMintRespDto": {
        "type": "object",
        "description": "Response of <code>POST /tradetrust/ebl/complete-mint</code> (and <code>/mint</code>, which ends at the same\n step) — the assembled record, its assigned token id, and the still-unsent mint transaction.",
        "properties": {
          "verifiableCredential": {
            "type": "object",
            "additionalProperties": {}
          },
          "tokenId": {
            "type": "string",
            "description": "0x-prefixed 32-byte hex token id, assigned during signing rather than at prepare time."
          },
          "unsignedMintTx": {
            "$ref": "#/components/schemas/UnsignedTxRespDto"
          }
        }
      },
      "EblMintReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tradetrust/ebl/mint</code> — the same build as {@link EblPrepareMintReqDto EblPrepareMintReqDto},\n plus the issuer's key pair for delegated signing and the minting address. One call, but\n <code>from</code> must still sign and broadcast the returned <code>unsignedMintTx</code> — the token does\n not exist until that is mined.\n\n <p><b>This sends the issuer's private key to core-engine.</b> See {@link MultikeyDto MultikeyDto}.",
        "properties": {
          "issuerDid": {
            "type": "string",
            "minLength": 1
          },
          "credentialSubject": {
            "type": "object",
            "additionalProperties": {},
            "minProperties": 1
          },
          "tokenRegistry": {
            "type": "string",
            "minLength": 1
          },
          "chainId": {
            "type": "integer",
            "format": "int64"
          },
          "chain": {
            "type": "string"
          },
          "context": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expirationDate": {
            "type": "string"
          },
          "renderMethod": {
            "type": "object",
            "additionalProperties": {}
          },
          "qrCode": {
            "type": "object",
            "additionalProperties": {}
          },
          "from": {
            "type": "string",
            "description": "Must hold <code>MINTER_ROLE</code> on the registry.",
            "minLength": 1
          },
          "beneficiary": {
            "type": "string",
            "description": "Initial owner of title to the goods (Title Escrow beneficiary). Defaults to <code>from</code>."
          },
          "holder": {
            "type": "string",
            "description": "Initial holder / possessor. Defaults to <code>from</code>."
          },
          "keyPair": {
            "$ref": "#/components/schemas/MultikeyDto"
          }
        },
        "required": [
          "credentialSubject",
          "from",
          "issuerDid",
          "keyPair",
          "tokenRegistry"
        ]
      },
      "EblNewBeneficiaryReqDto": {
        "type": "object",
        "description": "Request body shape shared by <code>POST /tradetrust/ebl/{tokenId</code>/transfer-beneficiary}\n (irreversible once broadcast) and <code>POST /tradetrust/ebl/{tokenId</code>/nominate} (step 1 of the\n two-step title transfer) — both are exactly <code>newBeneficiary</code> + the base action fields.",
        "properties": {
          "from": {
            "type": "string",
            "minLength": 1
          },
          "tokenRegistry": {
            "type": "string",
            "minLength": 1
          },
          "newBeneficiary": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "from",
          "newBeneficiary",
          "tokenRegistry"
        ]
      },
      "EblOnChainRespDto": {
        "type": "object",
        "description": "The registry-side view of a transferable record, part of {@link EblVerifyRespDto EblVerifyRespDto}.\n\n <p><code>beneficiary</code> and <code>holder</code> are <code>null</code> unless the record is active.",
        "properties": {
          "minted": {
            "type": "boolean",
            "description": "True while the token exists (active or surrendered); false if burned or never minted."
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "SURRENDERED",
              "BURNED",
              "NOT_MINTED",
              "UNKNOWN"
            ]
          },
          "registry": {
            "type": "string",
            "description": "The registry the record points at — worth checking rather than trusting <code>valid</code> alone:\n core-engine reports {@link Status#NOT_MINTED Status#NOT_MINTED}, not an error, for a record pointing elsewhere."
          },
          "tokenId": {
            "type": "string"
          },
          "titleEscrow": {
            "type": "string",
            "description": "The Title Escrow owning the token while active (the registry once surrendered)."
          },
          "beneficiary": {
            "type": "string"
          },
          "holder": {
            "type": "string"
          }
        }
      },
      "EblOwnerRespDto": {
        "type": "object",
        "description": "Response of <code>GET /tradetrust/ebl/{tokenId</code>/owner}.",
        "properties": {
          "tokenId": {
            "type": "string"
          },
          "owner": {
            "type": "string",
            "description": "The Title Escrow address holding the token (or the registry, once surrendered)."
          }
        }
      },
      "EblPrepareMintReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tradetrust/ebl/prepare-mint</code> — build the unsigned transferable record\n and its signing spec. <code>issuerDid</code>, <code>credentialSubject</code> and <code>tokenRegistry</code> are\n required; core-engine removed its server-wide default registry (a <code>tokenId</code> is only unique\n within one registry contract).",
        "properties": {
          "issuerDid": {
            "type": "string",
            "minLength": 1
          },
          "credentialSubject": {
            "type": "object",
            "additionalProperties": {},
            "minProperties": 1
          },
          "tokenRegistry": {
            "type": "string",
            "minLength": 1
          },
          "chainId": {
            "type": "integer",
            "format": "int64"
          },
          "chain": {
            "type": "string"
          },
          "context": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expirationDate": {
            "type": "string"
          },
          "renderMethod": {
            "type": "object",
            "additionalProperties": {}
          },
          "qrCode": {
            "type": "object",
            "additionalProperties": {}
          }
        },
        "required": [
          "credentialSubject",
          "issuerDid",
          "tokenRegistry"
        ]
      },
      "EblTransferHolderReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tradetrust/ebl/{tokenId</code>/transfer-holder} — irreversible once broadcast.",
        "properties": {
          "from": {
            "type": "string",
            "minLength": 1
          },
          "tokenRegistry": {
            "type": "string",
            "minLength": 1
          },
          "newHolder": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "from",
          "newHolder",
          "tokenRegistry"
        ]
      },
      "EblTransferOwnersReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tradetrust/ebl/{tokenId</code>/transfer-owners} — moves beneficiary and\n holder together, atomically, in one transaction.",
        "properties": {
          "from": {
            "type": "string",
            "minLength": 1
          },
          "tokenRegistry": {
            "type": "string",
            "minLength": 1
          },
          "newBeneficiary": {
            "type": "string",
            "minLength": 1
          },
          "newHolder": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "from",
          "newBeneficiary",
          "newHolder",
          "tokenRegistry"
        ]
      },
      "EblVerifyRespDto": {
        "type": "object",
        "description": "Response of <code>POST /tradetrust/ebl/verify</code> — document integrity <strong>and</strong> on-chain\n state. Returned for both outcomes; a burned or never-minted record is a value\n (<code>valid: false</code>), never an error.\n\n <p><b>Read <code>valid</code> AND <code>trusted</code></b> — same rule as\n <code>VerifyCredentialRespDto</code>, and for the same reason.",
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "issuer": {
            "type": "string"
          },
          "trusted": {
            "type": "boolean"
          },
          "onChain": {
            "$ref": "#/components/schemas/EblOnChainRespDto"
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VerifyCheckRespDto"
            }
          }
        }
      },
      "EndorsementChainRespDto": {
        "type": "object",
        "description": "Response of <code>GET /tradetrust/ebl/{tokenId</code>/endorsement-chain} — the record's full transfer\n history, oldest first.\n\n <p>Events stay as <code>Map&lt;String, Object&gt;</code> rather than a typed class, matching the SDK's own\n choice: core-engine adds event types and fields over time. Observed fields include <code>type</code>\n (<code>INITIAL</code>, <code>TRANSFER_BENEFICIARY</code>, <code>TRANSFER_HOLDER</code>, …), <code>owner</code>,\n <code>holder</code>, <code>transactionHash</code>, <code>transactionIndex</code>, <code>blockNumber</code>,\n <code>timestamp</code> (epoch milliseconds) and <code>remark</code>.",
        "properties": {
          "tokenId": {
            "type": "string"
          },
          "chain": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": {}
            }
          }
        }
      },
      "EstimateFeeReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tx/estimate-fee</code>.\n\n <code>from</code> matters: gas depends on the sender's state (whether a storage slot is already\n set, whether the account exists), so estimating without it can give a materially different\n number than the real transaction will use.",
        "properties": {
          "from": {
            "type": "string",
            "minLength": 1
          },
          "to": {
            "type": "string",
            "minLength": 1
          },
          "value": {
            "type": "integer"
          },
          "data": {
            "type": "string"
          }
        },
        "required": [
          "from",
          "to"
        ]
      },
      "EstimateFeeRespDto": {
        "type": "object",
        "description": "Response of <code>POST /tx/estimate-fee</code>. All values in wei.\n\n <code>fee</code> is <code>gasLimit * gasPrice</code> — the ceiling the sender must be able to cover, not\n what the transaction will necessarily cost. Unused gas is not charged, so the actual spend is\n <code>gasUsed * gasPrice</code>, which is only known from the receipt.\n\n No safety margin is applied to gasPrice: it is the node's <code>eth_gasPrice</code> verbatim, per\n the 2026-08-05 decision to follow the node. (myeg-ms-ethereum doubles it below 25 gwei — a\n threshold tuned for Ethereum mainnet and meaningless on a chain quoting 7 wei.)",
        "properties": {
          "gasLimit": {
            "type": "integer"
          },
          "gasPrice": {
            "type": "integer"
          },
          "fee": {
            "type": "string",
            "description": "<b>A decimal String, not a JSON number</b>, unlike the two factors above.\n\n <p><code>gasLimit</code> and <code>gasPrice</code> are each safely inside a JavaScript number — a block\n gas limit is millions, and even 20 gwei is 2 × 10^10. Their <b>product</b> is not: 10^6 gas at\n 20 gwei is 2 × 10^16, past <code>Number.MAX_SAFE_INTEGER</code> (~9.007 × 10^15). Z2 quoting 7 wei\n hides this today, so the field would look fine right up until this service pointed at a chain\n with an ordinary fee market."
          }
        }
      },
      "FinalityRespDto": {
        "type": "object",
        "description": "Response of <code>GET /tradetrust/finality/{txHash</code>} — how final an L2 transaction is.\n\n <p><strong>On Z2, <code>final</code> adds little to <code>/tx/status</code>'s <code>SUCCESS</code>.</strong> Zetrix\n L2 runs QBFT, which is deterministic and does not reorg, so a transaction is final the moment it is\n mined: <code>final</code> is exactly <code>confirmations &gt;= 1</code>, not a threshold anyone can tune. The two\n fields worth reading are <code>confirmations</code> (which <code>/tx/status</code> cannot give you without a\n second call for the chain head) and <code>l1Settled</code>.\n\n <p>Takes any transaction hash, including one this service never submitted.",
        "properties": {
          "l2Block": {
            "type": "integer",
            "format": "int32",
            "description": "<code>null</code> when the node has no receipt for this hash — still pending, or never existed."
          },
          "confirmations": {
            "type": "integer",
            "format": "int32"
          },
          "l1Settled": {
            "type": "boolean",
            "description": "Whether the batch containing this transaction has settled on L1. <code>null</code> means not\n checked, which is not the same as false: core-engine only answers this when an L1 settlement\n checker is configured on its side."
          },
          "final": {
            "type": "boolean",
            "description": "The wire field is <code>final</code>, which cannot be a Java identifier — bound explicitly rather\n than relying on Jackson deriving it from the <code>isFinal()</code> getter."
          }
        }
      },
      "GenerateBlobReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tx/generate-blob</code>.\n\n The three override fields exist so a caller can pin a transaction exactly (replacing a stuck\n nonce, for instance). Left null, each is resolved from the node: nonce from\n <code>eth_getTransactionCount(pending)</code>, gasPrice from <code>eth_gasPrice</code>, gasLimit from\n <code>eth_estimateGas</code>.",
        "properties": {
          "from": {
            "type": "string",
            "minLength": 1
          },
          "to": {
            "type": "string",
            "description": "Recipient. Required here — contract deployment (no recipient) is BT-2584.",
            "minLength": 1
          },
          "value": {
            "type": "integer",
            "description": "Native value in wei. Defaults to zero."
          },
          "data": {
            "type": "string",
            "description": "Hex-encoded call data. Null or empty for a plain transfer."
          },
          "nonce": {
            "type": "integer"
          },
          "gasPrice": {
            "type": "integer"
          },
          "gasLimit": {
            "type": "integer"
          },
          "maxFeePerGas": {
            "type": "integer",
            "description": "EIP-1559 ceiling, in wei. Supplying it builds a <b>type-2</b> transaction instead of a\n legacy one; leave it null and nothing changes.\n\n <p>You are charged <code>baseFeePerGas + maxPriorityFeePerGas</code> and refunded the rest, so a\n generous ceiling does not cost more - it only has to be high enough that the transaction\n stays includable if the base fee rises.\n\n <p>Mutually exclusive with <code>gasPrice</code>: naming both a legacy bid and a 1559 ceiling is\n contradictory, and guessing which one the caller meant is worse than refusing."
          },
          "maxPriorityFeePerGas": {
            "type": "integer",
            "description": "EIP-1559 validator tip, in wei. Only meaningful with <code>maxFeePerGas</code>; defaults to 0."
          }
        },
        "required": [
          "from",
          "to"
        ]
      },
      "GenerateBlobRespDto": {
        "type": "object",
        "description": "Response of <code>POST /tx/generate-blob</code>.\n\n The resolved nonce/gasPrice/gasLimit are echoed back deliberately: they are already encoded\n inside the blob, but a caller signing client-side needs to see what they are about to commit\n to without having to RLP-decode it first.",
        "properties": {
          "blob": {
            "type": "string",
            "description": "Unsigned transaction, RLP hex. Feed this to /tx/sign-blob or sign it yourself."
          },
          "nonce": {
            "type": "integer"
          },
          "gasPrice": {
            "type": "integer"
          },
          "gasLimit": {
            "type": "integer"
          },
          "chainId": {
            "type": "integer",
            "format": "int64",
            "description": "The chain id the blob must be signed with (EIP-155). Read from the node."
          },
          "maxFeePerGas": {
            "type": "integer",
            "description": "Set only when the blob is a type-2 transaction; null for a legacy one."
          },
          "maxPriorityFeePerGas": {
            "type": "integer"
          }
        }
      },
      "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"
        ]
      },
      "OnboardIssuerReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tradetrust/ebl/onboard-issuer</code> — build the transaction that deploys a\n brand-new eBL Token Registry.\n\n <p>This is where an eBL flow starts. <code>tokenRegistry</code> is required on <code>prepare-mint</code> and\n <code>mint</code> and core-engine keeps no server-wide default, so without a registry of your own there\n is nothing to mint into.\n\n <p>Like every other eBL write this returns an <strong>unsigned</strong> transaction: <code>admin</code>\n (or whoever you choose to pay) signs and broadcasts it, so that address needs gas first. Deployment\n is permissionless — the registry's admin comes from an encoded parameter rather than the sender —\n so the paying address and the admin need not be the same.\n\n <p><strong>The new registry's address is not in the transaction receipt's <code>contractAddress</code>\n field.</strong> The registry is deployed by the platform's shared deployer contract, not by this\n transaction directly, so that field is null. Once the transaction succeeds, read the address from\n <code>GET /tradetrust/ebl/onboard-issuer/registry?hash=...</code>.",
        "properties": {
          "name": {
            "type": "string",
            "description": "The registry's ERC-721 collection name, e.g. <code>&quot;MYEG Bill of Lading&quot;</code>.",
            "minLength": 1
          },
          "symbol": {
            "type": "string",
            "description": "The registry's ERC-721 symbol, e.g. <code>&quot;MYEGBL&quot;</code>.",
            "minLength": 1
          },
          "admin": {
            "type": "string",
            "description": "Becomes the new registry's admin, holding <code>DEFAULT_ADMIN_ROLE</code>, <code>MINTER_ROLE</code>,\n <code>RESTORER_ROLE</code> and <code>ACCEPTER_ROLE</code> — so this is the address that will be allowed\n to mint. Typically the issuer's own operational address.",
            "minLength": 1
          }
        },
        "required": [
          "admin",
          "name",
          "symbol"
        ]
      },
      "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"
        ]
      },
      "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"
          }
        }
      },
      "RegistryAddressRespDto": {
        "type": "object",
        "description": "Response of <code>GET /tradetrust/ebl/onboard-issuer/registry</code> — the address of the Token Registry\n an <code>onboard-issuer</code> transaction deployed.\n\n <p>This endpoint exists because the address is not where callers look for it. The registry is\n deployed by the platform's shared deployer contract rather than by the submitted transaction\n itself, so the receipt's <code>contractAddress</code> is null and <code>GET /tx/status</code> — which returns\n no logs — cannot show it either. The address is only in the deployer's <code>Deployment</code> event, so\n this reads the receipt and decodes it.\n\n <p>Poll while <code>status</code> is <code>PENDING</code>, exactly as with <code>/tx/status</code>: a hash the node\n has not seen yet is pending rather than absent, because over JSON-RPC \"still in the mempool\" and\n \"never existed\" are indistinguishable. <code>registry</code> is populated only on <code>SUCCESS</code>.",
        "properties": {
          "transactionHash": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "SUCCESS",
              "FAILED",
              "PENDING"
            ]
          },
          "registry": {
            "type": "string",
            "description": "The new registry's address — the value to pass as <code>tokenRegistry</code> from here on.\n\n <p><code>null</code> while <code>PENDING</code> or on <code>FAILED</code>. Also null in one case worth\n handling: a <code>SUCCESS</code> whose logs carry no <code>Deployment</code> event, which means the hash\n belongs to some other transaction rather than an <code>onboard-issuer</code> one."
          }
        }
      },
      "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"
          }
        }
      },
      "ResponseWrapperEblCompleteMintRespDto": {
        "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/EblCompleteMintRespDto"
          }
        }
      },
      "ResponseWrapperEblOwnerRespDto": {
        "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/EblOwnerRespDto"
          }
        }
      },
      "ResponseWrapperEblVerifyRespDto": {
        "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/EblVerifyRespDto"
          }
        }
      },
      "ResponseWrapperEndorsementChainRespDto": {
        "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/EndorsementChainRespDto"
          }
        }
      },
      "ResponseWrapperEstimateFeeRespDto": {
        "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/EstimateFeeRespDto"
          }
        }
      },
      "ResponseWrapperFinalityRespDto": {
        "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/FinalityRespDto"
          }
        }
      },
      "ResponseWrapperGenerateBlobRespDto": {
        "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/GenerateBlobRespDto"
          }
        }
      },
      "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"
          }
        }
      },
      "ResponseWrapperRegistryAddressRespDto": {
        "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/RegistryAddressRespDto"
          }
        }
      },
      "ResponseWrapperSignBlobRespDto": {
        "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/SignBlobRespDto"
          }
        }
      },
      "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"
          }
        }
      },
      "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"
          }
        }
      },
      "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"
          }
        }
      },
      "ResponseWrapperSubmitTxRespDto": {
        "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/SubmitTxRespDto"
          }
        }
      },
      "ResponseWrapperTxStatusRespDto": {
        "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/TxStatusRespDto"
          }
        }
      },
      "ResponseWrapperUnsignedTxRespDto": {
        "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/UnsignedTxRespDto"
          }
        }
      },
      "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"
          }
        }
      },
      "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"
        ]
      },
      "SignBlobReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tx/sign-blob</code>.\n\n <b>Carries a private key.</b> This is why the endpoint is POST rather than GET: a key in a\n query string ends up in access logs, proxy logs and browser history. The key is used to sign\n and then discarded — never logged, never persisted. Callers who would rather not send it at\n all should sign the blob client-side; that is the reason the blob/sign/submit split exists.",
        "properties": {
          "blob": {
            "type": "string",
            "description": "Unsigned transaction as RLP hex, from <code>/tx/generate-blob</code>.",
            "minLength": 1
          },
          "privateKey": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "blob",
          "privateKey"
        ]
      },
      "SignBlobRespDto": {
        "type": "object",
        "description": "Response of <code>POST /tx/sign-blob</code>.\n\n Contains no echo of the submitted private key, by design — see <code>SignBlobReqDto</code>.",
        "properties": {
          "signedBlob": {
            "type": "string",
            "description": "Signed transaction, RLP hex. Ready for /tx/submit."
          },
          "transactionHash": {
            "type": "string",
            "description": "The hash this transaction will have once submitted (keccak256 of the signed RLP).\n Computable before submission, so a caller can start watching for it immediately."
          },
          "chainId": {
            "type": "integer",
            "format": "int64",
            "description": "The chain id baked into the signature, for the caller to verify."
          }
        }
      },
      "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"
        ]
      },
      "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": {}
          }
        }
      },
      "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"
          }
        }
      },
      "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": {}
          }
        }
      },
      "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"
          }
        }
      },
      "SubmitTxReqDto": {
        "type": "object",
        "description": "Request for <code>POST /tx/submit</code>: a signed transaction as RLP hex.",
        "properties": {
          "signedBlob": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "signedBlob"
        ]
      },
      "SubmitTxRespDto": {
        "type": "object",
        "description": "Response of <code>POST /tx/submit</code>.\n\n A hash here means the node <b>accepted</b> the transaction into its pool — not that it has\n been mined, and not that it will succeed. There is currently no way to follow up on it: a\n status/receipt endpoint is a known gap with no ticket (see the BT-2583 plan doc §1).",
        "properties": {
          "transactionHash": {
            "type": "string"
          }
        }
      },
      "TxStatusRespDto": {
        "type": "object",
        "description": "Response of <code>GET /tx/status?hash=…</code> — the outcome of a submitted transaction.\n\n <p>This closes the gap that made <code>/tx/submit</code> a dead end: you got a hash and had no way to\n ask what became of it. A hash means only that the node accepted the transaction into its pool; a\n transaction that <b>reverts is still mined and still has that hash</b>, so the hash alone can\n never tell success from failure.\n\n <p>The four states are genuinely different and are worth distinguishing:\n <ul>\n   <li><code>SUCCESS</code> — mined, <code>status: 0x1</code>. It ran and did not revert.</li>\n   <li><code>FAILED</code> — mined, <code>status: 0x0</code>. It reverted; gas was still consumed. See\n       <code>revertReason</code>.</li>\n   <li><code>PENDING</code> — no receipt yet. Either still in the pool, or the hash never existed —\n       JSON-RPC cannot tell those apart, so neither can we, and claiming otherwise would be a\n       guess. Poll, and treat a hash that stays pending indefinitely as never having landed.</li>\n </ul>",
        "properties": {
          "transactionHash": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "SUCCESS",
              "FAILED",
              "PENDING"
            ]
          },
          "blockNumber": {
            "type": "integer"
          },
          "gasUsed": {
            "type": "integer"
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "contractAddress": {
            "type": "string",
            "description": "Set only for a contract creation, and this is the <b>authoritative</b> address — unlike the\n prediction from <code>/contract/deploy/generate-blob</code>, which is conditional on the nonce\n being spent as planned. Once a deploy reaches <code>SUCCESS</code>, use this one."
          },
          "revertReason": {
            "type": "string",
            "description": "Why a <code>FAILED</code> transaction reverted, when it can be recovered.\n\n <p>Revert data is not stored on chain, so this comes from replaying the call against the\n state it ran in. That needs historical state: a pruned node cannot serve it and this stays\n null. Null therefore means \"could not be recovered\", never \"no reason\"."
          }
        }
      },
      "UnsignedTxRespDto": {
        "type": "object",
        "description": "An <strong>unsigned</strong> EVM transaction, returned by every eBL write, by mint, and by\n <code>onboard-issuer</code>. Nothing has happened on-chain until the <code>from</code> address signs and\n broadcasts it.\n\n <p>Four calls on this same service finish the job:\n\n <ol>\n   <li><code>POST /tx/generate-blob</code> with <code>from</code> plus the <code>to</code>, <code>data</code> and\n       <code>value</code> below, which returns the RLP-encoded <code>blob</code>.\n   <li><code>POST /tx/sign-blob</code> with that <code>blob</code> — or sign it yourself and skip this call.\n   <li><code>POST /tx/submit</code> with the <code>signedBlob</code>.\n   <li><code>GET /tx/status?hash=...</code> until it reports <code>SUCCESS</code>.\n </ol>\n\n <p><b>Step 1 is not optional.</b> <code>/tx/sign-blob</code> takes a <code>blob</code> string, not the\n object below, so handing this response straight to it fails validation — an easy trap, and one\n this javadoc itself set until BT-2620's follow-up corrected it.\n\n <p><code>chainId</code> is informational: <code>/tx/sign-blob</code> signs with the chain this service is\n configured for, read live from the node. It matches when both this service and core-engine point\n at the same chain, which is the only supported arrangement — worth asserting rather than assuming\n if you run your own deployment of either.",
        "properties": {
          "to": {
            "type": "string",
            "description": "Contract to call — the token registry, or a Title Escrow."
          },
          "data": {
            "type": "string",
            "description": "ABI-encoded calldata."
          },
          "value": {
            "type": "string",
            "description": "Wei value as a decimal string; <code>&quot;0&quot;</code> for every eBL operation."
          },
          "chainId": {
            "type": "integer",
            "format": "int64",
            "description": "Chain the transaction must be signed for."
          }
        }
      },
      "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."
          }
        }
      },
      "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"
        ]
      },
      "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"
            }
          }
        }
      }
    },
    "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"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "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."
      }
    }
  }
}
