---
title: "Submit a change in any registered domain"
method: POST
path: "/v3/changes"
tags: ["Changes"]
---

# Submit a change in any registered domain

`POST /v3/changes`

## Request

- Base URL: `https://v3.euler.finance`
- URL: `https://v3.euler.finance/v3/changes`
- Auth: API key in header `X-API-Key`

One body for every domain, token images included; an API key must send `evidence`. A key needs the domain's `:propose` or `:write` capability; the server computes the op class, runs its checks and decides with the domain policy, and an active suspension turns an auto-apply into a proposal. A stale `expectedVersion` records a superseded change; the same actor's open proposal on the target is superseded by the new one. A token keeps one intrinsic APY source. A `yield-sources` or `yield-pools` change that turns an entry on, as an add or an enable, returns 409 with `details.reason` `one-apy-source` while the token has another enabled yield source or DefiLlama pool, or is a Pendle PT that has not matured; `details.apySources` names the other source and the message says how to switch. This holds for every actor, platform sessions included. A key's proposal is refused at submit, and an approval or a revert that would turn a second source on is refused the same way. Disabling, removing or editing a source is always allowed. To switch, send the new source and the change that disables or removes the old one together to `POST /v3/changes/batch`. A platform session's `dismiss` applies only while the work list's dismiss rule holds, and returns 409 otherwise; a key's dismissal is a proposal, checked again when an admin approves it. A geo `update` or `remove` must name a stored rule; a derived address row from `GET /v3/geo-policies` cannot be changed and returns 404. A price-sources `fill` from a key or a system actor applies on its own only when every fill check passes, `provider-fresh` included, which needs the provider's own time for its price (CoinGecko `last_updated_at`, DefiLlama `timestamp`, Pyth `publish_time`, or for an ERC-4626 share the time of the underlying's stored V3 price) to be at most 24 hours old and at most 5 minutes ahead. An ERC-4626 fill also needs `asset()` to be the identifier, any decimals override to equal the chain's, and the underlying to have a positive V3 price, taken as V3 selects it from any source, the on-chain fallback and other shares included. Pendle gives no price time; its price is a current quote, so the time of the read stands for it. A Pendle fill applies on its own only for the PT itself, when V3 has a Pendle market for it on a chain the Pendle price source supports and Pendle prices it above 0; a matured PT that Pendle still prices qualifies, at its redemption value. A Pyth source never applies on its own. When a provider or the chain cannot answer for a fill that could apply on its own (no current entry, a CoinGecko, DefiLlama, ERC-4626 or Pendle source, not disabled, from a key with `price-sources:write` or a system actor, and no active suspension), the change is not recorded and the request returns 503; send it again later. Any other change is recorded as a proposal with the failed checks. For token images, dispatches permissions by registered domain. `token-images:propose` permits staged candidates and `token-images:write` permits a verified CoinGecko or Pendle fill. Replacements and removals always remain proposals. The `derive` operation builds a Pendle PT's logo server-side from the current managed image of the token named in the optional `value.source` (by default the PT's recorded underlying in `token_integrations`), as a circle crop with the teal `#17e3c2` PT ring, recorded with `derivedFrom` (source digest) in its `derive-source` check. A platform session applies a derive; a system actor applies only an empty-slot fill from the recorded underlying's verified-provider image; any other derive is a proposal. A target that is not a Pendle PT returns 400; a source without a managed image, or one that already carries the ring, returns 409. A verified fill requires an eligible token class, empty managed slot, exact chain-and-contract provider match, and verified legacy cutover. Incomplete cutover or provider unavailability returns 503. API-key responses omit operator attribution. The generic change has expected/resulting versions and database decision order; token image snapshots and provider evidence are stored in a domain detail record.

## Query parameters

- `dryRun` boolean

## Request body

- ChangeSubmitRequest: The generic body for every domain. The server computes the operation class, runs its own checks and decides with the domain's policy.
  - `domain` 'token-images' | 'price-sources' | 'yield-sources' | 'yield-pools' | 'token-tags' | 'trust-entries' | 'screening-waivers' | 'geo', required
  - `operation` string, required
  - `key` string, required: The domain's target key, in the `keyFormat` that `GET /v3/changes/domains` serves: `<chainId>:<address>` for token images and for every dismissal or resume, `<source>/<chainId>:<address>` for price sources, `<tag>:<ruleType>:<value>` for token tags (rule types `symbols`, `symbolPrefixes`, `symbolContains`, `coingeckoCategories`, and `addresses`, whose value names one token as `<chainId>:<address>` in any case and is stored lowercase), and the address for screening waivers.
  - `value` unknown
  - `expectedVersion` string, nullable, required: The version the caller read; null when the target is empty. A stale version supersedes the change. Read it from `GET /v3/changes/targets/{domain}?key=…` (or `GET /v3/changes/targets/price-sources/{chainId}/{address}` for every source of a token).
  - `reason` string, required
  - `rationale` string, nullable
  - `evidence` ChangeEvidence: Required from API keys and system actors. Reviewers see it; the policy never reads it.
    - `model` string, required
    - `promptVersion` string, required
    - `runId` string, required
    - `sources` string[], required

## Response `200`

No exact provider candidate (token images), or a dry-run decision

- union
  - ChangeDryRunResult
    - `data` object, required
      - `wouldBe` 'applied' | 'proposed' | 'superseded', required
      - `opClass` string, required
      - `checks` ChangeCheck[], required
        - `id` string, required
        - `outcome` 'pass' | 'fail' | 'warn' | 'skip', required
        - `message` string, required
        - `details` object, nullable
      - `policy` ChangePolicyResult, required
        - `version` string, required
        - `decision` 'apply' | 'propose', nullable, required: Null on rows recorded before the generic policy existed.
        - `reasons` string[], required
    - `meta` PaginationMeta, required
      - `total` integer: Exact total count when the endpoint provides one.
      - `hasMore` boolean: Indicates whether another page exists beyond the current page.
      - `offset` integer
      - `limit` integer: Echoed page size after endpoint-side clamping.
      - `outcome` 'disabled' | 'removed': On a toolbox config DELETE, what happened to the row.
      - `products` ProductLabels[]: On a curation vault `POST`, `PUT`, or `PATCH`: the products the write touched (the one the vault left and the one it joined) as they are after the write, `governanceMode` included, so the caller applies them instead of re-reading.
        - `id` string, required
        - `chainId` integer, required
        - `entityId` string, required
        - `coBrandEntityIds` string[], required: Additional entities whose logos should be displayed. Product management remains exclusively with entityId.
        - `name` string, required
        - `logo` string, nullable
        - `description` string, nullable
        - `url` string, nullable: Absolute https URL.
        - `portfolioNotice` string, nullable
        - `isDeprecated` boolean, required
        - `deprecationReason` string, nullable
        - `notExplorable` boolean, nullable, required: Curator listing flag, outranked by platform overrides and by Euler's deprecation on the assessment. `null` leaves the decision to deprecation, `true` hides the vault(s) from discovery, `false` keeps listed a vault the curator deprecated, but never one Euler deprecated.
        - `governanceMode` 'governed' | 'ungoverned' | 'unknown', required: Derived by the server from the on-chain governor of member vaults; clients cannot set it. Products are created as `unknown`. Every vault assignment recomputes the mode over the resulting member set and rejects sets that mix governed and ungoverned vaults. Layer 2 adopts `governed` or `ungoverned` for an `unknown` product once its membership verifies against chain state. Entity members cannot update or delete an `ungoverned` product or its vaults after the initial assignment.
        - `resolved` ResolvedProductLabels, required: Read-time entity fallbacks for display. Derived, never stored.
          - `entityName` string, nullable, required
          - `url` string, nullable, required: Product url, falling back to the owning entity url.
          - `logo` string, nullable, required: Product logo, falling back to the owning entity logo.
        - `anyExplorableLend` boolean, required: True when at least one member vault is lend-discoverable per the materialized visibility verdicts. Live chain state (e.g. borrowability) is deliberately excluded.
        - `anyExplorableBorrow` boolean, required: True when at least one member vault is borrow-discoverable per the materialized visibility verdicts. Live chain state (e.g. borrowability) is deliberately excluded.
        - `createdAt` string, date-time, required
        - `updatedAt` string, date-time, required
        - `hold` LabelHoldSummary: What a draft read shows on a held row.
          - `id` string, uuid, required
          - `kind` 'quarantine' | 'entity-hold', required
          - `reason` string, required
          - `since` string, date-time, required
        - `platformListing` ProductPlatformListing: The platform-owned discovery decision on a product, as a product row shows it.
          - `notExplorableLend` boolean, required
          - `notExplorableBorrow` boolean, required
          - `reason` string, nullable, required
      - `timestamp` string, date-time, required
      - `chainId` string: Comma-separated chain IDs for multi-chain responses.
      - `degradedProviders` string[]: Live providers whose data is incomplete in this response.
      - `excludedChains` object[]: On a read across chains that names no `chainId`: the served chains it left out because they do not run the capability the operation needs, rather than reading them as empty. Absent when no chain was left out.
        - `chainId` integer, required
        - `capability` 'indexing' | 'activity' | 'prices' | 'historicalPrices' | 'assessments' | 'labels' | 'logos' | 'stats' | 'risk' | 'rewards' | 'apys', required
  - object

## Other responses

- `201`: Applied, proposed, or superseded change
- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `409`: Conflict
- `429`: Rate limited
- `503`: Service unavailable

## Changes

- **2026-10-25** `d880a68ad3e2`: 1 info
  - added the new `screening-waivers` enum value to the request property `domain`
- **2026-10-19** `f0ed0045ce41`: 1 info
  - added the optional property `anyOf[#/components/schemas/ChangeDryRunResult]/meta/excludedChains` to the response with the `200` status
- **2026-10-09** `1b550dddfa46`: 7 breaking, 6 warning, 14 info
  - added the new required request property `expectedVersion`
  - added the new required request property `key`
  - removed the enum value `fill` of the request property `operation`
  - removed the enum value `remove` of the request property `operation`
  - …23 more
- **2026-10-05** `875b105fe12a`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/euler/apis/euler-data-api-v3/changes/v3/changes/post.md)

---

[API](https://skmtc.dev/euler/apis/euler-data-api-v3.md) - [All operations](https://skmtc.dev/euler/apis/euler-data-api-v3/llms.txt) - [OpenAPI document](https://skmtc.dev/euler/apis/euler-data-api-v3/revisions/e987d7695ec1?raw)
