Skip to main content

Migration guide

EBSI API Migration Guide, from current pilot APIs to EBSI Core API v1.

This guide maps the current API (nine separately-versioned services) to the new API (a single unified EBSI Core API v1).

The headline change: every write operation now goes through one directly-accessible JSON-RPC endpoint POST /v1/ledger instead of the per-service …/jsonrpc transaction preparation endpoints.


1. What changed at a glance​

AreaOldNew
API surface9 APIs, each with its own OpenAPI doc and versionOne API: EBSI Core API v1 (1.0.0)
Base URLhttps://api-<env>.ebsi.<node operator's domain> + per-service version segmenthttps://api-<env>.ebsi.<node operator's domain>/v1, with the node picked from the Trusted Nodes List
Path version/did-registry/v5/…, /authorisation/v4/…, /timestamp/v4/…single /v1 at the base, no per-service version
Path names/authorisation/v4/…, /trusted-*-registry/…/auth/…, /*-registry/… (trusted- prefix dropped)
WritesPOST /<service>/<v>/jsonrpc transaction preparation methods + sendSignedTransactionPOST /v1/ledger (eth_sendRawTransaction)
Besu read accessPOST /ledger/v4/blockchains/besuPOST /v1/ledger
Contract instancesone deployment per registrymultiple beacon-proxy instances. Target one via the contractAddress read param or a scope:<0x-address> token suffix. GET /v1/<registry>/details lists a registry instance's on-chain links. Detailed in §4, Targeting a contract instance.
DID writesgranular methods (updateBaseDocument, addVerificationMethod, …) via /did-registry/v5/jsonrpcsame granular method names, sent as eth_sendRawTransaction calldata via /v1/ledger.
Scopesopenid didr_write (underscore)openid did-registry:write (colon, full registry name), combinable, instance-targetable, holder-DID-method-restricted
Token grantOpenID4VP draft 14 (Presentation Exchange with presentation_submission)OpenID4VP 1.0 final (DCQL) only. Draft 14 is removed. Detailed in §4, Requesting a token.
Paginationpage[after] = integer page numberpage[after] = integer page number, 1-based (unchanged)
Docs path/api/<service>/api/core/<service>

2. Base URL and path structure​

<node operator's domain> is the domain of an approved node, listed in the Trusted Nodes List.

Current

https://api-<env>.ebsi.<node operator's domain>/did-registry/v5/identifiers/{did}
https://api-<env>.ebsi.<node operator's domain>/authorisation/v4/token
https://api-<env>.ebsi.<node operator's domain>/trusted-issuers-registry/v5/issuers

New

https://api-<env>.ebsi.<node operator's domain>/v1/did-registry/identifiers/{did}
https://api-<env>.ebsi.<node operator's domain>/v1/auth/token
https://api-<env>.ebsi.<node operator's domain>/v1/issuers-registry/issuers

Service path renames:

CurrentNew
/authorisation/v4/auth
/did-registry/v5/did-registry
/ledger/v4/ledger
/timestamp/v4/timestamp
/track-and-trace/v1/track-and-trace
/trusted-contracts-registry/v1/contracts-registry
/trusted-issuers-registry/v5/issuers-registry
/trusted-policies-registry/v3/policies-registry
/trusted-schemas-registry/v3/schemas-registry

3. Write operations and the new Ledger JSON-RPC endpoint​

Current model: per service, 2 calls + local signing​

1. POST /<service>/<v>/jsonrpc Authorization: Bearer <token: *_write / *_invite>
{ "jsonrpc":"2.0", "method":"insertDidDocument", "params":[ … ], "id":1 }
Response: 200 { "result": <unsigned Ethereum transaction> }

2. sign the transaction locally

3. POST /<service>/<v>/jsonrpc
{ "jsonrpc":"2.0", "method":"sendSignedTransaction", "params":[ … ], "id":2 }
Response: 200 { "result": "<txHash>" }

Transaction preparation methods lived on each service: insertDidDocument, addVerificationMethod, setAttributeData, addIssuerProxy, insertPolicy, insertSchema, timestampHashes, createDocument, grantAccess where each returned an unsigned transaction.

Allow-listed contract calls went to POST /ledger/v4/blockchains/besu (eth_sendRawTransaction, scope openid ledger_invoke).

New model one endpoint: POST /v1/ledger​

POST /v1/ledger is the Besu JSON-RPC interface, exposed directly.

  • Read methods (eth_getBlockByNumber, eth_getTransactionByHash, eth_getTransactionReceipt, eth_call, eth_chainId, eth_estimateGas, …) no authentication, forwarded straight to the Besu node.
  • Write = eth_sendRawTransaction send a client-built, client-signed raw transaction with Authorization: Bearer <access_token>. There are no more server-side transaction preparation endpoints. Encode the calldata yourself from the contract ABI (GET /v1/<registry>/abi the default contract address is now published in each /abi response, and the on-chain links between registry contracts via GET /v1/<registry>/details) with any Ethereum library, then sign and submit.
  • The access token scope is validated against the target registry and contract function before the transaction is forwarded.
  • One token may combine several *:write scopes the Ledger routes each transaction to the registry its function belongs to. If the matching scope is absent the entry is rejected with a JsonRpcError naming the missing scope.
  • Batch requests (JSON array, ≤ 1024 entries) and notifications (defined by missing id) are supported.
  • Errors are JSON-RPC error objects: -32700 parse error is returned at HTTP 400 -32600 (invalid request / missing scope / missing or invalid access token), -32601 (method not found) and -32005 (batch too large) are returned at HTTP 200. A missing Bearer token on a write is a JSON-RPC -32600 at HTTP 200, not an HTTP 401. The endpoint also answers 405 (non-POST, with an Allow header) and 406 (unsatisfiable Accept) see §5, which now applies API-wide.
POST /v1/ledger Authorization: Bearer <token: did-registry:write>
{ "jsonrpc":"2.0", "method":"eth_sendRawTransaction", "params":["0x<signed tx>"], "id":1 }
Response: 200 { "result": "<txHash>" }

Then poll eth_getTransactionReceipt on the same endpoint and check status (0x1 success, 0x0 reverted).

Scope for allowed on-chain functions (write via /v1/ledger)​

ScopeRegistryAllowed functions
openid did-registry:inviteDID RegistryinsertDidDocument
openid did-registry:writeDID RegistryaddController, addVerificationMethod, addVerificationRelationship, expireVerificationMethod, revokeController, revokeVerificationMethod, rollVerificationMethod, updateBaseDocument
openid issuers-registry:invite / openid issuers-registry:writeIssuers RegistryaddIssuerProxy, removeIssuerProxy, updateIssuerProxy, setAttributeData, setAttributeMetadata
openid policies-registry:writePolicies RegistryinsertPolicy, updatePolicy, activatePolicy, deactivatePolicy, insertUserAttributes, deleteUserAttributes
openid schemas-registry:writeSchemas RegistryinsertSchema, updateSchema, updateMetadata
openid timestamp:writeTimestampcommitTimestampHashes, timestampHashes, timestampRecordHashes, timestampVersionHashes, timestampRecordVersionHashes, appendRecordVersionHashes, detachRecordVersionHash, insertRecordVersionInfo, insertRecordOwner, revokeRecordOwner, insertHashAlgorithm, updateHashAlgorithm
openid track-and-trace:authoriseTrack and TraceauthoriseDid
openid track-and-trace:createTrack and TracecommitCreateDocument, createDocument
openid track-and-trace:writeTrack and TracegrantAccess, revokeAccess, writeEvent, removeDocument
openid ledger:invokeTrusted Contractsany function on an allow-listed contract

DID Registry note: the DID Registry keeps the same granular method names as the current pilot transaction-preparation API (updateBaseDocument, addVerificationMethod, addVerificationRelationship, revokeVerificationMethod, expireVerificationMethod, rollVerificationMethod, addController, revokeController). Two specifics:

  • insertDidDocument (registering a DID's initial verification method) needs a did-registry:invite token, and the DID Registry only accepts a publicKey whose RFC 7638 thumbprint matches the token's cnf.jkt claim, a did-registry:invite token cannot register any other key (see the access_token_response schema).
  • addVerificationMethod and rollVerificationMethod require vMethodId to be the RFC 7638 SHA-256 thumbprint of publicKey.

Treat the ABI from GET /v1/did-registry/abi as the source of truth for signatures.

Commit-reveal: Timestamp hashes and Track & Trace createDocument​

Two write flows are now two transactions, not one:

  • Every Timestamp function that timestamps hashes (timestampHashes, timestampRecordHashes, timestampVersionHashes, timestampRecordVersionHashes, appendRecordVersionHashes) is the reveal leg and takes a trailing salt argument. It must be preceded, from the same sender and at least TIMESTAMP_COMMITMENT_MATURITY blocks earlier (currently 5), by a commitTimestampHashes transaction carrying the commitment returned by the contract's computeTimestampCommitment view for those exact arguments.
  • createDocument (Track & Trace, both overloads) is likewise a reveal leg with a trailing salt, preceded by commitCreateDocument at least CREATE_DOCUMENT_COMMITMENT_MATURITY blocks earlier (currently 5) from the same sender, carrying computeCreateDocumentCommitment's output.

Both legs use the same scope (timestamp:write / track-and-trace:create) and are each submitted as their own eth_sendRawTransaction. Revealing without a matured commitment reverts (CommitmentUnknown, CommitmentNotMatured). Keep the salt secret until the reveal that is what prevents a third party from front-running the timestamp or the document creation.


4. Authorisation and scopes​

  • "Authorisation API v4" is now "Authorisation Service" "Trusted X Registry" is now "X Registry" throughout.
  • Token lifetime unchanged: 2 hours, no refresh tokens.
  • JWKS is unchanged apart from the /auth path. The discovery document is corrected, and the token flow uses OpenID4VP 1.0 (final) only. See Requesting a token below.
  • Breaking: the OpenID4VP draft 14 grant is removed. GET /auth/presentation-definitions no longer exists, and the token endpoint only accepts the OpenID4VP 1.0 (final) grant, which uses a DCQL query in place of a Presentation Definition. Clients must migrate.
  • Breaking: the discovery document is corrected to match the actual behaviour of the Authorisation Service. See Discovery document changes below.

Scope separator: from _ to : + full names​

OldNew
openid didr_inviteopenid did-registry:invite
openid didr_writeopenid did-registry:write
openid tir_inviteopenid issuers-registry:invite
openid tir_writeopenid issuers-registry:write
openid tpr_writeopenid policies-registry:write
openid tsr_writeopenid schemas-registry:write
openid timestamp_writeopenid timestamp:write
openid tnt_authoriseopenid track-and-trace:authorise
openid tnt_createopenid track-and-trace:create
openid tnt_writeopenid track-and-trace:write
openid ledger_invokeopenid ledger:invoke

New scope capabilities​

  • Combinable scopes did-registry:write, issuers-registry:write, timestamp:write, track-and-trace:create, track-and-trace:write, policies-registry:write, schemas-registry:write may be requested together in a single token (e.g. openid did-registry:write issuers-registry:write) the holder must satisfy every requested scope. The onboarding/invite scopes (did-registry:invite, issuers-registry:invite, track-and-trace:authorise) and ledger:invoke must still be requested alone.

  • Instance targeting any scope may be suffixed with :<0x-address> to target a specific deployed contract instance (e.g. openid track-and-trace:write:0x61c36a8d610163660E21a8b7359e1Cac0C9133e1) omitted uses the environment default. See Targeting a contract instance below.

  • Allowed holder DID methods each scope only accepts a Verifiable Presentation signed by a holder DID that uses one of that scope's permitted DID methods:

    • did:ebsi only: did-registry:invite, did-registry:write, issuers-registry:invite, issuers-registry:write, timestamp:write, track-and-trace:authorise, track-and-trace:create, policies-registry:write, schemas-registry:write.
    • did:ebsi or did:key: ledger:invoke, track-and-trace:write.

    When scopes are combined, the holder's DID method must be allowed by every requested scope (the intersection) so openid track-and-trace:write alone accepts a did:key holder, but openid did-registry:write track-and-trace:write does not.

Requesting a token​

POST /v1/auth/token accepts the OpenID4VP 1.0 (final) grant only: grant_type=vp_token, the /auth/token URL and the same scopes as before. The table below maps the removed draft 14 grant to its replacement.

Removed (OpenID4VP draft 14)Current (OpenID4VP 1.0 final)
Requirements endpointGET /auth/presentation-definitions?scope=…GET /auth/dcql-queries?scope=…
Discovery claimpresentation_definition_endpointdcql_query_endpoint
Query languagePresentation Exchange (input_descriptors)DCQL (credentials)
vp_tokencompact-JWS Verifiable Presentation JWTJSON object keyed by credential-query identifier, e.g. {"self_attestation_credential": ["eyJ…"]}
presentation_submissionrequiredmust be omitted
Scopes requiring no credentialpresent an empty Verifiable Presentation (input_descriptors: [])present a self-issued SelfAttestation credential

A compact-JWS vp_token is rejected with invalid_request, with the message vp_token: must be a JSON object keyed by DCQL credential-query id (OpenID4VP 1.0); the legacy draft 14 compact-JWS form is no longer supported.

Signing algorithms of the presentation are defined per scope. did-registry:invite and track-and-trace:authorise accept ES256K only, and GET /auth/dcql-queries advertises the algorithm that applies to the requested scope. All other scopes accept ES256 and ES256K. Credentials accept ES256 and ES256K for every scope. This represents a widening relative to the pilot API, in which credentials accepted ES256 only and a combined scope request resolved to the intersection of the requested scopes.

Migration is required. The principal behavioural difference concerns the seven combinable scopes (did-registry:write, issuers-registry:write, timestamp:write, track-and-trace:create, track-and-trace:write, policies-registry:write, schemas-registry:write). DCQL requires credentials to be non-empty, so whereas the removed grant accepted an empty presentation, the current grant expects a single shared, self-issued SelfAttestation credential whose issuer and subject are both the holder's own DID. Requesting several of these scopes together results in a single credential requirement rather than one per scope. See Access Control for its exact structure and constraints.

Discovery document changes​

GET /auth/.well-known/openid-configuration is corrected to match the Authorisation Service's actual behaviour. This differs deliberately from the published Authorisation API v4 discovery document:

ClaimBeforeNow
authorization_endpointpresent (there is no such route)removed
presentation_definition_endpointpresentremoved. Use dcql_query_endpoint
id_token_signing_alg_values_supported["none"]["ES256"]. Verify the signature of ID tokens
id_token_types_supportedsubject_signed_id_tokenattester_signed_id_token

Error responses: Problem Details (RFC 9457)​

  • Multi-issue validation failures return the constant detail The request failed validation, together with a new errors array of { detail, pointer?, parameter? } items. pointer is an RFC 6901 JSON Pointer to a request body field, and parameter names a query or path parameter. Don't parse detail.
  • Most problems now carry a stable type of the form urn:ebsi:problems:<slug> (for example urn:ebsi:problems:invalid-contract-address) instead of about:blank. The URIs are the same in every environment and are not dereferenceable. Match on type rather than title.
  • Responses that carry only an HTTP status (malformed body, 405, 406, 503) keep about:blank.

Targeting a contract instance​

Every registry (DID Registry, Issuers Registry, Policies Registry, Schemas Registry, Timestamp, Track & Trace) is deployed as one or more beacon-proxy instances: separate contract addresses that share a single upgradeable implementation. Each environment has one default instance per registry (the address printed in that registry's /abi response). The Contracts Registry (Proxy Factory) is the on-chain registry of these instances GET /v1/contracts-registry/contracts lists them.

The pilot API exposed only the default instance. The new API lets you address a non-default instance in two independent places:

Reads the contractAddress query parameter. Accepted on every registry read endpoint except the seven /abi endpoints (the ABI is the same for every instance) and the three /auth/* discovery endpoints. ?contractAddress=0x… selects the instance omitted uses the environment default. Validation:

  • not matching ^0x[0-9a-fA-F]{40}$ returns 400 (parameter validation).
  • well-formed but nothing is deployed there, or the deployed contract isn't the expected registry type returns 400 Invalid Contract Address (title Invalid Contract Address, detail says "No contract is deployed at address …" or "… does not match the expected contract"). In the pilot this parameter was accepted and silently ignored.

Token requests the :<0x-address> scope suffix. Append it to any scope in the /auth/token request, e.g. openid track-and-trace:write:0x61c3…. This both scopes the token's on-chain checks to that instance and changes how the Authorisation Service resolves the DID Registry it uses to verify the holder's Verifiable Presentation signature:

ScopeWhat the suffixed address points at, and the DID Registry used for holder resolution
did-registry:invite / did-registry:writethe targeted instance is the DID Registry used for holder resolution
track-and-trace:authorise / track-and-trace:create / track-and-trace:writethe targeted Track & Trace instance its own on-chain didRegistry() and policiesRegistry() are read and used (holder resolution, and the TNT:authoriseDid policy check)
issuers-registry:invite / issuers-registry:writethe targeted Issuers Registry instance its own on-chain linked DID Registry is used for holder resolution
policies-registry:write / schemas-registry:write / timestamp:writethe targeted Policies / Schemas / Timestamp instance these have no linked DID Registry, so holder resolution always falls back to the environment default DID Registry
  • The address must be a currently registered beacon-proxy instance of the expected registry type in the Contracts Registry, unless it is that registry's own environment default. An address that resolves as the right contract type but was never deployed through the Contracts Registry results in an invalid_request.
  • The VP is signed once, so every combined scope whose eligibility is checked against a DID Registry instance (did-registry:*, issuers-registry:*, track-and-trace:* including one left unaddressed, which resolves to the default) must resolve to the same DID Registry. Combining an addressed did-registry:write with an unaddressed issuers-registry:write whose default Issuers Registry is linked to a different DID Registry results in an invalid_request, even though neither address on its own looks inconsistent. policies-registry:write / schemas-registry:write / timestamp:write are exempt because their eligibility isn't rooted in any DID Registry.

contractAddress parameter ties the two together. Before issuing a token, the Authorisation Service verifies the holder's Verifiable Presentation signature, which means fetching the holder's DID document from a DID Registry. It picks the DID Registry instance using the rules above, then resolves the holder DID against it through the public GET /v1/did-registry/identifiers/{holderDid} endpoint with contractAddress set to that instance. This is the same call, and the same parameter, that any client can make directly to resolve a DID against a non-default DID Registry instance. Omitting contractAddress falls back to the environment default.

A new read endpoint returns an instance's own address plus the registry contracts it was initialized against. ?contractAddress= selects the instance omitted uses the environment default.

EndpointResponse fields
GET /v1/did-registry/detailscontractAddress, policyRegistryContract
GET /v1/issuers-registry/detailscontractAddress, didRegistryContract, policyRegistryContract
GET /v1/schemas-registry/detailscontractAddress, policyRegistryContract
GET /v1/timestamp/detailscontractAddress, policyRegistryContract
GET /v1/track-and-trace/detailscontractAddress, didRegistryContract, policiesRegistryContract
  • DID / Issuers / Schemas / Timestamp links are fixed at initialize() and immutable.
  • Track & Trace's didRegistry / policiesRegistry are mutable (setDidRegistry / setPoliciesRegistry) and may be unset.
  • There is no contracts-registry/details the Contracts Registry is the instance registry use GET /v1/contracts-registry/contracts.
  • Same error surface as other reads: 400 Invalid Contract Address, 405, 406, 500, 503.

Use it to confirm that the instances you intend to combine in one token are mutually consistent (same DID Registry) before the request, or to find out which Policies Registry a policies-registry:write / schemas-registry:write / timestamp:write check will actually hit.


5. Read (REST) endpoints​

All current GET/HEAD endpoints keep a 1-to-1 equivalent re-pathed under /v1 with the service renames from §2. Behaviour changes:

  • Pagination is unchanged. page[after] is still a 1-based integer page number (page[after]=1, 2, …), same as the pilot API. Keep incrementing it, or follow links.next the cursor is the page index.
  • contractAddress query parameter now actually selects which beacon-proxy instance is queried (previously accepted-but-ignored). Accepted on every registry read endpoint except the /abi and /auth/* endpoints a wrong or undeployed address gives 400 Invalid Contract Address, not 500. Full detail in §4, Targeting a contract instance.
  • DID document content negotiation is retained. GET /v1/did-registry/identifiers/{did} returns the @context-free application/did+json representation only when the Accept header is exactly application/did+json any other value including */* yields application/did+ld+json.
  • Response self / links URLs now contain /v1/ and no /vN/ segment.
  • 405 Method Not Allowed (with an Allow header) and 406 Not Acceptable are documented on essentially every path.
  • POST /auth/token error bodies are now an OAuth 2.0 error object ({ "error": …, "error_description": … }) served as application/json and always with HTTP 400 including for server_error instead of application/problem+json.
  • New GET /v1/<registry>/details returns the registry contracts an instance is on-chain linked to. No pilot equivalent detailed in §4, Inspecting an instance's links.

