Submit the Command

Table of Contents

Code fragments are provided for illustration purposes only; they do not comprise a complete, working example

In the code fragments below, "PARTY= "05555"" is used as a placeholder for ASX Party IDs. For available party identifiers available in CDE, refer to the CDE Users page.

Submitting Commands to the Ledger through Ledger API

Preparing for Command Submission

Each submission requires the following information.


Workflow Id

The unique identifier for high level workflows. The id is created and stored by the client. Each workflow consists of one to many command submissions.

For CHESS customer, workflow Id must follow the naming convention of ApplicationId:CommandId

Where the Application Id:

  • represents the LedgerAPI user provided by ASX during onboarding; and
  • must match the Application Id in the Ledger API token.

Where the Command Id:

  • represents a unique identifier for command submissions that can be used to track command execution result; and
  • corresponds to the normalised BizMsgIdr
  • this is the BizMsgIdr from the message where the “|” character is replaced by a “-“;
  • for example, if the BizMessageIdr for the message is “12345|XYZ11111” the normalised value would be “12345-XYZ111111”.

Using the above fields in the above example, the Workflow Id ledger command corresponds to “ledgerclient1:12345-XYZ111111”.

For more details, please refer to The Ledger API services — Daml SDK documentation.

PartyInside the ledger, every command is executed as a party. The party will be cross checked with the Ledger API token to ensure the submitter is permitted to act on behalf of the parties.
CommandsEach submission may contain one to many commands. All the commands submitted in one submission will be completed or rejected as one atomic transaction.
Access tokenThe Ledger API token.

For further information, refer to the Identify the Master Ingress Contract, Build the Command Data Structure, Building the Payload and Submit the Command Ledger API connectivity sections.


Callbacks allows the listening and notification of specific events by identifying trigger points. There are two types of call backs- event callback and command completion callback. 

Event Callback

Register Event Callback

The following code fragment provides the ability to listen to exercised events for partyId. 

ITE1 Update

The below code sample has been updated to include accessToken.

