Digital Asset docs
Pause / Unpause
Updated 05-11-2026
This feature is introduced with version
0.15 of the Registry App.Version 0.9.0 of the utility-registry-v0 package added the underlying isPaused field to the
InstrumentConfiguration.A paused instrument cannot be interacted with by non-registrar parties, with narrow exceptions
where the registrar itself is the counterparty. The registrar can still administer the
instrument configuration (including unrelated fields) while it is paused.Overview
EachInstrumentConfiguration carries an isPaused : Optional Paused field. Some Paused marks
the instrument paused; None marks it unpaused (the default for newly created instruments).
There is no dedicated pause/unpause choice. Instead, the registrar toggles isPaused through the
same RegistrarService_ArchiveAndCreateInstrumentConfiguration choice used for any other
configuration change — archiving the current InstrumentConfiguration and creating a replacement
with the new isPaused value. Every other field (identifiers, credential requirements, block list
setting, provider app reward beneficiaries) except the force transfer setting can be changed in the
same call, so pausing an instrument doesn’t prevent the registrar from administering it.
Pausing is scoped to a single instrument. Other instruments under the same registrar are
unaffected.
Pausing and unpausing
- dApp UI
- API
1
Open instrument management
As the registrar, navigate to Registry → Management → Instrument Management and
select the instrument.
2
Open the modify dialog
Click Modify on the instrument’s details.
3
Toggle Pause Instrument
Check (or uncheck) Pause Instrument. Other fields on the same form can be changed at
the same time.
4
Submit
Click Modify to submit. On success, the UI shows “Successfully modified instrument
configuration” and the instrument’s new pause state is reflected immediately.
These ledger API calls are intended to be driven by a customer-owned component that pauses
and unpauses instruments on a chosen schedule (e.g. to reflect market holidays or
non-trading hours), rather than invoked manually.
1
Look up the current configuration
As the registrar, fetch the instrument’s current contract id and payload from the registry
backend’s instrument configuration endpoint, rather than querying the ledger directly:To confirm the instrument’s current pause state, query the public token-standard metadata
endpoint, which exposes it directly as You’ll also need the
CONFIG_RESPONSE=$(curl -s -X POST "${REGISTRY_BACKEND_URL}/api/utilities/v0/contract/instrument-configuration" \
-H "authorization: Bearer ${JWT_TOKEN}" \
-H "content-type: application/json" \
--data-raw '{
"registrar": "'"${REGISTRAR_PARTY}"'",
"instrumentIdentifier": {
"source": "'"${REGISTRAR_PARTY}"'",
"id": "'"${INSTRUMENT_ID}"'",
"scheme": "'"${INSTRUMENT_SCHEME}"'"
}
}')
contract.contractId is the InstrumentConfiguration contract id to pass as cid below,
and payload gives you defaultIdentifier.id, additionalIdentifiers,
issuerRequirements, and holderRequirements to carry over unchanged:INSTRUMENT_CONFIGURATION_CID=$(echo "$CONFIG_RESPONSE" | jq -r '.contract.contractId')
CURRENT_INSTRUMENT_ID=$(echo "$CONFIG_RESPONSE" | jq -r '.payload.defaultIdentifier.id')
CURRENT_ADDITIONAL_IDENTIFIERS=$(echo "$CONFIG_RESPONSE" | jq -c '.payload.additionalIdentifiers')
CURRENT_ISSUER_REQUIREMENTS=$(echo "$CONFIG_RESPONSE" | jq -c '.payload.issuerRequirements')
CURRENT_HOLDER_REQUIREMENTS=$(echo "$CONFIG_RESPONSE" | jq -c '.payload.holderRequirements')
issuerRequirements and holderRequirements come back from this endpoint with each
requiredClaims entry shaped as {"property": ..., "value": ...} — the backend’s own
DTO encoding. The raw ledger API command below expects the underlying Daml tuple
encoding instead, {"_1": ..., "_2": ...}. Convert both before splicing them into the
command in the next step:CURRENT_ISSUER_REQUIREMENTS=$(echo "$CURRENT_ISSUER_REQUIREMENTS" | jq -c '
map(.requiredClaims |= map({"_1": .property, "_2": .value}))
')
CURRENT_HOLDER_REQUIREMENTS=$(echo "$CURRENT_HOLDER_REQUIREMENTS" | jq -c '
map(.requiredClaims |= map({"_1": .property, "_2": .value}))
')
paused:curl -s "${REGISTRY_BACKEND_URL}/api/token-standard/v0/registrars/${REGISTRAR_PARTY}/registry/metadata/v1/instruments/${INSTRUMENT_ID}"
Neither endpoint returns
useBlockList or forceTransferEnabled — source their
current values separately (e.g. from the dApp UI’s instrument details panel) so you
don’t inadvertently reset them when submitting the choice below.RegistrarService contract id to exercise the choice on. There’s no
dedicated backend endpoint for it, so query the ledger directly: first get the current
ledger end offset, then look up the registrar’s active RegistrarService contract as of
that offset.LEDGER_END=$(curl -s "${JSON_API_URL}/v2/state/ledger-end" \
-H "authorization: Bearer ${JWT_TOKEN}" | jq .offset)
REGISTRAR_SERVICE_CID=$(curl -s -X POST "${JSON_API_URL}/v2/state/active-contracts" \
-H "authorization: Bearer ${JWT_TOKEN}" \
-H "content-type: application/json" \
--data-raw '{
"eventFormat": {
"filtersByParty": {
"'"${REGISTRAR_PARTY}"'": {
"cumulative": [
{
"identifierFilter": {
"TemplateFilter": {
"value": {
"templateId": "#utility-registry-app-v0:Utility.Registry.App.V0.Service.Registrar:RegistrarService",
"includeCreatedEventBlob": false
}
}
}
}
]
}
},
"verbose": false
},
"activeAtOffset": '"${LEDGER_END}"'
}' | jq -r --arg registrar_party "${REGISTRAR_PARTY}" \
'.[] | select(.contractEntry.JsActiveContract.createdEvent.createArgument.registrar == $registrar_party)
| .contractEntry.JsActiveContract.createdEvent.contractId')
2
Exercise RegistrarService_ArchiveAndCreateInstrumentConfiguration
As the registrar, exercise the choice on the To pause the instrument:To unpause, resubmit the same command with
RegistrarService contract, passing the
current InstrumentConfiguration contract id as cid and a payload that copies every
field from the existing configuration unchanged, except isPaused, which is set to {}
(an empty Paused record, i.e. Some Paused) to pause, or null (None) to unpause.
forceTransferEnabled must be passed through exactly as it currently is — the choice
rejects any attempt to change it in the same call.CURRENT_FORCE_TRANSFER_ENABLED should be null or true, and CURRENT_USE_BLOCK_LIST
should be null, true, or false — unlike isPaused, neither uses {}.commandId is generated with uuidgen, so it must be reachable on your PATH (it ships
by default on macOS and most Linux distributions; otherwise substitute any unique string).RESPONSE=$(curl -s -X POST "${JSON_API_URL}/v2/commands/submit-and-wait-for-transaction" \
-H "authorization: Bearer ${JWT_TOKEN}" \
-H "content-type: application/json" \
--data-raw '{
"commands": {
"commands": [
{
"ExerciseCommand": {
"templateId": "#utility-registry-app-v0:Utility.Registry.App.V0.Service.Registrar:RegistrarService",
"contractId": "'"${REGISTRAR_SERVICE_CID}"'",
"choice": "RegistrarService_ArchiveAndCreateInstrumentConfiguration",
"choiceArgument": {
"cid": "'"${INSTRUMENT_CONFIGURATION_CID}"'",
"payload": {
"instrumentId": "'"${CURRENT_INSTRUMENT_ID}"'",
"additionalIdentifiers": '"${CURRENT_ADDITIONAL_IDENTIFIERS}"',
"issuerRequirements": '"${CURRENT_ISSUER_REQUIREMENTS}"',
"holderRequirements": '"${CURRENT_HOLDER_REQUIREMENTS}"',
"useBlockList": '"${CURRENT_USE_BLOCK_LIST}"',
"isPaused": {},
"forceTransferEnabled": '"${CURRENT_FORCE_TRANSFER_ENABLED}"'
}
}
}
}
],
"workflowId": "",
"userId": "'"${USER_ID}"'",
"commandId": "'"$(uuidgen)"'",
"actAs": ["'"${REGISTRAR_PARTY}"'"],
"readAs": []
}
}')
"isPaused": null in place of
"isPaused": {} — every other field (including forceTransferEnabled) stays the same.3
Track the replacement contract id
The choice archives the old Use it for subsequent choices, since the old contract id no longer exists.
InstrumentConfiguration and creates a new one. Since
submit-and-wait-for-transaction returns the transaction’s event list rather than the
exercise result directly, pull the new contract id out of the CreatedEvent for
InstrumentConfiguration:INSTRUMENT_CONFIGURATION_CID=$(echo "$RESPONSE" | jq -r '
.transaction.events[]
| .CreatedEvent
| select(. != null)
| select(.templateId | test(":Utility.Registry.V0.Configuration.Instrument:InstrumentConfiguration$"))
| .contractId
')
This mirrors what the dApp UI does internally: the Modify dialog’s “Pause Instrument”
checkbox toggles the same
isPaused field and submits it through the same
RegistrarService_ArchiveAndCreateInstrumentConfiguration choice, carrying over every other
field from the current configuration (see ModifyInstrumentConfigurationForm in the
registry frontend).Enforcement while paused
Pause enforcement is applied at the point where a non-registrar party would act on the instrument. Requests, offers/acceptances, allocations, and two-step/self transfers all checkisInstrumentPaused before proceeding:
| Flow | Choice | Who is checked |
|---|---|---|
| Mint | acceptMint (accept request/offer) | Rejected unless holder == admin (registrar) |
| Mint | AllocationFactory_RequestMint | Rejected unless holder == registrar |
| Burn | validateBurn (accept request/offer) | Rejected unless holder == admin (registrar) |
| Burn | AllocationFactory_RequestBurn | Rejected unless holder == registrar |
| Burn/Mint | AllocationFactory_InternalBurnMint | Checked independently for each burn input owner and each mint output owner; the registrar’s own holdings are exempt |
| Allocation (V1) | AllocationFactory_AllocateInternal | Rejected unless sender == registrar |
| Allocation (V1 accept) | Transfer rule allocation accept | Rejected unless receiver == registrar; the registrar as sender does not exempt a non-registrar receiver |
| Two-step / self-transfer | AllocationFactory transfer/self-transfer flows | Rejected unless sender == admin (registrar) |
The registrar is only exempt where it is itself the party being checked (holder, sender, or
owner). A non-registrar counterparty is always blocked while the instrument is paused — for
example, a registrar-initiated transfer offer can still be created while paused, but a
non-registrar receiver cannot accept a paused instrument’s allocation.
Error messages
| Scenario | Error |
|---|---|
| Requesting a mint while paused | Instrument must not be paused to request a mint |
| Accepting a mint while paused | Instrument must not be paused to accept a mint |
| Requesting a burn while paused | Instrument must not be paused to request a burn |
| Accepting a burn request/offer while paused | Instrument must not be paused to accept a burn request/offer |
| Two-step / self-transfer while paused | Instrument must not be paused in two step and self transfers |
| Allocating internally while paused | Instrument must not be paused in allocate internal |
| Burn-mint internally while paused | Instrument must not be paused in internal burn mint |
| Accepting a V1 allocation while paused | Instrument must not be paused in allocation V1 |
Backwards compatibility
Parties don’t need to be running the latest app version to be protected by a pause. Pausing
blocks an instrument for everyone, regardless of which package version a counterparty’s client
was built against.
isPaused field and its enforcement were introduced in version 0.10.0 of the
utility-registry-app-v0 package, but all versions of a package are required to be Smart Contract
Upgrade (SCU) compatible with one another. In practice this means:
- A counterparty submitting commands from an older, pre-
0.10.0client still has its choices (acceptMint,AllocationFactory_RequestBurn, transfer/self-transfer flows, and so on) run against the currently vetted package on the participant, which is the one that contains theisInstrumentPausedcheck — not the older package the client code was compiled against. - There is no old-client code path that bypasses the pause check: the instrument configuration payload is read fresh on every choice exercise, so an older client cannot “see” a stale, unpaused configuration.
- Older clients that have no concept of
isPausedsimply have no way to unpause an instrument themselves — only a registrar exercisingRegistrarService_ArchiveAndCreateInstrumentConfiguration(via a client that supports the field) can change the pause state.
Related
- Blocklist — another instrument-level restriction enforced the same
way, via
checkBlockListonInstrumentConfiguration.