Endpoint map for reads​

ServiceCurrent (…ebsi.<node operator's domain> + path)New (…ebsi.<node operator's domain>/v1 + path)
Authorisation/authorisation/v4/.well-known/openid-configuration/auth/.well-known/openid-configuration
/authorisation/v4/jwks/auth/jwks
/authorisation/v4/presentation-definitions/auth/presentation-definitions
(none)/auth/dcql-queries (new)
POST /authorisation/v4/tokenPOST /auth/token
DID Registry/did-registry/v5/identifiers/did-registry/identifiers
/did-registry/v5/identifiers/{did}/did-registry/identifiers/{did}
/did-registry/v5/abi/did-registry/abi
(none)/did-registry/details (new)
Contracts Registry/trusted-contracts-registry/v1/contracts/contracts-registry/contracts
/trusted-contracts-registry/v1/contracts/{address}/contracts-registry/contracts/{address}
/trusted-contracts-registry/v1/templates/contracts-registry/templates
/trusted-contracts-registry/v1/templates/{id}/contracts-registry/templates/{id}
/trusted-contracts-registry/v1/abi/contracts-registry/abi
Issuers Registry/trusted-issuers-registry/v5/issuers/issuers-registry/issuers
/trusted-issuers-registry/v5/issuers/{did}/issuers-registry/issuers/{did}
/trusted-issuers-registry/v5/issuers/{did}/attributes[/{id}[/revisions[/{revId}]]]/issuers-registry/issuers/{did}/attributes/…
/trusted-issuers-registry/v5/issuers/{did}/proxies[/{proxyId}[/{path}]]/issuers-registry/issuers/{did}/proxies/…
/trusted-issuers-registry/v5/abi/issuers-registry/abi
(none)/issuers-registry/details (new)
Policies Registry/trusted-policies-registry/v3/policies[/{policyName}]/policies-registry/policies[/{policyName}]
/trusted-policies-registry/v3/subjects[/{subject}[/policies[/{policyName}]]]/policies-registry/subjects/…
/trusted-policies-registry/v3/abi/policies-registry/abi
Schemas Registry/trusted-schemas-registry/v3/schemas[/{schemaId}[/revisions[/{revId}[/metadata[/{metaId}]]]]]/schemas-registry/schemas/…
/trusted-schemas-registry/v3/abi/schemas-registry/abi
(none)/schemas-registry/details (new)
Timestamp/timestamp/v4/hash-algorithms[/{id}]/timestamp/hash-algorithms[/{id}]
/timestamp/v4/timestamps[/{timestampId}]/timestamp/timestamps[/{timestampId}]
/timestamp/v4/records[/{recordId}[/versions[/{versionId}]]]/timestamp/records/…
/timestamp/v4/abi/timestamp/abi
(none)/timestamp/details (new)
Track & Trace/track-and-trace/v1/documents[/{id}[/events[/{eventId}]]]/track-and-trace/documents/…
/track-and-trace/v1/documents/{id}/accesses/track-and-trace/documents/{id}/accesses
GET & HEAD /track-and-trace/v1/accesses/track-and-trace/accesses
/track-and-trace/v1/abi/track-and-trace/abi
(none)/track-and-trace/details (new)

