Skip to main content

Assets, Oracles & Ownership

An asset represents an existing on-chain token inside Filedgr, with an optional price oracle and a verifiable link to the wallet that controls it.

Registering an asset does not mint a token

POST /assets takes a token_address — the ERC-20 must already exist. Registration deploys Filedgr's own wrappers around it: a registry, and (when create_oracle is true, the default) a price oracle.

Deployment is asynchronous. In practice an asset reports two statuses: FILEDGR_RECEIVED on registration, then DLT_DEPLOYED once the on-chain deployment lands — or ERROR. The registry_address, oracle_address and factory_address fields are filled in at that point.

note

FILEDGR_REVIEWED, DLT_DEPLOYING and ACTIVE exist in the AssetStatus enum but are not emitted by the current pipeline. Treat DLT_DEPLOYED as the terminal success state — waiting for ACTIVE will never return.

warning

Price and ownership calls fail until the asset reaches DLT_DEPLOYED. Wait for deployment — via the asset.deployed webhook event — before doing anything else.

Prices come from data attachments, not from your request

This is the least obvious thing about the asset API.

POST /assets/{asset_id}/prices body: {"stream_id": "...", "attachment_id": "..."}

There is no endpoint that accepts a price value. You upload a file containing prices as a data attachment to a stream, then point this endpoint at it. The platform parses the attachment and pushes the values to the oracle.

Three constraints are easy to miss:

  1. Ownership must be verified first. The request is rejected unless ownership_verified is true on the asset. Complete the ownership challenge before submitting any prices.
  2. The attachment must contain a CSV. The platform looks for a .csv object inside the attachment and ignores everything else.
  3. Every price_type in the file must already be registered on the asset; unknown names are rejected.
price_type,price,timestamp,currency
NAV,101.25,2026-08-26T12:00:00,USD
BID,101.10,2026-08-26T12:00:00,USD

currency is optional and defaults to USD. timestamp accepts ISO-8601 or YYYY-MM-DD HH:MM:SS. Rows that fail to parse are skipped.

Price types

A price type is a named series. Any name works — NAV, BID, ASK, MARKET and CUSTOM are the conventional ones. One is the primary.

POST /assets/{asset_id}/price-types add
GET /assets/{asset_id}/price-types list
DELETE /assets/{asset_id}/price-types/{price_type} remove
PUT /assets/{asset_id}/price-types/primary set the primary
warning

On-chain, a price type is identified by the hash of its name. Casing and whitespace are therefore permanent and load-bearing: NAV and nav are two different, non-interchangeable price types. Pick a convention before you create the first one.

Values are scaled by 10 ** decimals (default 8) into integers on-chain.

Reading prices

GET /assets/{asset_id}/prices current
GET /assets/{asset_id}/prices/{price_type} history for one type
GET /assets/{asset_id}/prices/chart chart series (price_type, from_date, to_date)

Everything that writes returns 202

Adding, removing and re-prioritising price types, and submitting prices, all return 202 Accepted with a short {"message": "..."} acknowledgement. They are queued, not applied. Failures arrive as an asset.error webhook event; there is no per-price success event, so read the result back from the typed price endpoints.

note

asset.updated is accepted as a webhook subscription but is never emitted. The asset events the platform produces are asset.created, asset.deployed and asset.error.

note

The response bodies of the price and price-type routes are not contractually stable — several return untyped objects. Treat them as opaque, and drive your logic from the webhook events and the typed read endpoints instead.

Proving ownership

A three-step challenge/response that proves a wallet controls the asset's token contract.

POST /assets/{asset_id}/ownership/challenge {"claimer_address": "0x..."} -> {claim_id, challenge_message}
POST /assets/{asset_id}/ownership/verify {"claim_id": "...", "signature": "0x..."} -> 202
GET /assets/{asset_id}/ownership/status -> poll

Sign the challenge_message returned by step 1, then submit the signature in step 2. The claim advances CHALLENGE_CREATEDVERIFICATION_SUBMITTEDVERIFIED or REJECTED, and a successful verification flips ownership_verified on the asset.

danger

The signing convention here is not the one used for Signatures. Ownership challenges hash the challenge string, so a standard message-signing call is what you want. The signature statement flow signs a raw digest with no prefix. Do not copy code between the two.

Failures do not surface as an HTTP error — step 2 returns 202 regardless. A rejected claim shows up as status: REJECTED with an error_message on the status endpoint, so you must poll or watch for asset.error.

Permissions

POST, DELETE and GET on /assets/{asset_id}/permissions, with levels OWNER, ADMIN, EDITOR, VIEWER — the same asynchronous, on-chain-settled model as vault permissions.