Objects
Tokenized instances of templates representing real-world or digital assets.
What are Objects?
Objects are the core asset unit in DUAL — tokenized representations of real-world or digital items. Each object is an instance of a template and carries its own state, ownership, and activity history. Objects can represent anything: event tickets, loyalty points, product authenticity certificates, property deeds, or digital collectibles.
Every object has a unique id, belongs to an organization, is owned by a wallet, and references the template it was minted from. Its properties can be read, updated, and transferred through actions.
Emitting (Creating) Objects
Objects are not created directly — they are emitted through the Event Bus by executing an emit action. This ensures every object creation is signed, logged, and sequenced as part of the platform's audit trail:
POST /ebus/execute
{
"action": {
"emit": {
"template_id": "tmpl_abc123",
"properties": {
"serial_number": "SN-2024-001",
"manufacture_date": "2024-01-15"
}
}
}
}
You can also create objects through the convenience endpoint POST /objects for simpler use cases where full Event Bus semantics aren't needed.
Object Properties
An object's properties are a JSON object inherited from its template and potentially overridden at emission time. Properties can be updated through actions — for example, an action might change a ticket's status from "valid" to "redeemed".
GET /objects/obj_xyz
→ {
"id": "obj_xyz",
"template_id": "tmpl_abc123",
"owner_id": "w_alice",
"properties": {
"serial_number": "SN-2024-001",
"manufacture_date": "2024-01-15",
"status": "valid"
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
Ownership & Transfers
Every object is owned by exactly one wallet at any given time. Ownership is recorded in the owner_id field. Transfers happen through actions — the Event Bus verifies that the sender is the current owner, then atomically updates owner_id to the recipient's wallet.
Ownership history is preserved in the object's activity log, providing a complete chain of custody.
Object Relationships
Objects can have parent-child relationships, enabling hierarchical asset structures. This is useful for modeling:
- Bundle → Items — A "gift box" object that contains individual product objects.
- Collection → Members — An album object with individual trading card objects.
- License → Sub-licenses — A master license with derived usage licenses.
Query relationships with GET /objects/{id}/children and GET /objects/{id}/parents. Both endpoints support cursor pagination.
Activity Log
Every action performed on an object is recorded in its activity log, providing a complete and immutable audit trail. The activity log answers "who did what, when" for every state change:
GET /objects/obj_xyz/activity
→ {
"items": [
{
"id": "act_001",
"action_type": "io.acme.product.emit",
"status": "confirmed",
"payload": { ... },
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": "act_002",
"action_type": "io.acme.product.transfer",
"status": "confirmed",
"payload": { "to": "w_bob" },
"created_at": "2024-01-16T14:00:00Z"
}
],
"next": null
}
Activity logs are immutable — entries cannot be edited or deleted. They are backed by the Sequencer's hash chain, meaning any tampering would break the cryptographic integrity of the entire batch.
Search & Discovery
DUAL provides several ways to find objects:
- List —
GET /objectsreturns objects with cursor pagination. Filter by template, owner, or properties. - Search —
POST /objects/searchaccepts a structured query payload for more complex filtering across property values, date ranges, and ownership. - Count —
POST /objects/countreturns the number of objects matching a query without fetching the full list. Useful for dashboards and analytics. - By ID —
GET /objects/{id}retrieves a single object by its unique identifier.
Relationship to Other Concepts
- Templates — Every object is minted from a template. The template defines the schema; the object carries the state.
- Wallets — Wallets own objects. Ownership is the primary access control mechanism for object data.
- Actions — Actions are the only way to modify object state (properties, ownership). Direct mutation is not possible — all changes go through the Event Bus for auditability.
- Faces — Faces determine how the object is visually rendered. Since faces are registered on templates, all objects of the same template share the same visual definitions.