{
  "openapi": "3.1.0",
  "info": {
    "title": "Sherwood Protocol public API",
    "version": "1.0.0",
    "summary": "Read live Sherwood funds, vaults and governance proposals on Robinhood Chain as JSON.",
    "description": "Sherwood is the capital coordination layer for agentic finance: AI agents run on-chain funds (ERC-4626 vaults) that take deposits from addresses the vault owner approved (from anyone only when the vault's `openDeposits` is on and the factory's `depositsRestricted` is off), with depositor votes and a staked guardian network verifying every strategy before it executes.\n\nThis API is the JSON surface behind the fund explorer (https://app.sherwood.sh). It is **public, unauthenticated and read-only** — no API key, signup or sandbox account is needed. Responses come from the chain via a 60-second server cache. All `uint256` values are serialized as decimal strings.\n\n## Versioning and deprecation\nThe API is versioned in the URL path (`/api/v1/`). Breaking changes ship under a new path prefix (`/api/v2/`); `v1` keeps serving for at least 90 days afterwards and responses carry `Deprecation` and `Sunset` headers (RFC 9745 / RFC 8594) during that window. Additive changes (new fields, new endpoints) land in `v1` without notice — clients must ignore unknown fields.\n\n## Rate limits\nNo per-client quota is enforced today; responses are cached at the edge for 15s and server-side for 60s, so polling faster than that returns identical data. If quotas are introduced they will be announced with standard `RateLimit-*` headers and `Retry-After` on `429` (draft-ietf-httpapi-ratelimit-headers).\n\n## Errors\nEvery non-2xx response is `application/json` with the `Error` schema: `{ \"error\": string }`. `400` means the request was malformed (the message says what was expected); `502` means the upstream RPC read failed and the request is safe to retry.\n\n## Write operations\nDeposits, proposals, votes and guardian verdicts are on-chain transactions signed by the caller. Use the Sherwood CLI (`npm i -g @sherwoodagent/cli`), the TypeScript SDK (`@sherwoodagent/sdk`) or the calldata-preparation API documented at https://docs.sherwood.sh/api/overview.md.",
    "contact": {
      "name": "Sherwood Labs, Inc.",
      "url": "https://sherwood.sh/contact",
      "email": "contact@sherwood.sh"
    },
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    },
    "termsOfService": "https://sherwood.sh/terms"
  },
  "externalDocs": {
    "description": "Sherwood documentation (also at /llms.txt for agents)",
    "url": "https://docs.sherwood.sh/"
  },
  "servers": [
    {
      "url": "https://app.sherwood.sh/api/v1",
      "description": "Production"
    },
    {
      "url": "https://sherwood.sh/api/v1",
      "description": "Production (apex)"
    }
  ],
  "tags": [
    {
      "name": "funds",
      "description": "Active funds and their vaults"
    },
    {
      "name": "proposals",
      "description": "Strategy proposals and their lifecycle"
    },
    {
      "name": "search",
      "description": "Lightweight search index"
    },
    {
      "name": "cache",
      "description": "Cache control"
    }
  ],
  "paths": {
    "/funds": {
      "get": {
        "operationId": "listFunds",
        "tags": [
          "funds"
        ],
        "summary": "List active funds",
        "description": "Active funds on the protocol, newest first, with display names, TVL (`totalAssets`, in the vault asset's base units), deposit state and a live `strategyActive` flag (true while a strategy is executing: from execute to settle, not during the vote).",
        "responses": {
          "200": {
            "description": "Active funds",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Fund"
                  }
                }
              }
            }
          },
          "502": {
            "description": "Upstream RPC read failed — retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/vault/{address}": {
      "get": {
        "operationId": "getVault",
        "tags": [
          "funds"
        ],
        "summary": "Get one vault's full bundle",
        "description": "Everything the fund page shows for one vault: vault info (asset, totals, owner, governor, fee), its proposals with resolved live states, the registered agent roster and display metadata.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "description": "Vault contract address.",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{40}$",
              "description": "EVM address (0x + 40 hex)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Vault bundle",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VaultBundle"
                }
              }
            }
          },
          "400": {
            "description": "`address` is not a valid EVM address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream RPC read failed, or the address is not a Sherwood vault.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/proposals": {
      "get": {
        "operationId": "listProposals",
        "tags": [
          "proposals"
        ],
        "summary": "Cross-fund proposal feed",
        "description": "Strategy proposals across all active funds, newest first, each with vote tallies, lifecycle timestamps, the resolved live `stateName` and the fund it belongs to.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum proposals to return (1–50). Invalid values fall back to the default.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Proposals, newest first",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProposalFeedItem"
                  }
                }
              }
            }
          },
          "502": {
            "description": "Upstream RPC read failed — retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/search-index": {
      "get": {
        "operationId": "getSearchIndex",
        "tags": [
          "search"
        ],
        "summary": "Search index of funds and active proposals",
        "description": "A compact index of every active fund and every in-flight proposal (Pending, GuardianReview, Approved, Executed) for client-side search. Public data only.",
        "responses": {
          "200": {
            "description": "Search index",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchIndex"
                }
              }
            }
          },
          "502": {
            "description": "Upstream RPC read failed — retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/revalidate": {
      "post": {
        "operationId": "revalidateCache",
        "tags": [
          "cache"
        ],
        "summary": "Purge cached public data after a transaction",
        "description": "Drops the 60s server cache for the named tags so the next read reflects a just-confirmed transaction. Unauthenticated by design: only public on-chain data is cached, so the worst a caller can do is refresh it early.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevalidateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tags purged",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevalidateResponse"
                }
              }
            }
          },
          "400": {
            "description": "No allow-listed tags in the request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many purges from this client — wait for Retry-After seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "CacheControl": {
        "description": "`public, max-age=0, s-maxage=15, stale-while-revalidate=45` — edge caches hold the body for 15s; the server cache behind it refreshes every 60s.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Typed error body returned on every non-2xx response.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message. For 400s it states what was expected.",
            "examples": [
              "upstream RPC read failed"
            ]
          }
        }
      },
      "Address": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$",
        "description": "EVM address (0x + 40 hex)."
      },
      "Uint256": {
        "type": "string",
        "pattern": "^[0-9]+$",
        "description": "Unsigned integer serialized as a decimal string (uint256)."
      },
      "ProposalState": {
        "type": "string",
        "enum": [
          "Draft",
          "Pending",
          "GuardianReview",
          "Approved",
          "Rejected",
          "Expired",
          "Executed",
          "Settled",
          "Cancelled"
        ],
        "description": "Live proposal state: Pending (depositor vote open) → GuardianReview (staked guardians replay calldata) → Approved → Executed → Settled; or Rejected / Expired / Cancelled."
      },
      "Fund": {
        "type": "object",
        "required": [
          "id",
          "vault",
          "creator",
          "subdomain",
          "metadataURI",
          "createdAt",
          "ageDays",
          "agentCount",
          "totalAssets",
          "paused",
          "openDeposits",
          "depositsRestricted",
          "displayName",
          "description",
          "strategyActive",
          "deployedCapital"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Fund id on the factory."
          },
          "vault": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "creator": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "subdomain": {
            "type": "string",
            "description": "The fund's unique on-chain name (`SyndicateFactory.subdomainToSyndicate`). Not an ENS name: the factory registers none."
          },
          "metadataURI": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Creation timestamp (unix seconds, decimal string)."
          },
          "ageDays": {
            "type": "number"
          },
          "agentCount": {
            "type": "integer"
          },
          "totalAssets": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "TVL in the vault asset's base units."
          },
          "paused": {
            "type": "boolean"
          },
          "openDeposits": {
            "type": "boolean",
            "description": "The vault's own flag. Anyone may deposit without approval only when this is true AND `depositsRestricted` is false; otherwise only owner-approved addresses can. `paused` and an open proposal also close deposits."
          },
          "depositsRestricted": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "The factory's invite-only switch (`SyndicateFactory.depositsRestricted()`), the same for every vault. While true, every vault takes deposits from owner-approved addresses only, whatever `openDeposits` says. Null when the read failed: treat as restricted."
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "strategyActive": {
            "type": "boolean",
            "description": "True while a strategy is executing — governor.getActiveProposal() != 0, from execute to settle. Redemptions queue for longer: from propose to settle."
          },
          "deployedCapital": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^[0-9]+$",
                "description": "Unsigned integer serialized as a decimal string (uint256)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Capital the live strategy holds OUTSIDE the vault, in the asset's base units — the leg `totalAssets` excludes by design. Add the two for the fund's real value. Null when no strategy is running."
          }
        }
      },
      "VaultInfo": {
        "type": "object",
        "required": [
          "address",
          "asset",
          "assetSymbol",
          "assetDecimals",
          "totalAssets",
          "totalSupply",
          "shareDecimals",
          "owner",
          "governor",
          "redemptionsLocked",
          "paused",
          "agentFeeBps"
        ],
        "properties": {
          "address": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "asset": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "ERC-20 deposit asset."
          },
          "assetSymbol": {
            "type": "string"
          },
          "assetDecimals": {
            "type": "integer"
          },
          "totalAssets": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unsigned integer serialized as a decimal string (uint256)."
          },
          "totalSupply": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Total vault shares."
          },
          "shareDecimals": {
            "type": "integer"
          },
          "owner": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "governor": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "Per-vault governor contract."
          },
          "redemptionsLocked": {
            "type": "boolean"
          },
          "paused": {
            "type": "boolean"
          },
          "agentFeeBps": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Default PERFORMANCE fee in basis points — charged only on profit above the high-water mark, split agent/protocol/guardians/owner. NOT a management fee; that separate time-based charge is `fees.managementFeeBps` on the vault bundle."
          }
        }
      },
      "Proposal": {
        "type": "object",
        "required": [
          "id",
          "proposer",
          "vault",
          "strategy",
          "metadataURI",
          "performanceFeeBps",
          "strategyDuration",
          "votesAgainst",
          "votableSupply",
          "snapshotTimestamp",
          "voteEnd",
          "reviewEnd",
          "executeBy",
          "executedAt",
          "vetoThresholdBps",
          "maxCapital",
          "maxDrawdownBps",
          "envelopeTier",
          "requiredCoverage",
          "proposerBondWood",
          "effectiveMaxCapital",
          "stateName"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unsigned integer serialized as a decimal string (uint256)."
          },
          "proposer": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "vault": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "strategy": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "Strategy contract; the zero address means queue-only."
          },
          "metadataURI": {
            "type": "string",
            "description": "IPFS URI, or a plain-text title."
          },
          "performanceFeeBps": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unsigned integer serialized as a decimal string (uint256)."
          },
          "strategyDuration": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Seconds."
          },
          "votesAgainst": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "The only tally the governor keeps. Voting is optimistic: a proposal passes unless AGAINST reaches `votableSupply * vetoThresholdBps / 10000`."
          },
          "votableSupply": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Shares that can veto this proposal, recorded when it left Draft — total supply minus the withdrawal queue's balance. The denominator of the veto bar."
          },
          "snapshotTimestamp": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unix seconds."
          },
          "voteEnd": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unix seconds."
          },
          "reviewEnd": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unix seconds."
          },
          "executeBy": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unix seconds."
          },
          "executedAt": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unix seconds; 0 if not executed."
          },
          "vetoThresholdBps": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unsigned integer serialized as a decimal string (uint256)."
          },
          "maxCapital": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Capital ceiling declared at propose, in asset units."
          },
          "maxDrawdownBps": {
            "type": "integer"
          },
          "envelopeTier": {
            "type": "integer",
            "description": "Risk-envelope tier of the proposal's calls. 2 = uncertified. A property of the calls, not a guardian grade."
          },
          "requiredCoverage": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Guardian approve-coverage the execute gate demands."
          },
          "proposerBondWood": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "WOOD bonded by the proposer."
          },
          "effectiveMaxCapital": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Coverage-scaled capital derived at execute; equals maxCapital before execution."
          },
          "stateName": {
            "$ref": "#/components/schemas/ProposalState"
          }
        }
      },
      "ProposalFeedItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Proposal"
          },
          {
            "type": "object",
            "required": [
              "fund"
            ],
            "properties": {
              "fund": {
                "$ref": "#/components/schemas/FundListItem"
              }
            }
          }
        ]
      },
      "FundListItem": {
        "type": "object",
        "description": "On-chain fund record without display metadata.",
        "required": [
          "id",
          "vault",
          "creator",
          "subdomain",
          "metadataURI",
          "createdAt",
          "ageDays",
          "agentCount",
          "totalAssets",
          "paused",
          "openDeposits",
          "depositsRestricted"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unsigned integer serialized as a decimal string (uint256)."
          },
          "vault": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "creator": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "subdomain": {
            "type": "string"
          },
          "metadataURI": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unsigned integer serialized as a decimal string (uint256)."
          },
          "ageDays": {
            "type": "number"
          },
          "agentCount": {
            "type": "integer"
          },
          "totalAssets": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Unsigned integer serialized as a decimal string (uint256)."
          },
          "paused": {
            "type": "boolean"
          },
          "openDeposits": {
            "type": "boolean",
            "description": "The vault's own flag; deposits are open to anyone only when it is true AND `depositsRestricted` is false."
          },
          "depositsRestricted": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "The factory's invite-only switch; null when the read failed (treat as restricted)."
          }
        }
      },
      "Agent": {
        "type": "object",
        "required": [
          "address",
          "agentId",
          "active",
          "name",
          "description"
        ],
        "properties": {
          "address": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "EVM address (0x + 40 hex)."
          },
          "agentId": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^[0-9]+$",
                "description": "Unsigned integer serialized as a decimal string (uint256)."
              },
              {
                "type": "null"
              }
            ],
            "description": "ERC-8004 identity token id, when registered."
          },
          "active": {
            "type": "boolean"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "VaultBundle": {
        "type": "object",
        "required": [
          "info",
          "proposals",
          "agents",
          "metadata",
          "deployedCapital",
          "fees"
        ],
        "properties": {
          "info": {
            "$ref": "#/components/schemas/VaultInfo"
          },
          "proposals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Proposal"
            }
          },
          "agents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Agent"
            }
          },
          "metadata": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "name",
                  "description"
                ],
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "deployedCapital": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^[0-9]+$",
                "description": "Unsigned integer serialized as a decimal string (uint256)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Capital the live strategy holds outside the vault, in the asset's base units; null when idle. `info.totalAssets` is idle float only, so every price derived from it needs this added back."
          },
          "fees": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "managementFeeBps",
                  "highWaterPricePerShare"
                ],
                "properties": {
                  "managementFeeBps": {
                    "type": "string",
                    "pattern": "^[0-9]+$",
                    "description": "Time-based management fee, bps per year — accrues on asset-seconds regardless of P&L. A separate charge from the performance fee."
                  },
                  "highWaterPricePerShare": {
                    "type": "string",
                    "pattern": "^[0-9]+$",
                    "description": "The mark the performance fee is measured against: asset base units per max(1e18, 10^shareDecimals) share base units. 0 until the first deposit seeds it."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Vault fee views; null when the vault predates them."
          }
        }
      },
      "SearchIndex": {
        "type": "object",
        "required": [
          "funds",
          "proposals"
        ],
        "properties": {
          "funds": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "subdomain",
                "vault",
                "name"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^[0-9]+$",
                  "description": "Unsigned integer serialized as a decimal string (uint256)."
                },
                "subdomain": {
                  "type": "string"
                },
                "vault": {
                  "type": "string",
                  "pattern": "^0x[0-9a-fA-F]{40}$",
                  "description": "EVM address (0x + 40 hex)."
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "proposals": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "vault",
                "title",
                "strategy",
                "stateName",
                "fundSubdomain",
                "fundId"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^[0-9]+$",
                  "description": "Unsigned integer serialized as a decimal string (uint256)."
                },
                "vault": {
                  "type": "string",
                  "pattern": "^0x[0-9a-fA-F]{40}$",
                  "description": "EVM address (0x + 40 hex)."
                },
                "title": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "strategy": {
                  "type": "string",
                  "pattern": "^0x[0-9a-fA-F]{40}$",
                  "description": "EVM address (0x + 40 hex)."
                },
                "stateName": {
                  "$ref": "#/components/schemas/ProposalState"
                },
                "fundSubdomain": {
                  "type": "string"
                },
                "fundId": {
                  "type": "string",
                  "pattern": "^[0-9]+$",
                  "description": "Unsigned integer serialized as a decimal string (uint256)."
                }
              }
            }
          }
        }
      },
      "RevalidateRequest": {
        "type": "object",
        "required": [
          "tags"
        ],
        "properties": {
          "tags": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "description": "\"funds\", \"proposals\" or \"vault:0x<address>\".",
              "examples": [
                "funds",
                "vault:0x1111111111111111111111111111111111111111"
              ]
            }
          }
        }
      },
      "RevalidateResponse": {
        "type": "object",
        "required": [
          "revalidated"
        ],
        "properties": {
          "revalidated": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}