Issue & Transfer a Scope (Non-Fungible Asset)
Overview
Scopes (non-fungible tokens) represent unique assets and their on-chain records on Provenance. In this workflow you will:
- Create a Scope from a Scope Specification
- (Optional) add data access principals and create a session for records
- Transfer value ownership
- Verify state, ownership, and valuation (NAV)
We use the direct POST path on https://api.provlabs.com/.... The same write ops are mirrored under /vault, but aren't required here.
Replace these in all snippets:
- {SPEC_UUID} → the Scope Specification UUID you're anchoring this asset to
- {SCOPE_ID} → the created scope identifier (bech32 ID or UUID accepted by the endpoint)
- tp1… / pb1… → your addresses (testnet tp1…, mainnet pb1…)
Prerequisites
- Bearer token for NUVA Labs' APIs
- Fee model ready (caller pays vs sponsor via fee-grant) and fee-payer checks passed (as in Overview → )
- A Scope Specification UUID to anchor this asset (see "Scope Specifications" below if you need to create one)
On Scope Specifications:
- A Scope Specification defines the structure and purpose of a scope. It acts as a template for creating scopes and ensures consistency across similar assets.
- Each scope must be anchored to a Scope Specification UUID, which is passed as a path parameter when creating the scope.
- If you don't have a Scope Specification, you can create one using the Create Scope Specification API.
Example Request:
curl -X POST "$PROV_REST_BASE/metadata/scope-specification" \
-H "Authorization: Bearer $PROV_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Real Estate Asset Specification",
"name": "RealEstateSpec",
"parties_involved": [
{
"address": "tp1PARTY...",
"role": "PARTY_TYPE_OWNER",
"optional": false
}
],
"records_required": [
{
"name": "PropertyDetails",
"type": "record_type_property"
}
]
}'
- Request Body Parameters:
- description: A human-readable description of the scope specification.
- name: A unique name for the scope specification.
- parties_involved: A list of parties required for the scope, including their roles (e.g., PARTY_TYPE_OWNER, PARTY_TYPE_CONTROLLER).
- records_required: A list of records that must be included in scopes created from this specification.
- Response:
- The response will include the UUID of the newly created Scope Specification, which can then be used to create scopes.
Success checklist
- Scope created with expected owners and value_owner_address.
- Any required data_access principals are present
- Value owner change is reflected in reads (ownership & value-ownership queries)
Create a scope
What this does: creates a new non-fungible asset (Scope) from a specification, with optional owners, data_access, value_owner_address, and usd_mills for NAV context.
curl -X POST "$PROV_REST_BASE/metadata/scope-specification/\{SPEC_UUID\}/scope" \
-H "Authorization: Bearer $PROV_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owners": [
{
"address": "tp1OWNERADDRESS...",
"role": "PARTY_TYPE_OWNER",
"optional": false
}
],
"data_access": ["tp1DATAACCESS1...", "tp1DATAACCESS2..."],
"value_owner_address": "tp1VALUEOWNER...",
"usd_mills": 10000
}'
This page shows the path parameter uuid and fields like owners, data_access, value_owner_address, and usd_mills.
(Optional) Add data access (write)
When you need to grant addresses permission to receive off-chain data tied to this Scope, add them to data_access.
curl -X POST "$PROV_REST_BASE/metadata/scope/\{SCOPE_ID\}/data-access" \
-H "Authorization: Bearer $PROV_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data_access": ["tp1VIEWER1...", "tp1VIEWER2..."]
}'
Shows path param {id} (Scope identifier) and body shape.
Transfer value ownership (write)
Use this to change who controls the value associated with one or more Scopes (e.g., move asset ownership from issuer to investor account).
curl -X PATCH "$PROV_REST_BASE/metadata/scope/update-value-owners" \
-H "Authorization: Bearer $PROV_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scope_uuids": ["3d8de57d-bda1-4635-a377-3d9a8a5c3fca"],
"value_owner_address": "tp1NEWVALUEOWNER..."
}'
Tip: You can transfer for multiple scopes at once by passing several UUIDs in scope_uuids. Response includes tx_hash and height.
Verify Scope State & Ownership (reads)
Get Scope (by ID) — base view
curl -s -H "Authorization: Bearer $PROV_API_TOKEN" \
"$PROV_REST_BASE/metadata/scope/\{SCOPE_ID\}"
Materialized Scope (hydrate records)
Providing an assembled view for UI/analytics
curl -s -H "Authorization: Bearer $PROV_API_TOKEN" \
"$PROV_REST_BASE/metadata/scope/\{SCOPE_ID\}?hydrate=recordA&hydrate=recordB"
# Optional: add &block_height=### to inspect historical state
Supports hydrate and block_height query params.
Ownership & Value Ownership (for dashboards & checks)
Owned By Address (address appears in owners list):
curl -s -H "Authorization: Bearer $PROV_API_TOKEN" \
"$PROV_REST_BASE/provenance/metadata/v1/ownership/\{ADDRESS\}"
Value Owned By Address Address is the value_owner_address
curl -s -H "Authorization: Bearer $PROV_API_TOKEN" \
"$PROV_REST_BASE/provenance/metadata/v1/valueownership/\{ADDRESS\}"
NAV History
If you publish NAV (Net Asset Value) for this asset, you can retrieve NAV events to power dashboards:
curl -s -H "Authorization: Bearer $PROV_API_TOKEN" \
"$PROV_REST_BASE/provenance/metadata/v1/netassetvalues/\{SCOPE_ID\}"
(Optional) Add a Session (write)
Sessions are used to alter/add records within a Scope. Minimal example (one responsible party):
curl -X POST "$PROV_REST_BASE/metadata/scope-specification/\{SPEC_UUID\}/session" \
-H "Authorization: Bearer $PROV_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"party": {
"address": "tp1PARTY...",
"role": "PARTY_TYPE_CONTROLLER",
"optional": false
}
}'
Use the full schema on the SPEC page when attaching records/public records.
Continuation: Transfer → Trade Flow
If your Scope represents an asset that will be listed/traded, continue with Markets/Orders in the Trading & Settlement hub (see Settlement group in API reference).
- Spec root: https://docs.nuvalabs.com/api (see Settlement)