Code fragment
public void registerEgressChoiceCallback(DamlLedgerClient client, String partyId) {
    Flowable<TransactionTree> transactionsTrees = client.getTransactionsClient().getTransactionsTrees(
            new FiltersByParty(Collections.singletonMap(partyId, NoFilter.instance)), true, accessToken);
    EgressChoiceConsumer egressChoiceConsumer = new EgressChoiceConsumer();
    transactionsTrees.subscribe(tx -> egressChoiceConsumer.processTransactionTree(tx),
            e -> {
                print("Error in processTransactionTree! Cause: " + e.getCause() + "; Error message: " + e.getMessage());

Event Callback Method

The below code fragment provides a method for creating a callback for egress contracts and choices. For more information on creating this, please refer to the relevant messaging page (in this example, please refer to the Bilateral Demand Messaging page).

Code fragment
// Stream TransactionTree
public void processTransactionTree(TransactionTree tx) {
    Collection<TreeEvent> treeEvents = tx.getEventsById().values();
    for (TreeEvent treeEvent : treeEvents) {
        if (treeEvent instanceof CreatedEvent) {
            processContract((CreatedEvent) treeEvent);
        } else if (treeEvent instanceof ExercisedEvent) {
            if (!((ExercisedEvent) treeEvent).isConsuming()) {
                processExercisedEvent((ExercisedEvent) treeEvent);

// Process the egress contracts
private void processContract(CreatedEvent createdEvent) {
    String moduleName = createdEvent.getTemplateId().getModuleName();
    String entityName = createdEvent.getTemplateId().getEntityName();
    if (moduleName.equals("DA.ASX.API.Account.Holder_V001") && entityName.equals("HolderFullView")) {
        HolderFullView holderFullView = HolderFullView.fromValue(createdEvent.getArguments());
        print("Contract - HolderFullView = " + holderFullView.toString());
    } else if (moduleName.equals("DA.ASX.API.Account.Holder_V001") && entityName.equals("HolderStandardView")) {
        HolderStandardView holderStandardView = HolderStandardView.fromValue(createdEvent.getArguments());
       print("Contract - HolderStandardView = " + holderStandardView.toString());
    } else if (moduleName.equals("DA.ASX.API.Account.Account_V001") && entityName.equals("AccountAdditionalView")) {
        AccountAdditionalView accountAdditionalView = AccountAdditionalView.fromValue(createdEvent.getArguments());
        print("Contract - AccountAdditionalView = " + accountAdditionalView.toString());
    } else if (moduleName.equals("DA.ASX.API.Account.Account_V001") && entityName.equals("AccountStandardView")) {
        AccountStandardView accountStandardView = AccountStandardView.fromValue(createdEvent.getArguments());
        print("Contract - AccountStandardView = " + accountStandardView.toString());

// Process the egress choices
private void processExercisedEvent(ExercisedEvent exercisedEvent) {
    String choice = exercisedEvent.getChoice();
    Value value = exercisedEvent.getChoiceArgument();

    if (choice.equals(Hold_207_001_02_sese_024_001_08.class.getSimpleName())) {
        Hold207 message = Hold_207_001_02_sese_024_001_08.fromValue(value).message;
        print("Hold207 Choice: " + choice + "\n" + "Hold207 Message: " + message.toString());
    } else if (choice.equals(Hold_202_001_04_sese_025_001_07.class.getSimpleName())) {
        Hold202 message = Hold_202_001_04_sese_025_001_07.fromValue(value).message;
        print("Hold202 Choice: " + choice + "\n" + "Hold202 Message: " + message.toString());
    } else if (choice.equals(Acct_002_001_08_acmt_002_001_07.class.getSimpleName())) {
        Acct002 message = Acct_002_001_08_acmt_002_001_07.fromValue(value).message;
        print("Acmt002 Choice: " + choice + "\n" + "Acmt002 Message: " + message.toString());
    } else if (choice.equals(Comm_807_001_01_admi_002_001_01.class.getSimpleName())) {
        Comm807 message = Comm_807_001_01_admi_002_001_01.fromValue(value).message;
        print("Comm807 Choice: " + choice + "\n" + "Comm807 Message: " + message.toString());
    } else if (choice.equals(Comm_808_001_01_admi_002_001_01.class.getSimpleName())) {
        Comm808 message = Comm_808_001_01_admi_002_001_01.fromValue(value).message;
        print("Comm808 Choice: " + choice + "\n" + "Comm808 Message: " + message.toString());

Command Completion Callback

Register Command Completion Callback 

Command completion marks the end of the transaction started by a command. client.getCommandCompletionClient and completionStream() is used to listen to all command completion events.

This callback looks for a specific command and inspect its status. The below code fragment shows the registration of the command completion stream:

ITE1 Update

The below code sample has been updated to include accessToken.

Code fragment
// This allows you to subscribe to command completion for partyId.
public void registerCmdCompletionCallback(DamlLedgerClient client, String appId, String partyId) {
    Flowable<CompletionStreamResponse> completionStream = client.getCommandCompletionClient().completionStream(
            appId, LedgerOffset.LedgerEnd.getInstance(), new HashSet<>(Collections.singleton(partyId)), accessToken);

Command Completion Callback Method

Code fragment
// This is triggered every time a command completion occurs.
public void processCmdCompletion(CompletionStreamResponse completionStreamResponse) {
            .forEach(x -> {
				// Removes the command ID from the command tracker because this command is now completed
                // Note: x is typed response. The code below is provided for illustration.
                System.out.println(String.format("Completion: %s", x.toString()));

Submitting Commands

There are two methods to submitting commands to the Ledger API, synchronous and asynchronous. Callbacks may be used for both cases. 

Method 1: Command Service - Synchronous submission

A synchronous submission is where an acknowledgement to the initial message is required before proceeding to with the execution of further commands. 

The Command Service, a type of application service, combines command submission and completion into a single service.

The following code fragment submits a command synchronously:

ITE1 Update

The below code sample has been updated to include accessToken.

Code fragment
public void synchronousSubmit(DamlLedgerClient client, String workflowId, List<Command> exerciseCommands,
                                  String appId, String partyId) {
        String commandId = UUID.randomUUID().toString();
        client.getCommandClient().submitAndWait(workflowId, appId, commandId, partyId, exerciseCommands, accessToken).blockingGet();

Method 2: Command Service - Asynchronous Submission 

An asynchronous is one which does not require acknowledgement to the initial message before proceeding with the execution of further commands. 

In Ledger Services, the command submission and completion services submit commands to the ledger and tracks status of the submitted commands respectively.

For example:

ITE1 Update

The below code sample has been updated to include accessToken.

Code fragment
public void asynchronousSubmit(DamlLedgerClient client, String workflowId, List<Command> exerciseCommands,
                                   String appId, String partyId, String bizMsgIdr) {
        String commandId = UUID.randomUUID().toString();
        client.getCommandSubmissionClient().submit(workflowId, appId, commandId, partyId,
                exerciseCommands, accessToken);

In the code above, the two time-related arguments are mandatory:

  • The first argument must be specified in UTC time zone. If the time zone differs from this, the request will not comply with the message definition.
  • The second argument is maximumRecordTime, which specifies the interval within which the command must be executed. The request will be rejected if the time taken by the command execution is greater than the specified interval. Failing to specify the time arguments may result in a transaction time exception error. The error may also present if the time taken by the command execution is greater than what is specified in the plusSeconds() argument:
Code fragment

Example of System Response after Successful Submission

After successful submission of the command, the print out looks display as per the below: 

Code fragment
Completion: command_id: "12345-XYZ111111"
status {

Please refer to the Sandbox Manual for more information about configuring time settings for the Sandbox.

Review the Expected Output

The expected output for a Hold201 may either be Hold207 or Hold208, as shown in the ISO 20022 Messaging - MC - Bilateral Demand Transfer.

The printing of the expected output is triggered by event callback methods

Related Pages:

There are no related labels.

Browse Popular Pages:

No labels match these criteria.

This document provides general information only. ASX Limited (ABN 98 008 624 691) and its related bodies corporate (“ASX”) makes no representation or warranty with respect to the accuracy, reliability or completeness of the information. To the extent permitted by law, ASX and its employees, officers and contractors shall not be liable for any loss or damage arising in any way (including by way of negligence) from or in connection with any information provided or omitted or from anyone acting or refraining to act in reliance on this information.

© 2022 ASX Limited ABN 98 008 624 691