6. Removed / discontinued​

RemovedReplacement
POST /did-registry/v5/jsonrpcPOST /v1/ledger (eth_sendRawTransaction)
POST /timestamp/v4/jsonrpcPOST /v1/ledger
POST /track-and-trace/v1/jsonrpcPOST /v1/ledger
POST /trusted-issuers-registry/v5/jsonrpcPOST /v1/ledger
POST /trusted-policies-registry/v3/jsonrpcPOST /v1/ledger
POST /trusted-schemas-registry/v3/jsonrpcPOST /v1/ledger
POST /ledger/v4/blockchains/besuPOST /v1/ledger
POST /did-registry/v5/identifiers/{did}/actions (checkController)No REST replacement read the controller from the DID document or via eth_call to the DID Registry contract
GET /trusted-policies-registry/v3/usersNo REST replacement read user attributes on-chain via eth_call
GET /trusted-policies-registry/v3/users/{user}as above
GET /auth/presentation-definitions (OpenID4VP draft 14)GET /auth/dcql-queries

7. Migration checklist​

  • Repoint clients at <base>/v1 delete per-service version segments (/v5, /v4, /v3, /v1).
  • Rename paths: /authorisation/v4/* to /auth/*, /trusted-*-registry/* to /*-registry/*.
  • Change every requested scope from _ to : (didr_write to did-registry:write, …).
  • Replace the per-service jsonrpc build, sign, sendSignedTransaction flow with: build calldata from GET /v1/<registry>/abi, sign locally, POST /v1/ledger eth_sendRawTransaction.
  • Move Besu read calls from /ledger/v4/blockchains/besu to /v1/ledger.
  • For DID writes, keep the same method names (updateBaseDocument, addVerificationMethod, addController, …) just move them onto /v1/ledger eth_sendRawTransaction.
  • For Timestamp hash-timestamping and Track & Trace createDocument, add the commitTimestampHashes / commitCreateDocument transaction (same sender, ≥ 5 blocks earlier) and pass the salt on the reveal.
  • Handle 405 / 406 and the 400 Invalid Contract Address response and parse POST /auth/token errors as OAuth 2.0 error JSON, not problem+json.
  • For DID onboarding, the did-registry:invite token is locked to one key. It carries an RFC 7800 claim "cnf": { "jkt": "<RFC 7638 thumbprint>" } of the key the holder proved possession of in the Verifiable Presentation. The insertDidDocument call you send through POST /v1/ledger must register a publicKey whose RFC 7638 thumbprint matches cnf.jkt. It cannot register any other key.
  • The DID document Accept header still works on GET /v1/did-registry/identifiers/{did}, no client change needed. An exact Accept: application/did+json returns the @context-free form. Anything else acceptable, Accept: */* included, returns application/did+ld+json. An unsatisfiable Accept gets 406.
  • Drop checkController and the Policies Registry /users reads. Neither has a v1 REST replacement. For checkController (formerly POST /did-registry/v5/identifiers/{did}/actions), read the controller field from GET /v1/did-registry/identifiers/{did}, or filter the collection with GET /v1/did-registry/identifiers?controller=<did> and check whether the DID appears, or call the DID Registry contract with eth_call through POST /v1/ledger. For GET /trusted-policies-registry/v3/users and GET /trusted-policies-registry/v3/users/{user}, read the same attributes on-chain by calling the Policies Registry contract with eth_call through POST /v1/ledger, using the address and ABI from GET /v1/policies-registry/abi. eth_call reads need no authentication. Note that GET /v1/policies-registry/subjects and GET /v1/policies-registry/subjects/{subject} still exist and cover policy-to-subject lookups.
  • Optional: adopt combined-scope tokens and :<0x-address> instance targeting (registered beacon proxies only) use GET /v1/<registry>/details to check inter-registry links.
  • Migrate to the OpenID4VP 1.0 (final) grant. Draft 14 is removed. Replace GET /auth/presentation-definitions with GET /auth/dcql-queries, submit vp_token as a JSON object keyed by credential-query identifier, omit presentation_submission, and for the seven combinable scopes present a self-issued SelfAttestation credential in place of an empty presentation.
  • Handle Problem Details: match on type (urn:ebsi:problems:*), read the errors array, and don't parse detail.
  • Discovery: stop reading authorization_endpoint and presentation_definition_endpoint, and verify ID token signatures (ES256).