Summary
Get Started with Canton, the JSON Ledger API, TypeScript and WebSockets
This tutorial shows you how to interact with a Canton Ledger using the JSON Ledger API, WebSockets and TypeScript.
Overview
You will use and modify an existing example project provided with the Canton distribution.
Prerequisites
You should be familiar with the basics of TypeScript, WebSockets and tools such as npm and node.js.
Tools
Before starting, ensure you have the following installed:
- Node.js and npm — Download from https://nodejs.org/en/download/. Recommended version: 18.20.x or later.
- Dpm — Install following these instructions.
- Canton — Includes pre-built examples. See the Canton installation for details.
Example TypeScript Project with WebSockets
Open a terminal and navigate to the JSON Ledger API example folder:
cd <canton_installation>/examples/09-json-ledger-api
Start Canton:
./run.sh
Once the Canton console is ready, open a new terminal and navigate to the TypeScript example folder:
cd <canton_installation>/examples/09-json-ledger-api/typescript
Running the Example
Install the project dependencies:
npm install
You may see some warnings, which can be ignored for now.
The JSON Ledger API provides an OpenAPI specification. You can use this to generate TypeScript client classes (stubs).
To generate the TypeScript client:
npm run generate_api
Check the generated code in the generated/api folder. It should include the TypeScript classes for the API.
less generated/api/ledger-api.d.ts
This file contains definitions for services and models, such as /v2/commands/submit-and-wait, CreateCommand, and many others.
Next, generate TypeScript types from the Daml model:
npm run generate_daml_bindings
This creates bindings in the ./generated folder. Each Daml module has its own subfolder, for example:
generated/model-tests-1.0.0/lib/Iou
Now compile the TypeScript code:
npm run build
Once the build succeeds, run the example:
npm run scenario_websocket
You should see output similar to:
Alice creates contract
Ledger offset: 23
...
Bob accepts transfer
...
End of scenario
Code Highlights
The JSON Ledger API client is configured in src/client.ts:
import createClient from "openapi-fetch";
import type { paths } from "../generated/api/ledger-api";
export const client = createClient<paths>({ baseUrl: "http://localhost:7575" });
WebSockets Highlights
The WebSocket connection establishment logic is mentioned in src/index_websocket.ts:
const BASE_WS_URL = 'ws://localhost:7575';
export async function connectWs(uri: string): Promise<WebSocket> {
return new Promise<WebSocket>((resolve, reject) => {
const ws = new WebSocket(BASE_WS_URL + uri, ["daml.ws.auth"]);
ws.onopen = () => {
resolve(ws);
};
ws.onerror = (err: any) => {
reject(err);
};
})
}
The openapi-fetch library is used to create the API client.
Allocating a Party
In src/user.ts, a party is allocated as follows:
const resp = await client.POST("/v2/parties", {
body: {
partyIdHint: user,
identityProviderId: ""
}
});
Note
openapi-fetchuses the Indexed Access Types TypeScript feature to provide type safety. The example above looks like untyped JavaScript, but it is in fact type-safe.
Creating a Contract
In src/commands.ts, a contract is created using:
export async function createContract(userParty: string): Promise<components["schemas"]["SubmitAndWaitResponse"]> {
const iou: Iou.Iou = {
issuer: userParty,
owner: userParty,
currency: "USD",
amount: "100",
observers: []
};
const command: components["schemas"]["CreateCommand"] = {
createArguments: iou,
templateId: Iou.Iou.templateId
};
const jsCommands = makeCommands(userParty, [{ CreateCommand: command }]);
const resp = await client.POST("/v2/commands/submit-and-wait", {
body: jsCommands
});
return valueOrError(resp);
}
Querying Contracts
In src/index_websocket.ts, contracts are queried like this:
return new Promise<components["schemas"]["CreatedEvent"][]>((resolve, reject) => {
let contracts: components["schemas"]["CreatedEvent"][] = [];
return connectWs("/v2/state/active-contracts").then(ws => {
ws.send(JSON.stringify(filter));
ws.onmessage = function (msg: any) {
const data = JSON.parse(msg.data) as components["schemas"]["JsGetActiveContractsResponse"];
const contractEntry = (data.contractEntry as components["schemas"]["JsContractEntry"]);
let contract: components["schemas"]["CreatedEvent"] | undefined = undefined;
if ("JsActiveContract" in contractEntry){
contract = contractEntry.JsActiveContract.createdEvent as components["schemas"]["CreatedEvent"];
}
if (contract !== undefined) {
contracts.push(contract);
}
}
ws.onclose = function () {
resolve(contracts);
}
})
})
Extending the Example
In this step, you extend the Iou template with a new field and update the TypeScript code accordingly.
Open the Io.daml file in canton/examples/09-json-ledger-api/model.
Modify the Iou template to include a new comment field:
template Iou
with
issuer : Party
owner : Party
currency : Text
amount : Decimal
observers : [Party]
comment : Optional Text -- added field
where
-- leave the rest of the template unchanged
The
commentfield is optional, making this a backwards-compatible change.
Stop the Canton server (Ctrl+C) and restart it:
./run.sh
Rebuild the TypeScript bindings:
npm run generate_daml_bindings
Update the createContract function in src/commands.ts to include the new field:
export async function createContract(userParty: string): Promise<components["schemas"]["SubmitAndWaitResponse"]> {
const iou: Iou.Iou = {
issuer: userParty,
owner: userParty,
currency: "USD",
amount: "100",
observers: [],
comment: "This is a test comment" // new field
};
// leave the rest of the function unchanged
Rebuild the TypeScript code:
npm run build
Run the example:
npm run scenario_websocket
You won’t see the new comment field in the output yet. To display it, modify the showAcs function call in src/index.ts to include the new field:
showAcs(
contracts,
["owner", "amount", "currency", "comment"],
c => [c.owner, c.amount, c.currency, c.comment]
);
Then, execute npm run scenario_websocket once again.
JSON Ledger API Service V2
Note
THIS SECTION NEEDS TO MOVE TO
sdk/sdlc-howtos/applications/integrate/http-json
This section describes the new JSON Ledger API Service that is a part of Canton 3.x. If you are looking for the legacy JSON Ledger API, please navigate to JSON Ledger API V1.
The JSON Ledger API provides a way to interact with a ledger using HTTP and JSON instead of the gRPC Ledger API. The JSON Ledger API provides almost all the functionality available in the gRPC Ledger API, although the HTTP protocol imposes some minor limitations.
The full reference consists of:
We recommend using the OpenAPI/AsyncAPI specification to generate a client for your technology (Java, TypeScript).
We welcome feedback about the JSON Ledger API on our issue tracker, or on our forum.
Prerequisites
It is assumed that you know the basics of HTTP Protocol, HTTP methods, HTTP request and response headers (including authorization), JSON format, and WebSockets.
In order to test JSON Ledger API you need a tool such as:
- command line
curl - Postman
wscat, for testing WebSockets
Run the JSON Ledger API
Prepare canton configuration
canton {
participants {
participant1 {
storage {
type = memory
}
admin-api {
port = 14012
}
http-ledger-api {
port = 8080
}
}
}
}
Save the configuration as, for instance, json_enabled.conf.
Note
In Canton 3.1 and earlier
http-ledger-apiis namedhttp-ledger-api-experimental.
Start canton
You can run the JSON Ledger API alongside any ledger exposing the gRPC Ledger API you want. If you don’t have an existing ledger, you can start Canton with the configuration above:
bin/canton -c json_enabled.conf
-
Ensure that the Canton console is started.
-
Check that JSON Ledger API is running. In a terminal, use
curlto get the OpenAPI documentation:curl localhost:8080/docs/openapiAlternatively, open the web browser and navigate to:
http://localhost:8080/docs/openapi
You should see YAML file contents that start with:
openapi: 3.1.0 info: title: JSON Ledger API HTTP endpointsCongratulations, you have successfully started JSON Ledger API.
Note
Your JSON Ledger API service should never be exposed to the Internet. When running in production the JSON Ledger API should be behind a reverse proxy, such as via NGINX.
JSON Ledger API Advanced configuration
The full set of configurable options that can be specified via config file is listed below:
{
server {
// IP address that JSON Ledger API service listens on. Defaults to 127.0.0.1.
address = "127.0.0.1"
// JSON Ledger API service port number. A port number of 0 will let the system pick an ephemeral port.
port = 7575
// Prefix added to all JSON endpoints.
path-prefix = "example/prefix"
}
websocket-config {
// Maximum number of elements returned when using the HTTP POST alternative.
http-list-max-elements-limit = 1024
// Wait time for new elements before returning the list via the HTTP POST alternative.
http-list-wait-time = "1s"
}
}
Access Tokens
Each request to the JSON Ledger API Service must come with an access token (JWT). This also includes development setups using an unsecured sandbox. The JSON Ledger API Service does not hold on to the access token, which will be only used to fulfill the request it came along with. The same token will be used to issue the request to the Ledger API. The only exceptions are documentation endpoints, which do not strictly require tokens.
For a reference on the JWT Tokens we use, please read Authorization.
For a ledger without authorization, e.g., the default configuration of Daml Sandbox, you can use jwt.io (or the JWT library of your choice) to generate your token. You can use an arbitrary secret here. The default “header” is fine. Under “Payload”, fill in:
{
"aud": [],
"exp": null,
"iss": null,
"scope": "ExpectedTargetScope",
"sub": "actAs-Alice::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c"
}
The value after actAs is specified as a list and you provide it with the party that you want to use, such as in the example above which uses Alice for a party. actAs may include more than one party as the JSON Ledger API supports multi-party submissions.
The party should reference an already allocated party. In the Canton console a party can be allocated using:
val alice = participant1.parties.enable("Alice")
alice.toProtoPrimitive // this will display a full party ID
Then the Encoded box should have your token, ready for passing to the service as described in the following sections.
Alternatively, here is a token you can use for testing:
{
"aud": [],
"exp": null,
"iss": null,
"scope": "ExpectedTargetScope",
"sub": "participant_admin"
}
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOltdLCJleHAiOm51bGwsImlzcyI6bnVsbCwic2NvcGUiOiJFeHBlY3RlZFRhcmdldFNjb3BlIiwic3ViIjoicGFydGljaXBhbnRfYWRtaW4ifQ.8bABNm1t718TuJXwRQOF2gXOclrL38t0uCmWkIT7Pcg
Auth via HTTP
Set the HTTP header Authorization: Bearer <paste-jwt-here>.
Example header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiJNeUxlZGdlciIsImFwcGxpY2F0aW9uSWQiOiJIVFRQLUpTT04tQVBJLUdhdGV3YXkiLCJhY3RBcyI6WyJBbGljZSJdfX0.34zzF_fbWv7p60r5s1kKzwndvGdsJDX-W4Xhm4oVdpk
Example using curl:
curl -X 'POST' \
'http://localhost:8080/v2/users' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOltdLCJleHAiOm51bGwsImlzcyI6bnVsbCwic2NvcGUiOiJFeHBlY3RlZFRhcmdldFNjb3BlIiwic3ViIjoicGFydGljaXBhbnRfYWRtaW4ifQ.8bABNm1t718TuJXwRQOF2gXOclrL38t0uCmWkIT7Pcg' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d @body_create_user.json
Auth via WebSockets
WebSocket clients support a “subprotocols” argument (sometimes simply called “protocols”); this is usually in a list form but occasionally in comma-separated form. Check the documentation for your WebSocket library of choice for details.
For HTTP JSON requests, you must pass two subprotocols:
daml.ws.authjwt.token.paste-jwt-here
Example:
jwt.token.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiJNeUxlZGdlciIsImFwcGxpY2F0aW9uSWQiOiJIVFRQLUpTT04tQVBJLUdhdGV3YXkiLCJhY3RBcyI6WyJBbGljZSJdfX0.34zzF_fbWv7p60r5s1kKzwndvGdsJDX-W4Xhm4oVdpk
Example using wscat:
wscat -c http://localhost:8080/v2/state/active-contracts \
-s "jwt.token.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOltdLCJleHAiOm51bGwsImlzcyI6bnVsbCwic2NvcGUiOiJFeHBlY3RlZFRhcmdldFNjb3BlL1dpdGgtRGFzaC9BbmRfVW5kZXJzY29ycyIsInN1YiI6ImFjdEFzLUFsaWNlOjoxMjIwOTllNmI3YzkyZTEyZWIwMjZmNDgzZjU5YTczZTQ4MTQ5ZGM2ZTYzMGE0ZjRmOGZiOTViOGQyNjkyMTliMzU2YyJ9.FmJoP6eK2Yg6CAmO2G0SYosMS4IOai_6HlvO1siYNUA" \
-s "daml.ws.auth"
HTTP Status Codes
The JSON Ledger API reports errors using standard HTTP status codes. It divides HTTP status codes into three groups indicating:
- success (200)
- failure due to a client-side problem (400, 401, 403)
- failure due to a server-side problem (500)
Most common HTTP status codes are:
200- OK400- Bad Request (Client Error)401- Unauthorized, authentication required403- Forbidden, insufficient permissions (TODO)500- Internal Server Error
JSON Ledger API Errors
When the gRPC Ledger API returns an error code, the JSON Ledger API maps it to one of the above codes according to the official gRPC to HTTP code mapping.
If a client’s HTTP GET or POST request reaches an API endpoint, the corresponding response will always contain a JSON object. Either an expected message (corresponding to endpoint) or an error object specified as in the example below:
{
"cause": "The submitted request has invalid arguments: Cannot unassign contract `ContractId(00a7feb291fe1be6289c4d77f3a432b623083ed3f49bf8535f8571aa8bdf68b647ca10122006207a71ac6ed62dae27429b43e456dfbbc411a49d67b7e0067bef4c914c6e8a)`: source and target domains are the same",
"code": "INVALID_ARGUMENT",
"context": {
"category": "8",
"definite_answer": "false",
"participant": "participant1",
"test": "JsonV2Tests",
"tid": "99e67812af315256ef9a02a9fdb94646"
},
"correlationId": null,
"definiteAnswer": null,
"errorCategory": 8,
"grpcCodeValue": 3,
"resources": [],
"retryInfo": null,
"traceId": "99e67812af315256ef9a02a9fdb94646"
}
Where:
cause- a textual message containing readable error reasoncode- a Ledger API error codecontext- a Ledger API context of an errortraceId- telemetry tracing idgrpcCodeValueanderrorCategory- defined in Canton Error Codes
See The Ledger API error codes (Canton Error Codes Reference) for more details about error codes from Ledger API.
WebSockets Errors
In the case of WebSockets, an error might be delivered as a frame. Each incoming frame can either be a correct response (corresponding to the endpoint definition) or an error frame in the format above.
Example of successful Response, HTTP Status: 200 OK
- Content-Type:
application/json - Content:
{
"partyDetails": {
"party": "Carol:-_ a9c80151-002c-445e-9fae-3caf111e3ac4::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c",
"isLocal": true,
"localMetadata": {
"resourceVersion": "0",
"annotations": {}
},
"identityProviderId": ""
}
}
Create a New Contract
To create an Iou contract from the Quickstart guide:
template Iou
with
issuer : Party
owner : Party
currency : Text
amount : Decimal
observers : [Party]
HTTP Request
- URL, one of:
/v2/commands/submit-and-wait-for-transaction-tree/v2/commands/submit-and-wait-for-transaction/v2/commands/submit-and-wait
- Method:
POST - Content-Type:
application/json - Content:
{
"actAs": [
"Alice::1220ceff195fd59bbc357415ce25d7a6edb95475b134b1bbb6d70c8fa3dd3f8f32ad"
],
"userId": "app1",
"commandId": "somecommandid2",
"commands": [
{
"CreateCommand": {
"createArguments": {
"observers": [],
"issuer": "Alice::1220ceff195fd59bbc357415ce25d7a6edb95475b134b1bbb6d70c8fa3dd3f8f32ad",
"amount": "999.99",
"currency": "USD",
"owner": "Alice::1220ceff195fd59bbc357415ce25d7a6edb95475b134b1bbb6d70c8fa3dd3f8f32ad"
},
"templateId": "#model-tests:Iou:Iou"
}
}
]
}
Where:
templateIdis the contract template identifier with one of the following formats:#<package name>:<module>:<entity>or<package ID>:<module>:<entity>.- The package name format works with the smart contract upgrading feature so that contracts with different package IDs but the same package name can be used in a uniform manner.
- Using the package ID format refers to a single package.
createArgumentscontains contract fields as defined in the Daml template and formatted according to Daml-LF JSON encoding.commandIdis an optional field, a unique string identifying the command.actAsis a non-empty list of parties, overriding the set from the JWT user; must be a subset of the JWT user’s set.
Note
Only a subset of the possible fields is shown in the example. Refer to the OpenAPI specification for additional options.
HTTP Response
The response will differ according to the endpoint URL chosen to send the transaction. The example below assumes
/v2/commands/submit-and-wait-for-transaction-tree.
- Content-Type:
application/json - Content:
{
"transactionTree": {
"updateId": "12208230390f6af5a6e85e70af2f0ad3cdb4f2df2f1e41778a58ce888e128d2a1245",
"commandId": "somecommandid21",
"workflowId": "",
"effectiveAt": "2025-01-27T15:30:36.430940Z",
"offset": 18,
"eventsById": {
"0": {
"CreatedTreeEvent": {
"value": {
"offset": 18,
"nodeId": 0,
"contractId": "005f954f6759aa04b0998676689610fc1d84c5d31a46819f7bfdd7233ecd7e18b3ca1012207866cf05034eddcc7b0619ab06a46514b21732a0da6404f3f2fbb90872ed6289",
"templateId": "19b9fc297d4649bab9fd0eeb3ce23da763cb9ed7dfaedeb1281ac7d7f65a7883:Iou:Iou",
"contractKey": null,
"createArgument": {
"issuer": "Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55",
"owner": "Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55",
"currency": "USD",
"amount": "999.9900000000",
"observers": []
},
"createdEventBlob": "",
"interfaceViews": [],
"witnessParties": [
"Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55"
],
"signatories": [
"Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55"
],
"observers": [],
"createdAt": "2025-01-27T15:30:36.430940Z",
"packageName": "model-tests"
}
}
}
},
"rootNodeIds": [0],
"synchronizerId": "da1::122002b3938ee8f7f0a757c5db698fc6020352717ee53da9023485f8b9e0dc2ea360",
"traceContext": {
"traceparent": "00-bfc45078ca09495a4101b227f4c36bef-6471ff18d20230eb-01",
"tracestate": null
},
"recordTime": "2025-01-27T15:30:37.291866Z"
}
}
Where:
eventsByIdcontains ledger events with details of created contract(s).
Exercise by Contract ID
The JSON command below demonstrates how to exercise an Iou_Transfer choice on an Iou contract:
choice Iou_Transfer : ContractId IouTransfer
with
newOwner : Party
controller owner
do create IouTransfer with iou = this; newOwner
HTTP Request
- URL, one of:
/v2/commands/submit-and-wait-for-transaction-tree/v2/commands/submit-and-wait-for-transaction/v2/commands/submit-and-wait
- Method:
POST - Content-Type:
application/json - Content:
{
"actAs": [
"Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55"
],
"userId": "app1",
"commandId": "somecommandid2",
"commands": [
{
"ExerciseCommand": {
"choice": "Iou_Transfer",
"choiceArgument": {
"newOwner": "Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55"
},
"contractId": "005f954f6759aa04b0998676689610fc1d84c5d31a46819f7bfdd7233ecd7e18b3ca1012207866cf05034eddcc7b0619ab06a46514b21732a0da6404f3f2fbb90872ed6289",
"templateId": "#model-tests:Iou:Iou"
}
}
]
}
Where:
templateIdis the contract template or interface identifier, same as in the create request.choiceInterfaceIdis an optional template or interface that defines the choice, same format astemplateId.contractIdis the contract identifier, the value from the create response.choiceis the Daml contract choice being exercised.argumentis the contract choice argument(s).
HTTP Response
- Content-Type:
application/json - Content:
{
"completionOffset": 11,
"updateId": "12205cec9cccf807dd95df3c0ab270bd286ef1bc9b84aa26f7ecf1180248aaa9d070"
}
Where:
update_idis the ID of the transaction that resulted from the submitted command.completionOffsetis the ledger offset of the transaction containing the exercise’s ledger changes.
Create and Exercise in the Same Transaction
This command allows creating a contract and exercising a choice on the newly created contract in the same transaction.
HTTP Request
- URL, one of:
/v2/commands/submit-and-wait-for-transaction-tree/v2/commands/submit-and-wait-for-transaction/v2/commands/submit-and-wait
- Method:
POST - Content-Type:
application/json - Content:
{
"actAs": [
"Alice::1220870d67dba562b868d0256cb2968dbc59a7a6bdbc4b82ee623ff96c8cda3afb9a"
],
"userId": "defaultuser2",
"commandId": "somecommandid3",
"commands": [
{
"CreateAndExerciseCommand": {
"choice": "Iou_Transfer",
"createArguments": {
"observers": [],
"issuer": "Alice::1220df15d08ac34527e46492a6ee48a723e3d02ed3ec20a05ebf64be47173f24407f",
"amount": "999.99",
"currency": "USD",
"owner": "Alice::1220df15d08ac34527e46492a6ee48a723e3d02ed3ec20a05ebf64be47173f24407f"
},
"choiceArgument": {
"newOwner": "Alice::1220870d67dba562b868d0256cb2968dbc59a7a6bdbc4b82ee623ff96c8cda3afb9a"
},
"templateId": "#model-tests:Iou:Iou"
}
}
]
}
Where:
templateIdis the initial contract template identifier, in the same format as in the create request.payloadis the initial contract fields as defined in the Daml template and formatted according to Daml-LF JSON encoding.choiceInterfaceIdis an optional template or interface that defines the choice, same format astemplateId.choiceis the Daml contract choice being exercised.argumentis the choice argument(s).
HTTP Response
Please note that the response below is for a consuming choice. It contains:
- Content-Type:
application/json - Content:
{
"transaction": {
"command_id": "somecommandid",
"domainId": "domain1::12208f22808f6fe77f8689d0d565488e34e9a3abb2eaf37270f6e5b2b2073894db9f",
"effectiveAt": "1970-01-01T00:00:00Z",
"events": [
{
"CreatedEvent": {
"contractId": "00811e868ffe78638ecf14b8d69a5df6eafd8b16f13a431117ee26981cc39f40e1ca101220c15b891d2cac1287d5067594c81f60b722141204f63f2a27c3328a70467c5018",
"contractKey": null,
"createArgument": {
"iou": {
"amount": "999.9900000000",
"currency": "USD",
"issuer": "Alice_ffe0d699-2978-42d7-a315-b7faec7ba95a::1220d7b8bc76f04500216f07e1576da75db0e811ea1e01d5688140816823615a34bf",
"observers": [],
"owner": "Alice_ffe0d699-2978-42d7b8bc76f04500216f07e1576da75db0e811ea1e01d5688140816823615a34bf"
},
"newOwner": "Alice_ffe0d699-2978-42d7a315-b7faec7ba95a::1220d7b8bc76f04500216f07e1576da75db0e811ea1e01d5688140816823615a34bf"
},
"createdAt": "1970-01-01T00:00:00Z",
"createdEventBlob": "",
"eventId": "#1220ad3e6509fa8977478aa49cf3261b376dd55b06f6409e3ec59553272c89d3616c:2",
"interfaceViews": [],
"nodeId": 2,
"observers": [],
"offset": 12,
"packageName": "model-tests",
"signatories": [
"Alice_ffe0d699-2978-42d7-a315-b7faec7ba95a::1220d7b8bc76f04500216f07e1576da75db0e811ea1e01d5688140816823615a34bf"
],
"templateId": "a36cfff1da2f15da3d760c872860bf03140aa699f73b57b639fd40dfe8156cfe:Iou:IouTransfer",
"witnessParties": [
"Alice_ffe0d699-2978-42d7-a315-b7faec7ba95a::1220d7b8bc76f04500216f07e1576da75db0e811ea1e01d5688140816823615a34bf"
]
}
}
],
"offset": 12,
"recordTime": "1970-01-01T00:00:00.000042Z",
"traceContext": {
"traceparent": "00-d84d23ff01031adbcf76f19525fa62fb-003327aa6c8ede2d-01",
"tracestate": null
},
"updateId": "1220ad3e6509fa8977478aa49cf3261b376dd55b06f6409e3ec59553272c89d3616c",
"workflowId": ""
}
}
Get All Active Contracts
List all currently active contracts for all known templates.
Note
You can only query active contracts with the
/v2/state/active-contractsendpoint. Archived contracts (those that were archived or consumed during an exercise operation) will not be shown in the results.
HTTP WebSocket Request
- URL:
/v2/state/active-contracts - Protocol: WebSocket
- Content:
Send a single query frame:
{
"filter": {
"filtersByParty": {},
"filtersForAnyParty": {
"cumulative": [
{
"identifierFilter": {
"WildcardFilter": {
"value": {
"includeCreatedEventBlob": true
}
}
}
}
]
}
},
"verbose": false,
"activeAtOffset": 23
}
Note
Be careful and do enter a proper value for
activeAtOffset.
Example Response frames
The response might look like an example below:
{
"workflowId": "",
"contractEntry": {
"JsActiveContract": {
"createdEvent": {
"offset": 23,
"nodeId": 1,
"contractId": "00afee8d924751d4677ae7f09ed7c38c84b9e8601bb2c75bc30f832f866896755dca101220b62bec39ca9352f599cc432bbca1969a33d368a0ee4cdad56093226f27103dc2",
"templateId": "19b9fc297d4649bab9fd0eeb3ce23da763cb9ed7dfaedeb1281ac7d7f65a7883:Iou:IouTransfer",
"contractKey": null,
"createArgument": {
"iou": {
"issuer": "Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55",
"owner": "Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55",
"currency": "USD",
"amount": "999.9900000000",
"observers": []
},
"newOwner": "Alice::1220743926ada1ba52150596c07712995f4a4f4f8fb95b8d269219b356c"
},
"createdEventBlob": "CgMyLjES0QQKRQCv7o2SR1HUZ3rn8J7Xw4yEuehgG7LHW8MPgy+GaJZ1XcoQEiC2K+w5ypNS9ZnMQyu8oZaaM9NooO5M2tVgkyJvJxA9whILbW9kZWwtdGVzdHMaVApAMTliOWZjMjk3ZDQ2NDliYWI5ZmQwZWViM2NlMjNkYTc2M2NiOWVkN2RmYWVkZWIxMjgxYWM3ZDdmNjVhNzg4MxIDSW91GgtJb3VUcmFuc2ZlciKiAmqfAgrLAQrIAWrFAQpPCk06S0FsaWNlOjoxMjIwNzQzOTI2YWRhMWJhNTIxNTA1OTZjMDc3MTI5OTVmNGExZDJjNDNlZmNjOWQ5ZTU2OWJiODJiMTk5OGE0NmM1NQpPCk06S0FsaWNlOjoxMjIwNzQzOTI2YWRhMWJhNTIxNTA1OTZjMDc3MTI5OTVmNGExZDJjNDNlZmNjOWQ5ZTU2OWJiODJiMTk5OGE0NmM1NQoHCgVCA1VTRAoSChAyDjk5OS45OTAwMDAwMDAwCgQKAloACk8KTTpLQWxpY2U6OjEyMjA3NDM5MjZhZGExYmE1MjE1NTA1OTZjMDc3MTI5OTVmNGExZDJjNDNlZmNjOWQ5ZTU2OWJiODJiMTk5OGE0NmM1KktBbGljZTo6MTIyMDc0MzkyNmFkYTFiYTUyMTUwNTk2YzA3NzEyOTk1ZjRhMWQyYzQzZWZjYzlkOWU1NjliYjgyYjE5OThhNDZjNTU5Agm71LEsBgBCKgomCiQIARIgyJpshr25uq/bCHZXro6K5Wl3FyO/llMqF2W5DbrzoKAQHg==",
"interfaceViews": [],
"witnessParties": [
"Alice::1220743926ada1ba52150596c07712995f4a1d2c43efcc9d9e569bb82b1998a46c55"
],
"signatories": [
"Alice::1220743926ada1ba52150596c07712995f4a4f4f8fb95b8d269219b356c"
],
"observers": [],
"createdAt": "2025-01-27T15:35:50.124802Z",
"packageName": "model-tests"
},
"synchronizerId": "da1::122002b3938ee8f7f0a757c5db698fc6020352717ee53da9023485f8b9e0dc2ea360",
"reassignmentCounter": 0
}
}
}
Get All Active Contracts via HTTP
For each WebSocket endpoint, we provide an alternative HTTP POST endpoint that returns the same results synchronously. HTTP POST alternatives are less robust. They are also less efficient because:
- They impose a higher load on the server.
- The result size is always limited (refer to the configuration).
- There is a slight performance overhead and delay in retrieving results.
We recommend using WebSockets for production environments, as they offer better performance and scalability. HTTP alternatives are suitable for prototyping and less demanding applications with lower data and load requirements.
HTTP Request
- URL:
/v2/state/active-contracts - Protocol: HTTP
- Method:
POST - Content-Type:
application/json - Content:
{
"filter": {
"filtersByParty": {},
"filtersForAnyParty": {
"cumulative": [
{
"identifierFilter": {
"WildcardFilter": {
"value": {
"includeCreatedEventBlob": true
}
}
}
}
]
}
},
"verbose": false,
"activeAtOffset": 23
}
Note
Be careful and do enter a proper value for
activeAtOffset.
Example HTTP Response
[
{
"workflowId": "",
"contractEntry": {
"JsActiveContract": {
"createdEvent": {
"offset": 18,
"nodeId": 0,
"contractId": "0047c4cc5cf2f0bda731a172fd90f5cc592f9d3ad2e5c45450d9de1cc4aa5adb71ca101220a8faa4673f7e9c28319eb57f98aad84d0bf1395fed971f24eb486bc4f48d9828",
"templateId": "19b9fc297d4649bab9fd0eeb3ce23da763cb9ed7dfaedeb1281ac7d7f65a7883:Iou:Iou",
"contractKey": null,
"createArgument": {
"issuer": "Alice::1220b6683b7d875557055126f8b8f059d9c070ece95e7784d279a135c8c1285a4f1f",
"owner": "Alice::1220b6683b7d875557055126f8b8f059d9c070ece95e7784d279a135c8c1285a4f1f",
"currency": "USD",
"amount": "999.9900000000",
"observers": []
},
"createdEventBlob": "CgMyLjES7wMKRQBHxMxc8vC9pzGhcv2Q9cxZL5060uXEVFDZ3hzEqlrbccoQEiCo+qRnP36cKDGetX+YqthNC/E5X+2XHyTrSGvE9I2YKBILbW9kZWwtdGVzdHMaTApAMTliOWZjMjk3ZDQ2NDliYWI5ZmQwZWViM2NlMjNkYTc2M2NiOWVkN2RmYWVkZWIxMjgxYWM3ZDdmNjVhNzg4MxIDSW91GgNJb3UiyAFqxQEKTwpNOktBbGljZTo6MTIyMGI2NjgzYjdkODc1NTU3MDU1MTI2ZjhiOGYwNTlkOWMwNzBlY2U5NWU3Nzg0ZDI3OWExMzVjOGMxMjg1YTRmMWYKTwpNOktBbGljZTo6MTIyMGI2NjgzYjdkODc1NTU3MDU1MTI2ZjhiOGYwNTlkOWMwNzBlY2U5NWU3Nzg0ZDI3OWExMzVjOGMxMjg1YTRmMWYKBwoFQgNVU0QKEgoQMg45OTkuOTkwMDAwMDAwMAoECgJaACpLQWxpY2U6OjEyMjBiNjY4M2I3ZDg3NTU1NzA1NTEyNmY4YjhmMDU5ZDljMDcwZWNlOTVlNzc4NGQyNzlhMTM1YzhjMTI4NWE0ZjFmOS/eEk3FLAYAQioKJgokCAESIANCONtfqgCanbAYj1wMaIE3YKLDoH+lM9kT19z2h1F4EB4=",
"interfaceViews": [],
"witnessParties": [
"Alice::1220b6683b7d875557055126f8b8f059d9c070ece95e7784d279a135c8c1285a4f1f"
],
"signatories": [
"Alice::1220b6683b7d875557055126f8b8f059d9c070ece95e7784d279a135c8c1285a4f1f"
],
"observers": [],
"createdAt": "2025-01-28T14:49:33.525551Z",
"packageName": "model-tests"
},
"synchronizerId": "da1::1220ff7a4caf08bd4442003c65c2f351ebc15cc3a2063fd876da82e3a64a2348d30b",
"reassignmentCounter": 0
}
}
}
]
Fetch Parties by Identifiers
- URL:
/v2/parties/{partyId} - Method:
GET - Content-Type:
application/json - Content: empty
HTTP Response
- Content-Type:
application/json - Content:
{
"partyDetails": [
{
"party": "Alice::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c",
"isLocal": true,
"localMetadata": {
"resourceVersion": "",
"annotations": {}
},
"identityProviderId": ""
}
]
}
Where:
identifier- a stable unique identifier of a Daml partyisLocal- true if the party is hosted by the backing participant
The result might contain an empty JSON array partyDetails.
Fetch All Known Parties
- URL:
/v2/parties - Method:
GET - Content: empty
HTTP Response
{
"partyDetails": [
{
"party": "Alice::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c",
"isLocal": true,
"localMetadata": {
"resourceVersion": "",
"annotations": {}
},
"identityProviderId": ""
},
{
"party": "Bob::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c",
"isLocal": true,
"localMetadata": {
"resourceVersion": "",
"annotations": {}
},
"identityProviderId": ""
},
{
"party": "participant1::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c",
"isLocal": true,
"localMetadata": {
"resourceVersion": "",
"annotations": {}
},
"identityProviderId": ""
}
],
"nextPageToken": ""
}
Note
nextPageTokenis used by gRPC if there is paging enabled. In the current resource version of JSON Ledger API, paging is not yet supported.
Allocate a New Party
HTTP Request
- URL:
/v2/parties - Method:
POST - Content-Type:
application/json - Content:
{
"identityProviderId": "",
"localMetadata": null,
"partyIdHint": "Carol"
}
HTTP Response
{
"partyDetails": {
"party": "Carol:-_ a9c80151-002c-445e-9fae-3caf111e3ac4::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c",
"isLocal": true,
"localMetadata": {
"resourceVersion": "0",
"annotations": {}
},
"identityProviderId": ""
}
}
Create a New User
HTTP Request
- URL:
/v2/users - Method:
POST - Content-Type:
application/json - Content:
{
"rights": [
{
"kind": {
"CanActAs": {
"value": {
"party": "Bob::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c"
}
}
}
}
],
"user": {
"id": "CreatedUser1",
"identityProviderId": "",
"isDeactivated": false,
"metadata": null,
"primaryParty": ""
}
}
Only the userId field in the request is required, which means that a JSON object containing only it is a valid request to create a new user.
HTTP Response
{
"user": {
"id": "CreatedUser1",
"primaryParty": "",
"isDeactivated": false,
"metadata": {
"resourceVersion": "0",
"annotations": {}
},
"identityProviderId": ""
}
}
Get Specific User Information
HTTP Request
- URL:
/v2/users/{userId} - Method:
GET - Content-Type:
application/json - Content: empty
HTTP Response
{
"user": {
"id": "CreatedUser1",
"primaryParty": "",
"isDeactivated": false,
"metadata": {
"resourceVersion": "0",
"annotations": {}
},
"identityProviderId": ""
}
}
Delete Specific User
HTTP Request
- URL:
/v2/users/{userId} - Method:
DELETE - Content-Type:
application/json - Content: empty
HTTP Response
{}
An empty response with a status 200 confirms successful deletion.
List Users
HTTP Request
- URL:
/v2/users - Method:
GET
HTTP Response
{
"users": [
{
"id": "CreatedUser",
"primaryParty": "",
"isDeactivated": false,
"metadata": {
"resourceVersion": "0",
"annotations": {}
},
"identityProviderId": ""
},
{
"id": "participant_admin",
"primaryParty": "",
"isDeactivated": false,
"metadata": {
"resourceVersion": "0",
"annotations": {}
},
"identityProviderId": ""
}
],
"nextPageToken": ""
}
Grant User Rights
HTTP Request
- URL:
/v2/users/{userId}/rights - Method:
POST - Content-Type:
application/json - Content:
{
"identityProviderId": "",
"rights": [
{
"kind": {
"ParticipantAdmin": {
"value": {}
}
}
}
],
"userId": "CreatedUser1"
}
HTTP Response
{
"newlyGrantedRights": [
{
"kind": {
"ParticipantAdmin": {
"value": {}
}
}
}
]
}
Returns the rights that were newly granted.
Revoke User Rights
HTTP Request
- URL:
/v2/users/{userId}/rights - Method:
PATCH - Content-Type:
application/json - Content:
{
"identityProviderId": "",
"rights": [
{
"kind": {
"ParticipantAdmin": {
"value": {}
}
}
}
],
"userId": "CreatedUser1"
}
HTTP Response
{
"newlyRevokedRights": [
{
"kind": {
"ParticipantAdmin": {
"value": {}
}
}
}
]
}
Returns the rights that were actually revoked.
List Specific User Rights
HTTP Request
- URL:
/v2/users/{userId}/rights - Method:
GET - Content-Type:
application/json
HTTP Response
{
"rights": [
{
"kind": {
"CanActAs": {
"value": {
"party": "Bob::122099e6b7c92e12eb026f483f59a73e48149dc6e630a4f4f8fb95b8d269219b356c"
}
}
}
},
{
"kind": {
"ParticipantAdmin": {
"value": {}
}
}
}
]
}
List All DALF Packages
HTTP Request
- URL:
/v2/packages - Method:
GET
HTTP Response
{
"packageIds": [
"9e70a8b3510d617f8a136213f33d6a903a10ca0eeec76bb06ba55d1ed9680f69",
"0e4a572ab1fb94744abb02243a6bbed6c78fc6e3c8d3f60c655f057692a62816",
"5aee9b21b8e9a4c4975b5f4c4198e6e6e8469df49e2010820e792f393db870f4",
"a1fa18133ae48cbb616c4c148e78e661666778c3087d099067c7fe1868cbb3a1",
"a36cfff1da2f15da3d760c872860bf03140aa699f73b57b639fd40dfe8156cfe",
"54f85ebfc7dfae18f7d70370015dcc6c6792f60135ab369c44ae52c6fc17c274"
]
}
Where packageIds is the JSON array containing the package IDs of all loaded DALFs.
Download a DALF Package
HTTP Request
- URL:
/v2/packages/<package ID> - Method:
GET - Content: empty
Note that the desired package ID is specified in the URL.
HTTP Response, status: 200 OK
- Transfer-Encoding:
chunked - Content-Type:
application/octet-stream - Content: DALF bytes
The content (body) of the HTTP response contains raw DALF package bytes, without any encoding. Note that the package ID specified in the URL is actually the SHA-256 hash of the downloaded DALF package and can be used to validate the integrity of the downloaded content.
Upload a DAR File
HTTP Request
- URL:
/v2/packages - Method:
POST - Content-Type:
application/octet-stream - Content: DAR bytes
The content (body) of the HTTP request contains raw DAR file bytes, without any encoding.
HTTP Response, Status: 200 OK
- Content-Type:
application/json - Content:
{}
gRPC Ledger API Migration Guide
Migrating from Version 3.2 to Version 3.3
Application ID to User ID rename
Application ID was already synonymous with the User ID for all Canton deployments supporting authorization. This is a pure rename without changing the semantics, except for the slightly changed list of accepted characters for the User ID. (Please see both in value.proto.)
| Message with changed field(s) | Migration instruction |
|---|---|
GetMeteringReportRequest, PrepareSubmissionRequest, ExecuteSubmissionRequest, CompletionStreamRequest, Commands, Completion, ReassignmentCommand | Field application_id is consistently renamed to user_id. |
‘Domain’ is Renamed to ‘Synchronizer’
A simple renaming without changing the semantics.
| Changed endpoint | Migration instruction |
|---|---|
GetConnectedDomains | Endpoint renamed to GetConnectedSynchronizers. |
| Changed message | Migration instruction |
|---|---|
DomainTime, GetConnectedDomainsRequest, GetConnectedDomainsResponse, ConnectedDomain, Transaction, Metadata, PrepareSubmissionRequest | Messages renamed: Domain* to Synchronizer*. |
| Message with changed field(s) | Migration instruction |
|---|---|
DisclosedContract, Commands, Created, Archived, ActiveContract, TransactionTree | Field domain_id is consistently renamed to synchronizer_id. |
Completion | Field domain_time is renamed to synchronizer_time. |
OffsetCheckpoint | Field domain_times renamed to synchronizer_times. |
ConnectedSynchronizer | Fields renamed: domain to synchronizer. |
event_id Field Changes to offset and node_id
Field event_id is converted into two fields: offset and node_id. These two properties identify the events precisely.
The structural representation of Daml transaction trees changed: instead of exposing the root event IDs and the children for exercised events, only the last descendant node ID is exposed.
| Changed endpoint | Migration instruction |
|---|---|
GetTransactionTreeByEventId, GetTransactionByEventId | The *ByEventId pointwise-lookup endpoints changed; they are renamed to *ByOffset respectively. The lookups should be based on the newly introduced offset field, instead of the event_id from the respective Event. |
| Message with changed field(s) | Migration instruction |
|---|---|
CreatedEvent, ArchivedEvent, ExercisedEvent | Field event_id is removed in favor of new field offset (referencing the offset of origin) and new field node_id. These two fields identify the event. |
ExercisedEvent | Field child_event_ids is removed. Instead the new field last_descendant_node_id can be used, which captures structural information of the transaction trees. |
TransactionTree | Field events_by_id changed its type: the key of the map was previously a string referring to event_ids, now it is an int32 referring to the node_ids of the Events. Field root_event_ids is removed. The root nodes can now be computed with the help of the ordered list of events and the last_descendant_id. If Java bindings are used there is a helper function getRootNodeIds(). |
Miscellaneous
| Message with changed field(s) | Migration instruction |
|---|---|
Completion | Field deduplication_offset can have value zero, with meaning: participant begin. |
Interactive submission
The interactive submission related proto definitions moved to the interactive folder.
| Message with changed field(s) | Migration instruction |
|---|---|
ExecuteSubmissionRequest | The min_ledger_time field has been removed as it was unused. min_ledger_time can be set in the PrepareSubmissionRequest message instead. |
Universal Streams
Universal Streams is a new feature introduced in 3.3. It has a broad set of changes that consolidate the flat and tree style formats together. The old API is now deprecated and is replaced by a new API in this release. There are two sets of changes needed:
- Required changes to work with 3.3.
- Calls to the old API need to use the new API before the next major release where the old API is removed.
Both changes can be done in this release if that is most efficient.
Required Changes in 3.3
| Changed endpoint | Migration instruction |
|---|---|
SubmitAndWaitForTransaction | Instead of SubmitAndWaitRequest, a new message SubmitAndWaitForTransactionRequest needs to be used. Here you can specify the transaction_format field. If no transaction_format field is provided, a default will be used where:- transaction_shape set to ACS_DELTA- event_format defined with:- filters_by_party containing wildcard-template filter for all original Commands.act_as and Commands.read_as parties- verbose flag set |
GetEventsByContractId | In case of no events found, previously a GetEventsByContractIdResponse is returned with empty created and empty archived fields, now a CONTRACT_NOT_FOUND error is raised. Additionally, if EventFormat would filter out all results, a CONTRACT_NOT_FOUND error would be raised as well. |
GetTransactionByOffset, GetTransactionById | The behavior of these endpoints changed: instead of returning an empty transaction, a TRANSACTION_NOT_FOUND error will be raised if the transaction cannot be found, or filtering based on the query parameters would lead to an empty transaction. |
| Message with changed field(s) | Migration instruction |
|---|---|
Event | The oneof event is extended with exercised to support universal streams. The values of the events in case of transaction_shape are:- LEDGER_EFFECTS can be created or exercised- ACS_DELTA can be created or archived |
GetUpdatesResponse | The update oneof is extended to accommodate new TopologyTransaction updates. These types of messages will be only populated if the experimental flag is turned on, and the respective field of the update_filter is set. |
Changes Required before the Next Major Release
The changes below must be made prior to the next major release because the old APIs will be removed. This includes any related Java language bindings.
A helpful note is included in the source code for those items that will be removed. The protobuf definitions contain documentation markers for the places where an endpoint, a message or a field will be removed in 3.4. Look for the following formulation:
// Provided for backwards compatibility, it will be removed in the Canton version 3.4.0.
| Deprecated endpoint | Migrating to | Migration instruction |
|---|---|---|
SubmitAndWaitForTransactionTree | SubmitAndWaitForTransaction | To retain the original behavior, the transaction_format field of the SubmitAndWaitForTransactionRequest should be defined with:- transaction_shape set to LEDGER_EFFECTS- event_format defined with:- filters_by_party containing wildcard-template filter for all original Commands.act_as parties- verbose flag set |
GetUpdateTrees | GetUpdates | To retain the original behavior, the update_format field of the GetUpdatesRequest should be used instead of the deprecated filter and verbose fields:- include_transactions should be defined with:- transaction_shape set to LEDGER_EFFECTS- event_format should be defined by the original filter and verbose fields- If you are interested in seeing full trees for all querying parties and rendered views/blobs for selected interfaces or templates: extend the filters_by_party map and filters_for_any_party with a WildcardFilter in the cumulative field, where the include_created_event_blob is set to false.- include_reassignments should be defined by the original filter and verbose fields, and additionally for each Filters (in filters_by_party map and potentially in filters_for_any_party). The list of cumulative should be extended with a WildcardFilter where the include_created_event_blob is set to false. This step is needed because update streams are filtered by the cumulative filters, whereas for GetUpdateTrees for all querying parties all reassignments returned and the filter only influenced event rendering.- include_topology_events should be unset. |
GetTransactionTreeByOffset | GetUpdateByOffset | |
GetTransactionTreeById | GetUpdateById | |
GetTransactionByOffset | GetUpdateByOffset | To retain the original behavior, the include_transactions field of the GetUpdateByOffsetRequest should be defined with:- transaction_shape set to LEDGER_EFFECTS- event_format defined with:- filters_by_party containing wildcard-template filter for all original requesting_parties parties- verbose flag set |
GetTransactionById | GetUpdateById | To retain the original behavior, the include_transactions field of the GetUpdateByIdRequest should be defined with:- transaction_shape set to LEDGER_EFFECTS- event_format defined with:- filters_by_party containing wildcard-template filter for all original requesting_parties parties- verbose flag set |
GetTransactionByOffset | GetUpdateByOffset | To retain the original behavior, the include_transactions field of the GetUpdateByOffsetRequest should be defined with:- transaction_shape set to ACS_DELTA- event_format should be defined with:- filters_by_party should be wildcard filter for all original requesting_parties- verbose should be set |
GetTransactionById | GetUpdateById | To retain the original behavior, the include_transactions field of the GetUpdateByIdRequest should be defined with:- transaction_shape set to ACS_DELTA- event_format should be defined with:- filters_by_party should be wildcard filter for all original requesting_parties- verbose should be set |
| Deprecated message | Migration instruction |
|---|---|
SubmitAndWaitForTransactionTreeResponse | SubmitAndWaitForTransaction should be used with transaction_format set to LEDGER_EFFECTS in the SubmitAndWaitForTransactionRequest. |
TreeEvent, TransactionTree, GetUpdateTreesResponse, GetTransactionTreeResponse | These were used in tree* query/streaming scenarios, which is deprecated. The respective non-tree* endpoint should be used with transaction_shape set to LEDGER_EFFECTS in the request, and therefore resulting Events will be either CreatedEvent or ExercisedEvent. |
TransactionFilter | EventFormat should be used, which contains the original fields from TransactionFilter plus the verbose flag. |
GetTransactionByOffsetRequest, GetTransactionByIdRequest | The GetUpdateByOffsetRequest and GetUpdateByIdRequest respectively should be used with include_transactions of the update_format field. |
| Message with deprecated field(s) | Migration instruction |
|---|---|
GetEventsByContractIdRequest | The new event_format field should be used instead of the deprecated requesting_parties.To retain the original behavior, the event format field should be defined with: - filters_by_party should be wildcard filter for all requesting_parties- verbose should be set |
GetActiveContractsRequest | The new event_format field should be used instead of the deprecated filter and verbose fields.To retain the original behavior, the event_format field should be defined with:- filters_by_party and filters_for_any_party should be the same as in the original filter field- verbose should be the same as the original verbose field |
GetUpdatesRequest | The new update_format field should be used instead of the deprecated filter and verbose fields.To retain the original behavior: - include_transactions should be defined with:- transaction_shape set to ACS_DELTA- event_format should be defined by the original filter and verbose fields- include_reassignments should be defined by the original filter and verbose fields- include_topology_events should be unset |
Deprecated Java Bindings
All Java-binding classes that wrap the gRPC Ledger API messages slated for removal in 3.4 will be removed themselves. It will affect:
GetTransactionByIdRequestGetTransactionByOffsetRequestGetTransactionResponseGetUpdateTreesResponseSubmitAndWaitForTransactionTreeResponseTransactionFilterTransactionTreeTransactionTreeUtilsTreeEvent
JSON Ledger API v1 is Deprecated by JSON Ledger API v2
There are two JSON Ledger API versions in this release:
- JSON Ledger API v1: exists in Daml 3.2. This version is deprecated and will be removed in the next major release.
- JSON Ledger API v2: is introduced in this release and will be the supported JSON Ledger API version going forward.
Please refer to the JSON Ledger API v2 migration guide for the specifics to migrate from JSON Ledger API v1 to v2. This must be done prior to the next major release.
Identifier Addressing by-package-name
As described in value.proto, the following interface and template identifier formats are supported in Canton 3.3 for requests to the gRPC Ledger API:
- package-name reference format (
#<package-name>:<module>:<entity>) is supported and is the recommended format to use. - package-id reference format (
<package-id>:<module>:<entity>) is still supported but deprecated. Support is discontinued in Canton 3.4.
Prior to the next major release, all clients using the package-id reference format must switch to using the package-name reference format for all requests.
Migrating from Version 3.3 to Version 3.4
Preferred package version endpoints
A new set of endpoints supersedes and extends the existing functionality provided by InteractiveSubmissionService.GetPreferredPackageVersion (gRPC) and interactive-submission/preferred-packages (JSON). The new endpoints are:
InteractiveSubmissionService.GetPreferredPackages(gRPC)interactive-submission/preferred-packages(JSON)
Users must switch to using the new endpoints in Canton 3.4, as the old endpoints will be removed.
Existing users can switch to the new endpoints by adapting the request payload. For example, in the gRPC version, construct the GetPreferredPackagesRequest by populating one PackageVettingRequirement with homonymous fields from the old GetPreferredPackageVersionRequest.
For more details, refer to the protobuf definitions in interactive_submission_service.proto.
Package-name based queries
For the Ledger API queries using filters, the interface or template identifiers in the event format can be specified using either the package-name or the package-id format (via the package_id field in the Identifier message of the InterfaceFilter or the TemplateFilter of CumulativeFilter).
The package-id format is deprecated and will not be supported in future releases. If the package-id format is used, a warning message will be logged and the package-id will be internally translated to the corresponding package-name format.
The affected endpoints are:
StateService.GetActiveContractsUpdateService.GetUpdatesUpdateService.GetUpdateTreesUpdateService.GetTransactionTreeByOffsetUpdateService.GetTransactionTreeByIdUpdateService.GetTransactionByOffsetUpdateService.GetTransactionByIdUpdateService.GetUpdateByOffsetUpdateService.GetUpdateById
Transient events in ACSDelta
Transactions with transient events for which there was an intersection between submitters and querying parties were previously exposed as transactions with empty events in ACS_DELTA shape. In 3.4, those transactions will not be exposed at all in the ACS_DELTA shape. As before, they will still be exposed in the LEDGER_EFFECTS shape.
Participant divulgence
Participant divulgence emerges if there is a party divulgence, and none of the stakeholders of the divulged contracts are hosted on the participant. From Canton 3.4, participant divulgence is no longer performed in the Active Contracts service and the ACS_DELTA shaped transaction endpoints.
Specifications
API specs
Introduction
Placeholder chapter.