- Overview
- Setup
- Tutorials
- How Tos
- Reference
Registry Utility - Transfer Preapproval API Example¶
This example shows how to create a TransferPreapproval contract via the HTTP JSON API.
Preparation¶
Prerequisites:
Access to a running Canton HTTP JSON API.
Local tools:
bash,curl,jq, anduuidgen.
Add all the required information to the source.sh file:
1#!/usr/bin/env bash
2
3## =================================================================================================
4## Purpose: Configurations for this example, amend variables as needed.
5## Script: source.sh
6## =================================================================================================
7
8# Receiver details
9RECEIVER_TOKEN="<PASTE_JWT_TOKEN_HERE>"
10RECEIVER_PARTY_ID="holder::1220e71be62943820d0f7ecc365fc498adcd25e1b1fd165f0ae9b65c343230f93579"
11RECEIVER_USER_ID="holder"
12
13# Operator details
14OPERATOR_PARTY_ID="operator::1220ae8c93e1f1263d0366cbc4c2a2fe587b5227e929ddd76528380c59deb58ada8f"
15# You can retrieve the operator party ID from `http://<host>/api/utilities/v0/operator`, e.g.
16# - Local: `curl http://localhost:8080/api/utilities/v0/operator`
17# - DevNet: `curl https://api.utilities.digitalasset-dev.com/api/utilities/v0/operator`
18
19# Instrument admin (or registrar) details whose instruments are being preapproved.
20INSTRUMENT_ADMIN_PARTY_ID="registrar::1220e71be62943820d0f7ecc365fc498adcd25e1b1fd165f0ae9b65c343230f93579"
21
22# Instrument ids to preapprove (an empty list means all instruments are preapproved)
23INSTRUMENT_IDS='[
24 "INST"
25]'
26
27# JSON API endpoint
28# Example (local): HTTP_JSON_API="http://localhost:8001/api/json-api"
29# Example (remote): HTTP_JSON_API="https://<your-host>/api/json-api"
30HTTP_JSON_API="http://localhost:8001/api/json-api"
31
32# Daml template IDs (package-name qualified)
33TRANSFER_PREAPPROVAL_TEMPLATE="#utility-registry-app-v0:Utility.Registry.App.V0.Model.TransferPreapproval:TransferPreapproval"
The required information is:
Details of |
Description |
|---|---|
Receiver (authorizing user) |
|
Operator |
|
Instrument admin |
|
Instrument IDs |
|
JSON API base URL |
|
Note
You can retrieve the OPERATOR_PARTY_ID via http://<your-host>/api/utilities/v0/operator, e.g.
Local:
http://localhost:8080/api/utilities/v0/operatorDevNet:
https://api.utilities.digitalasset-dev.com/api/utilities/v0/operator
Create Transfer Preapproval¶
This step submits a single transaction containing a single CreateCommand.
Run the following script:
1#!/usr/bin/env bash
2
3## =================================================================================================
4## How-to Tutorial: Create transfer preapproval
5## Authorized by: receiver
6## Script: create-transfer-preapproval.sh
7## =================================================================================================
8
9set -euo pipefail
10
11SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
12source "${SCRIPT_DIR}/source.sh"
13
14OUTPUT_DIR="${SCRIPT_DIR}/../response"
15mkdir -p "$OUTPUT_DIR"
16
17# Validate inputs
18if [[ -z "${HTTP_JSON_API:-}" ]]; then
19 echo "Error: HTTP_JSON_API is not set in source.sh"
20 exit 1
21fi
22if [[ -z "${TRANSFER_PREAPPROVAL_TEMPLATE:-}" ]]; then
23 echo "Error: TRANSFER_PREAPPROVAL_TEMPLATE is not set in source.sh"
24 exit 1
25fi
26if [[ -z "${OPERATOR_PARTY_ID:-}" ]]; then
27 echo "Error: OPERATOR_PARTY_ID is not set in source.sh"
28 exit 1
29fi
30if [[ -z "${INSTRUMENT_ADMIN_PARTY_ID:-}" ]]; then
31 echo "Error: INSTRUMENT_ADMIN_PARTY_ID is not set in source.sh"
32 exit 1
33fi
34if [[ -z "${RECEIVER_TOKEN:-}" || -z "${RECEIVER_USER_ID:-}" || -z "${RECEIVER_PARTY_ID:-}" ]]; then
35 echo "Error: RECEIVER_TOKEN/RECEIVER_USER_ID/RECEIVER_PARTY_ID must be set in source.sh"
36 exit 1
37fi
38if [[ -z "${INSTRUMENT_IDS:-}" ]]; then
39 echo "Error: INSTRUMENT_IDS is not set in source.sh"
40 exit 1
41fi
42echo "${INSTRUMENT_IDS}" | jq -e 'type == "array" and all(.[]; type == "string")' >/dev/null
43
44COMMANDS_JSON=$(jq -n \
45 --arg templateId "${TRANSFER_PREAPPROVAL_TEMPLATE}" \
46 --arg operator "${OPERATOR_PARTY_ID}" \
47 --arg receiver "${RECEIVER_PARTY_ID}" \
48 --arg instrumentAdmin "${INSTRUMENT_ADMIN_PARTY_ID}" \
49 --argjson instrumentIds "${INSTRUMENT_IDS}" \
50 '[
51 {
52 CreateCommand: {
53 templateId: $templateId,
54 createArguments: {
55 operator: $operator,
56 receiver: $receiver,
57 instrumentAdmin: $instrumentAdmin,
58 instrumentAllowances: ($instrumentIds | map({id: .}))
59 }
60 }
61 }
62 ]')
63
64RESULT=$(
65 curl -s \
66 --url "${HTTP_JSON_API}/v2/commands/submit-and-wait-for-transaction" \
67 --header "Authorization: Bearer ${RECEIVER_TOKEN}" \
68 --header "Content-Type: application/json" \
69 --request POST \
70 --data @- <<EOF
71{
72 "commands": {
73 "commands": ${COMMANDS_JSON},
74 "workflowId": "",
75 "userId": "${RECEIVER_USER_ID}",
76 "commandId": "$(uuidgen | tr -d '\n')",
77 "deduplicationPeriod": {
78 "DeduplicationDuration": {
79 "value": { "seconds": 30, "nanos": 0 }
80 }
81 },
82 "actAs": [
83 "${RECEIVER_PARTY_ID}"
84 ],
85 "readAs": [],
86 "submissionId": "$(uuidgen | tr -d '\n')",
87 "disclosedContracts": [],
88 "domainId": "",
89 "packageIdSelectionPreference": []
90 }
91}
92EOF
93)
94
95echo "--- Command response ---"
96echo "$RESULT" | jq
97
98OUTPUTFILE="response-step-1.json"
99echo "$RESULT" > "$OUTPUTFILE"
The result is the transaction response stored in response/response.json.
Example response:
1{
2 "transaction": {
3 "updateId": "1220d28660de73bc898696c75955f9ad3cf99ca7d317634a29749779e2c960d81cdc",
4 "commandId": "C07EC6B2-25E0-46E5-B0A2-1AB04BF0BD31",
5 "workflowId": "",
6 "effectiveAt": "2026-02-05T15:25:53.665284Z",
7 "events": [
8 {
9 "CreatedEvent": {
10 "offset": 339,
11 "nodeId": 0,
12 "contractId": "00c2307ab2eeb1d348197729fcae1dbfac52201392bf054fc955f278007a6d01b2ca1212201d0efef35a061299b59ab211bf9b3ed8cb05af08c808c4f26d2c9907a47089b7",
13 "templateId": "f33ca939728c8401e67af3ab1dfc1ad7dc00fb35862bf5e964c5cdb3d5b6857c:Utility.Registry.App.V0.Model.TransferPreapproval:TransferPreapproval",
14 "contractKey": null,
15 "createArgument": {
16 "operator": "operator::1220ae8c93e1f1263d0366cbc4c2a2fe587b5227e929ddd76528380c59deb58ada8f",
17 "receiver": "holder::1220e71be62943820d0f7ecc365fc498adcd25e1b1fd165f0ae9b65c343230f93579",
18 "instrumentAdmin": "registrar::1220e71be62943820d0f7ecc365fc498adcd25e1b1fd165f0ae9b65c343230f93579",
19 "instrumentAllowances": [
20 {
21 "id": "INST"
22 }
23 ]
24 },
25 "createdEventBlob": "",
26 "interfaceViews": [],
27 "witnessParties": [
28 "holder::1220e71be62943820d0f7ecc365fc498adcd25e1b1fd165f0ae9b65c343230f93579"
29 ],
30 "signatories": [
31 "holder::1220e71be62943820d0f7ecc365fc498adcd25e1b1fd165f0ae9b65c343230f93579"
32 ],
33 "observers": [
34 "operator::1220ae8c93e1f1263d0366cbc4c2a2fe587b5227e929ddd76528380c59deb58ada8f"
35 ],
36 "createdAt": "2026-02-05T15:25:53.665284Z",
37 "packageName": "utility-registry-app-v0",
38 "representativePackageId": "f33ca939728c8401e67af3ab1dfc1ad7dc00fb35862bf5e964c5cdb3d5b6857c",
39 "acsDelta": true
40 }
41 }
42 ],
43 "offset": 339,
44 "synchronizerId": "global-domain::12208750245c5bca762b46827b41716c2d945838858b6ef97b35fb76817db0044d20",
45 "traceContext": {
46 "traceparent": "00-81a6821119081c80a3082310c434c8b9-ba14d7e97aa83062-01",
47 "tracestate": null
48 },
49 "recordTime": "2026-02-05T15:25:53.690588Z",
50 "externalTransactionHash": null
51 }
52}
Once a transfer preapproval is established for a receiver and instrument, all incoming transfers become direct. If you are following the Registry Utility - Transfer API Example, you may skip the second step; the transfer is automatically effectuated immediately upon completion of the first step.