Skip to main content

Read and submit price feeds

Read the on-chain price-feed directory, latest values, rounds, and staleness windows, and submit a signed observation as a trusted issuer on V1 or V2.

Overview

A feed is an on-chain series of values, usually the price of an asset or an exchange rate, that DALP reads for valuation and settlement. It maps a subject address, the asset the value is about, and a topic, the kind of value, to one governed source of observations. An observation is a value with the time it was seen. The API reads the directory, feed configuration, values, and staleness policy, and authorised issuers can submit observations. Price sources publish prices into feeds from a provider, keyed by ISIN, the International Securities Identification Number. Read Market data for the governance model and the V1 or V2 reference for complete schemas.

Use V2 for a new integration. V1 routes use /api/v1/system/feeds/* and have no staleness-window or price-source routes. V2 routes use /api/v2/system/feeds/*. Both accept a session cookie or X-Api-Key, including on submission routes.

Call GET /api/v2/system/feeds/capabilities before using a feed capability. The directory, issuer-signed factory, or adapter factory can be absent from an organisation. An unavailable capability returns an error, not an empty feed list.

Two ways a feed gets registered

An issuer-signed feed is created through the platform's factory. The create request sets its decimals, subject, topic, and description. The platform fixes its retention to the latest round on-chain, requires every submitted value to be greater than zero, and allows no future drift from chain time in the feed contract. The create request does not expose those three settings. A rate or exchange-rate movement that can be zero or negative is unsupported by native feed creation. The indexed API can still serve past accepted observations. Read the feed's configuration with GET /api/v2/system/feeds/{feedAddress}/configuration before using it for valuation or settlement.

An external feed, such as a Chainlink-compatible aggregator already deployed on chain, is registered rather than created: POST /api/v2/system/feeds/external-registrations maps an existing contract into the directory at a subject and topic. Replacing the feed at that key with PUT /api/v2/system/feeds/registrations retires the previous address once the queued write settles. Removing it with DELETE /api/v2/system/feeds/registrations only deletes the directory mapping. The feed contract itself stays deployed and readable at its own address. Neither the old feed's history nor its own contract state changes.

A stable adapter gives consumers one address that survives a feed replacement underneath it. POST /api/v2/system/feeds/adapters deploys one for a subject and topic. Hold onto adapterAddress from the response, not the feed address behind it, especially from a smart contract or any long-lived integration.

An adapter on an issuer-signed feed changes its kind but not its origin. kind reads aggregator-adapter as soon as an adapter exists for the feed, so it no longer says who feeds it. origin says whether the platform's factory created the feed and, for any other feed, whether it has an adapter:

  • issuer-signed: the platform's factory created the feed and a trusted issuer signs its values, with or without an adapter.
  • aggregator-adapter: a feed that the factory did not create, such as a Chainlink-compatible aggregator, and that has an adapter.
  • external: a feed that the factory did not create and that has no adapter. This includes a DALP feed registered in the directory without the factory.

Read origin, not kind, to tell whether the platform treats a feed as issuer-signed. The platform does the same: an issuer-signed feed that has an adapter can be mapped to a price source, the source publishes to it, and that feed's item on GET /api/v2/system/feeds names the source in provenance. origin is on GET /api/v2/system/feeds, GET /api/v2/system/feeds/{feedAddress}, and GET /api/v2/system/feeds/{feedAddress}/configuration, and the list filters on it with filter[origin].

Decide who may submit a value

A submission passes two checks before the platform queues it. The role check does not depend on the feed's topic.

The caller must hold the feedsManager role on the system, or the governance role on the feed's subject. The check runs against the wallet that executes the request, so a caller who holds neither is refused with 403 and FEED_MANAGER_PERMISSION_REQUIRED.

Registering an external feed with POST /api/v2/system/feeds/external-registrations, replacing a feed with PUT /api/v2/system/feeds/registrations and removing one with DELETE /api/v2/system/feeds/registrations need the feedsManager role on the system. The feeds directory contract accepts these writes only from that role, so a holder of the governance role on the subject is refused with the same 403 before any gas is spent. The Console hides Replace feed and Remove feed from that holder.

The platform then signs the update itself, for the organisation's own operations account, and that account carries the organisation's issuer identity. The identity must be a trusted issuer for the feed's topic in the feed's trustedIssuersRegistry, or the submission is refused with 403 and FEED_ISSUER_NOT_TRUSTED. Read the registry address from the feed's configuration response, and add the organisation's issuer identity to it for the topic before you submit.

Both checks run before the platform queues the write, so a caller refused by either one spends no gas. The trusted-issuer check reads the indexed registry, so while the index trails a recent change to the registry, the feed contract can still refuse the update on chain with the same FEED_ISSUER_NOT_TRUSTED. A custody policy that refuses to sign the update answers with 403 and FEED_SUBMISSION_NOT_AUTHORIZED instead: review the policy for the issuer key, not the registry.

Read the latest value and staleness

Each feed has one source for its latest round, and the platform picks it per feed, not per request. A feed that the indexer follows as a DALP scalar feed, such as an issuer-signed feed, is read from the index, whether or not an adapter sits in front of it. Every other feed, such as an externally registered aggregator, is read from the feed contract when you call. The feed kind and an adapter address do not decide the source, so read latestRound rather than inferring it. latestRound on GET /api/v2/system/feeds and GET /api/v2/system/feeds/{feedAddress} names the source for each feed: indexed, chain, or none when the index follows the feed but holds no round yet.

curl "https://your-instance.example.com/api/v2/system/feeds/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/latest" \
  -H "X-Api-Key: sm_dalp_..."

An indexed feed answers:

{
  "data": {
    "feedAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
    "roundId": "42",
    "answer": "108420000",
    "decimals": 8,
    "formattedAnswer": "1.0842",
    "startedAt": "2026-03-11T16:02:08.000Z",
    "updatedAt": "2026-03-11T16:02:08.000Z",
    "recordedAt": "2026-03-11T16:02:20.000Z",
    "answeredInRound": "42",
    "description": "EUR / USD",
    "version": "1",
    "asOfBlockNumber": "18445240"
  },
  "links": {
    "self": "/api/v2/system/feeds/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/latest"
  }
}
Read
Source
Freshness
GET /api/v2/system/feeds/{feedAddress}/latest, feed with latestRound of indexed
The index.
recordedAt is the block time of the write the index read, and asOfBlockNumber is the block the index has processed. The value can trail the chain by the indexer's lag.
GET /api/v2/system/feeds/{feedAddress}/latest, feed with latestRound of chain
The feed contract, read when the request runs.
recordedAt and asOfBlockNumber are null. The value is what the contract reports when the request runs.
GET /api/v2/system/feeds/{feedAddress}/latest, feed with latestRound of none
The index.
Answers 404 DALP-0131 (FEED_ROUND_NOT_FOUND) until the index reads a round.
GET /api/v1/system/feeds/{feedAddress}/latest and GET /api/v1/system/feeds/{feedAddress}/round/{roundId}
The feed contract, for every feed.
Always live. V1 has no recordedAt.
GET /api/v1/system/feeds/{feedAddress}/staleness
The index for the latest answer, and the chain head for the current time.
The age is the chain head time minus the indexed update time. A feed with no indexed answer yet is refused.
GET /api/v2/system/feeds and GET /api/v2/system/feeds/{feedAddress}
The index.
Directory data only, so it trails the chain by the indexer's lag. Use latestRound to decide where to read the value.

For an indexed feed, updatedAt is the time the value was observed and recordedAt is when the chain recorded it. Apply a freshness rule to updatedAt for either source, and check asOfBlockNumber when you need to know how far the index has read. For a valuation, compare the age of updatedAt with the effective staleness window.

Staleness is a governed policy, and only V2 exposes it. GET /api/v2/system/feeds/staleness-windows/effective?subject=...&topicId=... resolves the same window the platform's own price-age checks enforce elsewhere, such as before a mint. Two governed inputs feed that resolution, and exactly one effective value comes out:

How DALP resolves the effective staleness windowThe global default and per-feed override resolve to one effective window. An override can set a seconds value, mark the feed as never expiring, or inherit the global default. The effective source and age limit determine whether a value is stale.Staleness policyGlobal defaultSystem-wide age limitPer-feed overrideSeconds, never expires,or inherit the global defaultAge limit + sourceEffective windowStale if age exceeds the limit

Two governed inputs resolve to one effective window, tagged with its source. The platform's own staleness checks, including the one that gates a mint, use that resolved value.

Read the effective window rather than inferring it, because the default depends on how the resolver was set up. A resolver that has no observed global-window event keeps the on-chain default of one day. The platform initialises a new resolver to never-expires only when the account that creates it holds the Feeds Manager role, and the effective window is never-expires once an event setting it has been observed. The response's source field tells you which input won: "feed-override" or "global-default".

A bare maxAgeSeconds: 0 on the setter routes cannot express both “clear this feed's override” and “reject every value as stale.” That ambiguity is why the override is a tagged choice rather than a number. PUT /api/v2/system/feeds/staleness-windows takes subject, topicId and a policy of {"mode": "seconds", "seconds": N}, {"mode": "never"}, or {"mode": "inherit"} for one feed, with an optional walletVerification. PUT /api/v2/system/feeds/staleness-windows/global sets the system-wide default with {"mode": "seconds", "seconds": N} or {"mode": "never"}, and has no inherit option. GET /api/v2/system/feeds/staleness-windows/global reads that default back in the same shape, so a client can read it, change it, and write it back.

V1 has no equivalent to any of the three staleness-window routes. A V1-only integration can read a feed's raw age against a threshold it supplies on each call, but it cannot read or set the platform's governed window, and a staleness-window call on V1 returns 404.

Submit a signed value

The submission route resolves the issuer's nonce and signs for you, so an integration never reads or tracks nonces itself.

POST /api/v2/system/feeds/{feedAddress}/submissions takes the human value as a decimal string, such as "1.08". The platform converts it to on-chain units using that feed's decimals. A value with more decimal places than the feed stores is refused with 400, FEED_PRICE_NOT_REPRESENTABLE (DALP-12245), before anything is signed. Round the value to the feed's decimals and submit it again. It also takes observedAt as a Unix-seconds timestamp. The platform clamps observedAt into the window from the feed's latest observation to the current chain time, so a future timestamp is published at chain time. It refuses the submission with 400, FEEDS_SUBMIT_OBSERVED_AT_TOO_FAR_IN_FUTURE (DALP-0639), only when the feed already holds an answer newer than chain time. The submit route resolves a signer rather than an executing wallet: the platform signs with the organisation's operations account, while the wallet that executes the request carries the role check. It answers like other queue-backed writes: 200 when the write lands within the wait that Prefer asks for, otherwise 202 with Location set to the transaction's status URL. This route also declares an X-Transaction-Hash header on both answers, and Preference-Applied echoes only the Prefer directives the platform honoured. See Idempotency and async settlement for the retry and polling discipline. This route follows it exactly, with no feed-specific exception.

Feed prices from a provider

A price source maps the ISINs a provider delivers to the price feeds of your tokens. The platform checks each delivered price and publishes the ones that pass through the organisation's operations signer. When the prices family of the organisation's approval policy is on, a price the checks flag waits for a second person, as Review a held price describes. Use a price source when a custodian or market-data vendor, not a person, is the origin of a token's price. The V2 routes are under /api/v2/price-sources, which needs the Feeds Manager role, and /api/v2/held-prices, which accepts the Feeds Manager role or the governance role of the held price's token.

Price sources support five protocols: an SFTP CSV file, an SFTP ISO 20022 reda.001 file, FIX market data over FIXT.1.1 or FIX.4.4, Bloomberg Data License, and LSEG DataScope Select. The Bloomberg and LSEG requests are built from each vendor's public documentation and have not run against the live service. The routes store a source's settings and credential, map instruments, and run the checks, review, and stale alert described here. The platform fetches each delivery itself, because no route accepts a pushed one. It polls an SFTP source for files, keeps one session open to a FIX source and reads its market data messages, and sends a Bloomberg or LSEG source's request at the clock times the source names. A vendor request names only the source's mapped ISINs and the source holds no instrument list of its own, so mapping an ISIN is what puts it on the request.

Set a source up in this order:

  1. POST /api/v2/price-sources creates a draft source with its connection settings, publish threshold, hold tolerance, and timing. The platform rejects a host on a loopback, link-local, metadata, or private address unless the deployment's allow list covers its range, and no allow list lifts a cloud metadata address (DALP-14704). An operator sets the allow list, the caps on each delivery, and the network paths described in Price source prerequisites.
  2. PUT /api/v2/price-sources/{sourceId}/credential stores the credential. The credential is write-only: no response returns it, only credentialSet and a credentialHint. The hint holds the last characters of the secret so an operator can tell which credential is stored: up to three, fewer for a secret under nine characters, and an empty string for a secret of one or two characters. For a private key it holds the last characters of the hex SHA-256 digest of the key text, never of the key. Send the expectedCredentialVersion you read, or a concurrent change answers 409 (DALP-14738). A retired source takes no new credential and answers 409 (DALP-14741): create a new source instead.
  3. For an SFTP source, PUT /api/v2/price-sources/{sourceId}/host-key accepts the server's host key fingerprint after you check it with the provider.
  4. POST /api/v2/price-sources/{sourceId}/instruments maps up to 50 ISINs to tokens in one request. A token with no price feed gets an issuer-signed feed in the same operation. The response is a stored operation with a links.self link. Poll GET /api/v2/price-sources/{sourceId}/instrument-operations/{operationId} until its status is no longer in_progress. Each item reads pending, mapped, completed, or failed, and a failed item carries the code and message of its error. The operation is failed when it has finished and any item failed.
  5. POST /api/v2/price-sources/{sourceId}/transitions with activate starts the source. The same route takes pause and retire.

After step 2 you can check the connection before you activate. POST /api/v2/price-sources/{sourceId}/connection-tests queues a test for any protocol, and GET /api/v2/price-sources/{sourceId}/connection-tests/{testId} returns its result. The test runs from the scheduler host and reads no price. It lists the remote path for SFTP, opens the connection without logging on and, for TLS, checks the provider's certificate for FIX, lists the account's catalogues for Bloomberg, and asks for a fresh token for LSEG. A test that does not finish within 30 seconds fails.

An active SFTP, Bloomberg, or LSEG source can fetch before its next scheduled run. POST /api/v2/price-sources/{sourceId}/deliveries queues the fetch and answers with an operation, and GET /api/v2/price-sources/{sourceId}/delivery-operations/{operationId} returns it until its status is succeeded or failed. The operation records the user who asked, and links.self points at it. A Feeds Manager can call both routes, with a signed-in session or an API key and no wallet verification. The POST declares no idempotency and reads no Idempotency-Key. A retry while the first request still waits answers 409 with DALP-14743, but a retry after the scheduler claimed the request queues another fetch, and for Bloomberg or LSEG that is another vendor request. Read the operation, or the source, before you repeat the request. The scheduler host that holds the source's lease picks the request up within 10 seconds. An SFTP source then polls its remote path at once. A Bloomberg or LSEG source starts one request and collects its result as it does on schedule. A successful operation reports how many deliveries the fetch produced. A failed one carries the reason.

  • A FIX source streams its prices and answers 422 with DALP-14742.
  • A source that is not active answers 409 with DALP-14736. Activate it first.
  • A source with a fetch still waiting for the scheduler answers 409 with DALP-14743 and names the waiting operation. Read that operation instead of asking again.

Every delivery from the fetch names the user who asked. If one of its prices is held, that user cannot approve it, and another Feeds Manager or the token's governor can. A delivery from the schedule names no one.

To remove a credential, send POST /api/v2/price-sources/{sourceId}/credential/transitions with {"transition": "remove", "expectedCredentialVersion": 3, "walletVerification": {"secretVerificationCode": "123456", "verificationType": "PINCODE"}}. The platform deletes the secret, clears the hint, advances credentialVersion and answers the source. An active source answers 409 (DALP-14740), so pause it first. A credentialVersion that moved since you read it answers 409 with DALP-14738 and removes nothing. A source that already holds no credential answers 200 whatever version you send. Removal also clears the credential of a retired source, whose secret retirement already deleted. Activation answers 409 (DALP-14737) for a source without a credential, including one whose credential a concurrent removal took.

Activation, editing a source with PATCH, setting or removing the credential, the host key, and instrument mapping each need a signed-in session and a wallet verification. An API key answers 403 (DALP-14734). Pause, retire, create, and the reads work with an API key. Because these writes carry a wallet verification or a secret, they declare no idempotency, so read the source again before you repeat one. When the organisation lists the platform-settings protection group, creating or editing a source, a source transition, setting or removing the credential, the host key, and instrument mapping answer 202 and wait as a pending approval until a second person decides them, as Actions describes.

The platform rejects a heartbeat that cannot fit the staleness bound. The heartbeat plus the source's expected delivery interval plus its expected lag must be shorter than the bound, which is the feed's effective staleness window when it is finite and otherwise the source's staleAfterSeconds. A request that breaks the rule answers 422 with DALP-14703.

What happens to a delivered price

Each price becomes an observation with its ISIN, price, currency, price type, and observed time. The platform applies these gates in order and records the outcome on the observation:

Outcome
Meaning
duplicate
The same source, instrument, price type, observed time, and value arrived before.
invalid, unmapped, currency_mismatch
The price is not positive or cannot be written at the feed's decimals, the ISIN has no live mapping, or the currency differs from the feed's quote currency.
out_of_order, future_dated
The observed time is earlier than the feed's latest, or more than 60 seconds ahead of the chain head timestamp. The platform never moves an observed time to make it fit.
correction_held
The same observed time and price type arrived with a different value. With the prices approval family on, the price is held. With it off, the price publishes and the observation never shows this outcome.
held
The price differs from the last published price by more than the source's hold tolerance. With the prices approval family on, the price is held. With it off, the price publishes and the observation never shows this outcome.
skipped_policy
The price did not move by the publish threshold, and no heartbeat interval has elapsed.
published, superseded, publish_failed
The price reached the feed, a newer observation replaced it in the queue for that feed, or the publish did not complete.

The platform judges time on the chain head timestamp, not the server clock. Read an observation with GET /api/v2/price-sources/{sourceId}/observations, filtered by filter[status] and filter[isin]. The list is newest first, so with filter[isin] its first row is the latest observation of that ISIN. Read the source's files and messages with GET /api/v2/price-sources/{sourceId}/deliveries. Each delivery carries outcomes, the counts of how its observations settled: accepted, rejected, dropped, held and published. accepted overlaps held and published, and an unsettled observation is in no group.

GET /api/v2/price-sources and GET /api/v2/price-sources/{sourceId} take include=metrics, which adds a metrics block to each source: the live instruments, the observations publishedToday since 00:00:00 UTC of the current UTC day, the open held prices, and lastDeliveryAt, which is null before the first delivery. include=latestDelivery adds the most recent delivery, and the two includes combine in one request. GET /api/v2/price-sources/{sourceId}/unmatched-isins lists the ISINs a source delivered that no mapping covers, and takes page[limit] and page[cursor] to page through them.

Review a held price

The platform holds a flagged price only when the prices family of the organisation's approval policy (PUT /api/v2/operations/approval-policy) is on at that moment. A price already held keeps waiting for a decision if the family is turned off later. Every family starts off. With it off, a price the checks flag publishes without review, the platform records a waived approval for that observation, and it creates no held price.

With the family on, a held price waits for a decision by a Feeds Manager or by the governor of that token. GET /api/v2/held-prices lists only the held prices the caller may decide, and takes filter[status] and filter[sourceId]. With both, meta.total is the open-hold count of that source. Each held price carries source, which holds the name of the source that delivered it and the hold tolerance in force at the time of the read, the one a decision checks against. A later edit of the source changes it. A user who created the source, edited its settings or mappings, accepted its host key, or queued the delivery that produced the hold cannot decide it. The platform checks that group again at every decision, so a user who edits the source after the hold joins it.

Read the evidence before you decide. GET /api/v2/held-prices/{heldPriceId}/evidence returns the full delivery to a Feeds Manager and only the record that produced the observation to a governor.

POST /api/v2/held-prices/{heldPriceId}/decisions approves or rejects. The request names reviewedObservationId, the evidenceDigest from the held price, and a wallet verification, and it needs a signed-in session. The platform checks the caller's authority again at decision time.

Answer
Cause
The latest candidate moved beyond the hold tolerance after you reviewed it. Read it again.
A newer price was published for the feed.
The candidate is past the staleness bound.
The source is paused. A rejection is still allowed.
The source is stopped. Restart it, then approve again. A rejection is still allowed.
You are in the group that cannot decide this held price.

Retiring the source cancels each of its held prices that still waits for a decision and withdraws its approval, so the held price reads cancelled. A retired source holds no new price.

An approved price goes to the feed through the feed's publish lane. Manual submissions, batch submissions, source publications, and token price writes to one feed take turns on that lane, one write at a time, because a feed accepts an issuer's updates only with strictly sequential nonces. If that publish fails, POST /api/v2/held-prices/{heldPriceId}/transitions with retry_publish and a wallet verification tries again.

Restart a stopped source

A source stops publishing after a credential or configuration failure. GET /api/v2/price-sources/{sourceId} returns the source's failureClass, which reads credential or config while the source is stopped. Its observations settle as publisher_not_authorized with the reason source_stopped. A transient failure retries on its own and does not stop the source.

Fix the cause first. A config failure means the organisation's identity is not deployed or is not an identity contract, or the organisation has no operations signer. A credential failure means the operations signer holds no claim key, the key an identity contract accepts for signing claims, or the organisation is not a trusted issuer for the price topic, as Decide who may submit a value describes. No price-source route changes the identity, the signer, or the trust.

The platform also stops a source when its fetch from the provider is refused three times in a row for a credential or configuration cause. A credential failure is a refused login or logon, a vendor answer of 401 or 403 other than a Bloomberg 403 that reports clock skew, a stored credential that is missing or of the wrong kind, or a Bloomberg credential past its expiry date. A configuration failure is a refused endpoint, a source setting the provider cannot serve, an SFTP host key problem, a FIX certificate or sequence-number problem, or a Bloomberg refusal of the scheduler's egress address. An unreachable provider, a timeout, and a vendor answer of 408, 429, or 5xx are transient, so the platform retries them. Fix a provider cause with the provider, in the source's settings, or by replacing the credential.

Then clear the stop. Send pause and then activate to POST /api/v2/price-sources/{sourceId}/transitions. Activation clears the failure class and rechecks the endpoint and heartbeat rules against the current settings. For a credential failure, PUT /api/v2/price-sources/{sourceId}/credential with a new credential also clears the stop, and a config failure stays until you activate. The new credential does not repair the signer or the trust, so the next publication stops the source again while those causes stand. Pause accepts an API key. Activation and the credential change need a signed-in session and a wallet verification.

Clearing a stop does not prove the cause is gone. The next publication or provider fetch checks again and stops the source again if the cause remains. A paused source publishes nothing, so a feed it maps can go stale until you activate it.

Stale feeds and provenance

A mapped feed that is older than its staleness bound at the chain head raises one operations case of kind stale-feed for each stale episode. The case names the source and the likely cause, such as a paused source or a held price, and resolves when a fresh price lands, the feed is unmapped, or the source is retired. Platform operators also get the metric dalp_market_data_stale_price_feeds and an alert rule on it. While the indexer trails the chain head by more than the configured bound, the platform makes no stale judgement and reports the lag.

Each item of GET /api/v2/system/feeds includes provenance. It names the source with its protocol, and its vendor for a Bloomberg or LSEG source, which is null for an SFTP or FIX source. It also gives the price field and type, the observationId that produced the latest published price, and that price's observed, recorded and published times. signedBy is the operations signer that signed the price, read from the on-chain event, so it stays null until the indexer has seen the transaction. approvedBy is the approver when a held price carried the price. A feed maintained by hand returns null.

Feed errors and retries

Most feed errors are caller mistakes: a bad address, a topic name and topic id that disagree, or a malformed request body. Check the error's retryable flag and the current instruction state before deciding whether another write is needed. Apart from a feed create, which returns its first result for a repeated idempotency key, the feed routes do not document request-level replay safety. Retaining the original key alone does not make an API retry safe:

  • The directory has no mapping yet for a subject and topic you expect to be registered. This happens when a registration transaction has confirmed but Ledger Data has not caught up. Confirm the registration, then retry once the index catches up.
  • The feed provider, or the organisation's price-feed signer, does not answer. Once the dependency recovers, inspect the original operation before deciding whether to submit again.
  • A queued write returns a transaction reference the platform cannot confirm. Check the original request status before submitting again.

Five failures are never worth retrying as sent: a topic name and topic id that resolve to different topics, a submission to a feed that already holds an answer newer than chain time (400, FEEDS_SUBMIT_OBSERVED_AT_TOO_FAR_IN_FUTURE, DALP-0639), a value with more decimal places than the feed stores (400, FEED_PRICE_NOT_REPRESENTABLE, DALP-12245), a submitted value of zero or below (400, FEED_VALUE_NOT_POSITIVE, DALP-14446), and an issuer-signed create for a subject and topic that already has a feed (409, FEED_ALREADY_EXISTS, DALP-14445). Each needs a corrected request, not a repeated one. The platform refuses the last three before it queues a transaction, so they cost no gas. A create repeated with its original idempotency key returns its first result, not the 409. A write that an operator cancels in the transaction queue before it settles answers 409, market-data.feeds.WRITE_CANCELLED (DALP-20525). It is not retryable as sent: nothing was published and the platform starts no new attempt, so send the write again with a new idempotency key if it is still wanted. A strict submission from a price source ends the same way and does not retry the cancelled write.

A write the queue has accepted and has not settled is checked every five seconds for its first minute, then at growing intervals: 90 seconds, doubling to a 15-minute cap. Its answer can therefore arrive up to 15 minutes after the write settles on a stalled sender.

Next steps

Was this page helpful?