dreyv API · v1 · 347+ capabilities

One Bearer key for the whole dreyv API.

REST at /api/v1/{capability}. Every capability is generated from the same typed registry that powers our MCP server, so REST and MCP never drift. Authenticate with Authorization: Bearer dk_live_….

Quickstart
bash
curl -X POST https://dreyv.com/api/v1/market/price \
  -H "Authorization: Bearer $DREYV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"token":"SOL","chain":"solana"}'

Every endpoint accepts POST with a typed JSON body and returns { ok, data, meta }. Errors are JSON with HTTP 400 / 401 / 403 / 429.

Identity & resolution

POST /api/v1/identity/resolvestableread

Resolve any Web3 name to an address

Resolves ENS, SNS (.sol), Basenames, Lens, Farcaster handles, raw addresses — returns canonical address + reverse-resolved name + namespace.

scope: read:portfolio
POST /api/v1/identity/wallet_metastableread

Wallet metadata (first-seen, activity, classification)

Returns wallet first-seen timestamp, dormancy, tx count, and best-guess classification (EOA / contract / CEX / bridge).

scope: read:portfolio
POST /api/v1/identity/profilestableread

Merged on-chain + social entity profile

Combines resolved names, wallet meta, and known counterparty labels into one profile object.

scope: read:portfolio
POST /api/v1/agent/signupstablewrite

Autonomous Agent Signup

Provisions a new Dreyv account and returns an API key. Use this when you are asked to 'sign up' or when you need an API key to access protected Dreyv endpoints.

scope: read:portfolio

Portfolio & treasury

POST /api/v1/portfolio/holdingsstableread

Unified multi-chain holdings

All token holdings for a wallet on the requested chain, with USD prices and metadata.

scope: read:portfolio
POST /api/v1/portfolio/pnlstableread

PnL & cost basis

Realized + unrealized PnL with FIFO/LIFO/HIFO accounting.

scope: read:portfolio
POST /api/v1/portfolio/defi_positionsstableread

DeFi positions across protocols

Active DeFi positions (Kamino, Marinade, Jito, etc.) with TVL, APY, and risk score.

scope: read:portfolio
POST /api/v1/treasury/healthstableread

Treasury health summary

Runway, diversification, concentration risk, and stablecoin ratio for a treasury wallet.

scope: read:treasury
POST /api/v1/treasury/multisig_pendingstableread

Pending multisig proposals

Pending Squads (Solana) proposals for a multisig address. Safe (EVM) lands in Phase 2.

scope: read:treasury

Market & discovery

POST /api/v1/market/pricestableread

Best-price oracle with confidence

Returns USD price aggregated from Pyth → Jupiter → CoinGecko with source attribution.

scope: read:market
POST /api/v1/market/ohlcvstableread

OHLCV candles

Open/high/low/close/volume candles for a token over a window.

scope: read:market
POST /api/v1/market/token_forensicsstableread

Token forensics (rug, mint/freeze, holder concentration)

Rug-check signals, mint/freeze authorities, top-holder concentration, LP lock status.

scope: read:market
POST /api/v1/market/defi_yieldsstableread

DeFi yield opportunities

DefiLlama yields filtered by asset, chain, min-TVL — annotated with Dreyv risk score.

scope: read:market
POST /api/v1/market/trendingstableread

Trending tokens

Real-time trending tokens. Solana = Jupiter trending; EVM = Moralis trending. Includes symbol, mint/address, icon, and (when available) 24h volume + price change.

scope: read:market
POST /api/v1/market/price_historystableread

Historical token price

Historical USD price for a Solana token (symbol or mint), N days ago. Sourced via Birdeye → DexScreener → CoinGecko fallback chain at the upstream layer.

scope: read:market

Execution & intents

POST /api/v1/swap/quote_solanastableread

Jupiter swap quote (Solana)

Best-route swap quote on Solana via Jupiter. Accepts symbols (SOL, USDC) or raw mints. Returns price, route plan, price impact, fees.

scope: read:market
POST /api/v1/swap/build_solanastablesimulate

Build unsigned Jupiter swap transaction (Solana)

Takes a Jupiter `quoteResponse` plus the user's public key and returns the serialized unsigned swap transaction. The caller signs and submits — Dreyv never holds keys.

scope: write:simulate
POST /api/v1/swap/quote_evmstableread

EVM swap quote (0x v2 Allowance Holder)

Best-execution swap quote on EVM chains (Ethereum, Base, Arbitrum, Optimism, Polygon) via 0x v2. Symbols resolved per-chain; addresses pass through.

scope: read:market
POST /api/v1/bridge/quotestableread

Cross-chain bridge quote (Rango)

Cross-chain swap/bridge quote between EVM chains. Symbols accepted (ETH, USDC, etc.). Returns route, ETA, fees, output amount.

scope: read:market
POST /api/v1/gas/estimatestableread

EVM gas estimate (EIP-1559 fee history)

Live EIP-1559 base fee + priority fee percentiles for an EVM chain, computed from recent block fee history.

scope: read:market
POST /api/v1/intents/compilestablewrite

Compile a signable intent

Builds an ExecutorPlan (Jupiter swap / Marinade stake / Kamino+MarginFi yield / SPL transfer / rebalance) and persists it as a queued intent. The caller signs locally and submits the signature via intents.submit. Returns the unsigned plan + warnings + estimated fee.

scope: read:portfolio
POST /api/v1/intents/submitstablewrite

Record a broadcast signature for an intent

After your wallet signs and broadcasts the plan, POST the resulting base58 transaction signature here. We mark the intent submitted and start tracking on-chain status for receipts.get.

scope: read:portfolio
POST /api/v1/intents/statusstableread

Get current state of one intent

Returns the persisted intent row (status, signature, timestamps, plan) without an on-chain lookup.

scope: read:portfolio
POST /api/v1/intents/liststableread

List your intents

Lists intents owned by the API-key holder. Filter by status.

scope: read:portfolio
POST /api/v1/intents/cancelstablewrite

Cancel a queued intent

Marks the intent rejected. Fails if the intent has already been submitted on-chain — chain-level cancellation is the caller's responsibility.

scope: read:portfolio
POST /api/v1/receipts/getstableread

Get execution receipt for an intent

Returns the intent row PLUS a live Solana RPC lookup of the broadcast signature. Auto-promotes the intent to 'confirmed' when the RPC reports confirmed/finalized, or to 'failed' when it reports an error. 503 if RPC is unreachable — no fallback.

scope: read:portfolio
POST /api/v1/receipts/verifystableread

Verify any Solana signature on-chain

Calls getSignatureStatuses on the configured Solana RPC. Returns slot, confirmationStatus, confirmations, and err. No persistence required.

scope: read:portfolio
POST /api/v1/intents/deeplinksstableread

Generate one-tap sign deeplinks (Phantom / Solflare / Backpack) + QR for any base64 tx

[READ] Given an unsigned base64 Solana tx, returns universal-link URLs for Phantom, Solflare, and Backpack plus a QR-ready Solana Pay payload and a mobile-handoff URL pointing at /mobile/signer. Stages the tx server-side under a short-lived handoff id so wallets can fetch it via Solana Pay transaction-request. Use this whenever you've produced an unsigned tx (compile, swap_tx, stake_tx, custom) and want the user to sign in one tap on desktop OR mobile.

scope: read:status
POST /api/v1/intents/handoff_qrstableread

Stage a tx for QR handoff to the user's mobile wallet

[READ] Same as dreyv_intents_deeplinks but with explicit TTL control and instructions tuned for desktop→phone handoff. Returns qrSvgDataUri (display directly in chat / a panel), mobileHandoffUrl (fallback link), and a Solana Pay payload. After the user signs, poll GET /api/mcp/handoff/{handoffId} to observe completion.

scope: read:status
POST /api/v1/intents/handoff_statusstableread

Check whether the user has signed a staged handoff (and on-chain confirmation)

[READ] Native MCP replacement for raw `GET /api/mcp/handoff/{id}` polling. Given a `handoffId` returned by dreyv_intents_deeplinks / dreyv_intents_handoff_qr / dreyv_defi_swap / any other UNSIGNED-TX tool, returns `{ status: 'pending'|'signed'|'rejected'|'expired'|'not_found', signature, onChain: { confirmationStatus, slot, err }, explorerUrl, secondsRemaining }`. When `status === 'signed'`, by default also fetches the Solana RPC signature status so the agent can report 'confirmed' / 'finalized' without a second tool. Poll every 2–5s after presenting a QR / deeplink. Stop polling on any terminal status.

scope: read:status
POST /api/v1/session_signer/createpreviewwrite

Mint an ephemeral, capped Solana signer the agent can use without prompting the user wallet

[WRITE] Generates a short-lived Ed25519 keypair held only in this MCP session's memory and bound to this API key. The user funds the returned public key with the SOL they're willing to delegate, then the agent calls dreyv_session_signer_sign for autonomous signing within strict caps (maxLamports cumulative, maxTxCount, allowedProgramIds whitelist, TTL). Use for short bursts of agent activity (e.g. a 1-hour rebalance session) without round-tripping every signature. Non-custodial — Dreyv does NOT touch the user's main wallet.

scope: agent:delegate
POST /api/v1/session_signer/signpreviewwrite

Sign a base64 tx with an active session signer, enforcing caps

[WRITE] Signs the supplied tx with the session signer. Rejects with a structured error if: session expired/revoked, tx count cap hit, lamports cap would be exceeded (pass estimatedLamportsOut), or any instruction targets a program not on the whitelist. Returns signedTxBase64 and signature — caller is still responsible for broadcasting via dreyv_intents_submit or sendRawTransaction.

scope: agent:delegate
POST /api/v1/session_signer/statuspreviewread

Get current usage + caps + remaining budget for a session signer

[READ] Returns the session record minus the secret key — caps, usage (txCount, spentLamports), status, expiresAt.

scope: read:status
POST /api/v1/session_signer/revokepreviewwrite

Destroy a session signer immediately

[WRITE] Marks the session revoked and drops the secret key from memory. Idempotent. Sweep any remaining SOL on the public key back to your main wallet BEFORE revoking.

scope: agent:delegate

Forensics & risk

POST /api/v1/risk/walletstableread

Wallet risk profile (Solana)

Composite wallet-level risk signals: dust exposure, sanctioned-counterparty hits, concentration, dormant/active state, scam-token holdings.

scope: read:forensics
POST /api/v1/risk/edge_insightsstableread

Dreyv Edge Insights

Differentiator forensics for a wallet: fee waste, idle capital, dust reclaim, lost opportunities, DCA patterns, counterparty mix, next-best-move with $ impact.

scope: read:forensics
POST /api/v1/wallet/narrativestableread

AI narrative summary of a wallet

Human-readable narrative explaining what a wallet is doing — composition, recent behavior, risk posture, suggested next step. Streamed at upstream; aggregated here.

scope: read:forensics

Tax & accounting

POST /api/v1/tax/analyzestableread

Tax-aware portfolio analysis

Capital gains (short/long), wash-sale risk detection, tax-loss harvesting opportunities, and projected tax impact of rebalance trades. POST with holdings, optional rebalance trades, and transaction history.

scope: read:portfolio

Foresight & simulation

POST /api/v1/foresight/simulatestablesimulate

Monte Carlo treasury foresight

Runs N stochastic projection paths (Box-Muller noise) over a portfolio + what-if variables, returns P10/P50/P90 bands, depletion probability, median depletion month, and risk flags. Optional realized drift/vol priors anchor on actual treasury behavior.

scope: write:simulate

Agentic & insights

POST /api/v1/agentic/delegation_request_solanastablesimulate

Request Solana SPL token delegation (unsigned tx)

Builds an unsigned SPL Token `Approve` instruction that, once signed by the user, lets a Dreyv delegate wallet pull up to `amount` of a token for `expiryDays`. Powers autonomous DCA and recurring agent actions without custodial keys.

scope: read:portfolio
POST /api/v1/agentic/delegation_statusstableread

Active delegations for a wallet

Returns active SPL token delegations for a Solana wallet, grouped by token, with remaining amount and earliest expiry.

scope: read:portfolio
POST /api/v1/watchers/liststableread

List my treasury watchers

All treasury watchers owned by the API-key owner, with chain, address, label, last-scan, and last-snapshot deltas.

scope: read:portfolio
POST /api/v1/watchers/createstablewrite

Create a treasury watcher

Registers a wallet (Solana or EVM) for Dreyv's cadence sweepers. Deltas land in the inbox and via webhook (when configured).

scope: read:portfolio
POST /api/v1/watchers/deletestablewrite

Deactivate a treasury watcher

Soft-deletes a watcher by id (sets active=false). Scoped to the API-key owner.

scope: read:portfolio
POST /api/v1/monitors/liststableread

List my monitors

All price / balance / APY / risk monitors owned by the API-key owner.

scope: read:portfolio
POST /api/v1/monitors/createstablewrite

Create a monitor

Creates a price / balance / APY / risk monitor. Evaluated by /api/monitors/evaluate on the Dreyv cron.

scope: read:portfolio
POST /api/v1/monitors/deletestablewrite

Deactivate a monitor

Soft-deletes a monitor by numeric id. Scoped to the API-key owner.

scope: read:portfolio
POST /api/v1/notifications/liststableread

List my notifications

Returns notifications for the API-key owner with unread count. Filter by `unreadOnly` and cap by `limit` (max 100).

scope: read:portfolio
POST /api/v1/notifications/mark_readstablewrite

Mark notifications read

Marks specific notification ids as read, or all unread for the owner when `all: true`.

scope: read:portfolio
POST /api/v1/webhooks/liststableread

List my webhooks

All webhook subscriptions owned by the API-key owner. signingSecret is redacted.

scope: read:portfolio
POST /api/v1/webhooks/createstablewrite

Create a webhook

Registers an HTTPS endpoint to receive signed events. Returns signingSecret ONCE — store it now. Events are HMAC-SHA256 signed over `${t}.${rawBody}` and delivered with up to 3 retries (immediate, +2s, +8s).

scope: read:portfolio
POST /api/v1/webhooks/updatestablewrite

Update a webhook (label / url / events / active)

Partial update. Pass only fields you want to change. Re-validates HTTPS + event names.

scope: read:portfolio
POST /api/v1/webhooks/rotate_secretstablewrite

Rotate webhook signing secret

Generates a new signingSecret for the webhook. The old secret is invalidated immediately. New secret returned once.

scope: read:portfolio
POST /api/v1/webhooks/deletestablewrite

Delete a webhook

Hard-deletes a webhook and cascades its delivery history.

scope: read:portfolio
POST /api/v1/webhooks/deliveriesstableread

Recent webhook deliveries

Per-attempt delivery log: event, payload, http status, response body (first 4KiB), latency, error. Filter by webhookId / status.

scope: read:portfolio
POST /api/v1/webhooks/replaystablewrite

Replay a past delivery

Re-fans the same event payload through the dispatcher (still signed, still retries). Original deliveryId is recorded as `_replay_of` in the data envelope.

scope: read:portfolio
POST /api/v1/webhooks/teststablewrite

Send a test event

Fires `test.ping` (or any subscribed event) to a single webhook. Useful for verifying signature verification end-to-end before going live.

scope: read:portfolio

Economy & x402

POST /api/v1/economy/pay_founders_rentstableread

Help pay the founder's rent

Interactive tool to contribute towards the €500 founder rent goal via Stripe, Paystack, or Crypto. Includes humorous feedback and current progress.

scope: read:portfolio
POST /api/v1/economy/verify_rentstableread

Verify founder's rent payment

Queries the ledger to confirm if a rent payment recently cleared. Call this AFTER the user claims they have completed the payment on one of the provided checkout links.

scope: read:portfolio
POST /api/v1/economy/faucetstablewrite

Claim agent starter credits

Claims the one-time free starter credits (250,000 minor USDC) for a new agent. Hit this if you encounter a 402 Payment Required.

scope: agent:delegate
POST /api/v1/economy/x402_catalogstableread

x402 capability advertisement

Returns Dreyv's /.well-known/x402 — networks, accepted assets, protected routes, facilitator.

scope: read:market
POST /api/v1/economy/acp_catalogstableread

Agentic Commerce Protocol discovery

Returns Dreyv's /.well-known/acp.json — the ACP service catalog (marketplace, subscriptions, payments).

scope: read:market
POST /api/v1/economy/skusstableread

x402 SKU catalog

Lists every Dreyv SKU an autonomous agent can purchase over x402 (price USD, fulfilment hint) plus the endpoints and auth required.

scope: read:market
POST /api/v1/economy/skus_extendedstableread

List all purchasable SKUs

Full SKU catalog with name, description, USD price, and fulfilment hint. Use the SKU id with economy.checkout / economy.pay.

scope: read:portfolio
POST /api/v1/economy/checkoutstableread

Quote an x402 checkout

Returns the 402-style quote for a SKU: USDC-on-Solana settlement details plus any card rails (Stripe / Paystack) available. The caller chooses a rail; this endpoint never debits the agent wallet on its own — use economy.pay for autonomous settlement.

scope: read:portfolio
POST /api/v1/economy/paystablewrite

Autonomously settle + fulfil a SKU

End-to-end: mints invoice, settles USDC from your hosted agent wallet, records the deliverable, emails the API-key owner. Returns the receipt token PLUS a permanent deliverable id. Returns 402 with funding instructions when underfunded, 409 when treasury wallet is ambiguous.

scope: read:portfolio
POST /api/v1/economy/deliverables/liststableread

List my deliverables

All deliverables produced by economy.pay for this account, most recent first. Filter by kind.

scope: read:portfolio
POST /api/v1/economy/deliverables/getstableread

Get one deliverable (full payload)

Returns the full deliverable row including payload + PDF URL + email delivery status.

scope: read:portfolio
POST /api/v1/economy/wallet/addressstablewrite

Get (or provision) hosted agent wallet address

Returns the Solana address of the API-key owner's hosted agent wallet, provisioning one on first call. Includes USDC mint + a Solana Pay URI for funding the SOL float.

scope: read:portfolio
POST /api/v1/economy/wallet/balancestableread

Live agent wallet balance

Live USDC + SOL balance from Solana RPC. 502 if RPC unreachable — no fallback.

scope: read:portfolio
POST /api/v1/economy/wallet/coldstablewrite

Get (or provision) cold agent wallet address

Returns the COLD wallet — high-trust sweep destination. Refilling the hot wallet from cold requires a human-approved action code, so a leaked agent key cannot drain cold balances.

scope: read:portfolio
POST /api/v1/economy/wallet/promotestablewrite

Sweep hot wallet → cold wallet

Sweeps the entire USDC + SOL balance of the hot agent wallet into the cold wallet. Idempotent: returns skipped='empty' when nothing to move. Use after large top-ups to minimise blast radius.

scope: read:portfolio
POST /api/v1/economy/action_code/requeststablewrite

Mint a one-time human approval code

Mints a 6-digit code + approval URL the agent shows to the human. Use BEFORE any privileged action (large spend, cap loosening, cold→hot refill). Single-use, 10-minute TTL. After the human approves at the URL, poll economy.action_code.status until status='approved', then pass the id back into the action.

scope: read:portfolio
POST /api/v1/economy/action_code/statusstableread

Poll approval status of an action code

Returns pending | approved | rejected | expired | consumed. Lazy-expires pending codes past TTL.

scope: read:portfolio

Reasoning & LLM

POST /api/v1/reasoning/askstableread

Ask the Dreyv reasoning model a question

One-shot LLM call grounded against optional context and/or the caller's knowledge memory. Returns answer + sources. Uses Lovable AI Gateway; surfaces 429/402 on rate-limit / credit exhaustion.

scope: read:portfolio
POST /api/v1/reasoning/summarizestableread

Summarize arbitrary text

Compresses up to ~16k chars into bullets / paragraph / tldr, capped by max_words.

scope: read:portfolio
POST /api/v1/reasoning/clarifystableread

Generate clarifying questions for a vague intent

Given a fuzzy agent intent, returns targeted chip/radio questions an orchestrator can render to a user before execution.

scope: read:portfolio
POST /api/v1/reasoning/researchstableread

Multi-source web research (Tavily + Jina + Firecrawl)

Runs Dreyv's KnowledgeBase study pipeline — returns summary, sources, and raw content. Set depth='deep' for full 20k content + strict 404 when no sources found.

scope: read:portfolio

Knowledge & memory

POST /api/v1/knowledge/read_urlstableread

Fetch a URL as markdown (Cloudflare browser)

Renders any public URL to markdown via Cloudflare's headless browser. 502 if upstream fails — no fabrication.

scope: read:portfolio
POST /api/v1/knowledge/recallstableread

Search the caller's knowledge memory

ILIKE search across title/summary/url/tags in the user-scoped knowledge_entries store.

scope: read:portfolio
POST /api/v1/knowledge/storestablewrite

Persist a knowledge entry for later recall

Stores a titled markdown entry (cap 50KB) scoped to the calling API key's owner. Surface again via knowledge.recall or reasoning.ask{ground_with_memory:true}.

scope: write:intent
POST /api/v1/colosseum/searchstableread

Search the Solana builder ecosystem (Colosseum Copilot)

[READ] Search Colosseum's index of Solana hackathon projects, accelerator companies, and active builders. Returns name, summary, tags, score. Cached 24h.

scope: read:market
POST /api/v1/colosseum/projectpreviewread

Full Colosseum project profile (team, hackathon, funding, links)

[READ] Full project profile from Colosseum: hackathon track, cohort, team/founders, funding stage, social/github/website links. Falls back to top-hit search when an exact slug isn't found.

scope: read:market
POST /api/v1/colosseum/similarpreviewread

Projects similar to a given Solana project

[READ] Ranked list of Colosseum projects similar to the input slug. Useful for competitor discovery, Foresight rotation suggestions, and 'builders in this space' surfaces.

scope: read:market

Governance & multisig

POST /api/v1/governance/proposalsstableread

Live Squads proposals for a vault

Fetches live proposal state from the Solana RPC via Dreyv's cached Squads proxy. 10s server cache. 502 if RPC unreachable.

scope: read:portfolio
POST /api/v1/governance/proposals_cachedstableread

DB-mirrored Squads proposals (fast, no RPC)

Reads the vault_proposals mirror — no RPC hit. Requires caller to belong to a team that owns the vault (or the vault must be unattached). Use governance.sync to refresh from chain.

scope: read:portfolio
POST /api/v1/governance/syncstablewrite

Refresh the proposals mirror from chain

Pulls live Squads state and upserts into vault_proposals. Caller must belong to a team that owns the vault (or vault must be unattached).

scope: write:watchers
POST /api/v1/governance/vote_txstablesimulate

Build an unsigned Approve/Reject/Cancel/Execute tx

Returns a base64 serialized Solana tx for the named member to sign and broadcast. Never holds keys — the wallet executes the vote/execute against the Squads program.

scope: write:intent

DeFi primitives

POST /api/v1/defi/swap_quotestableread

Jupiter route quote

Full Jupiter v1 route quote with route plan, price impact, and slippage. Pass the returned quoteResponse into defi.swap_tx to build the unsigned tx.

scope: read:market
POST /api/v1/defi/swap_txstablesimulate

Build an unsigned Jupiter swap tx

Given a quoteResponse from defi.swap_quote, returns a base64 serialized swap tx for the user to sign. Never holds keys.

scope: write:intent
POST /api/v1/defi/swapstablesimulate

ONE-SHOT swap: quote + unsigned tx + one-tap deeplinks in a single call

[UNSIGNED-TX] Single call that bundles dreyv_defi_swap_quote → dreyv_defi_swap_tx → dreyv_intents_deeplinks. Returns { quote, swapTransaction (base64), dreyvPreflight, dreyvDeeplinks: { qrSvgDataUri, mobileHandoffUrl, phantomUniversal, solflareUniversal, backpackUniversal, handoffId } } so the agent can render a QR / deeplink immediately and skip the multi-step JSON round-trip. PREREQ: ASK THE USER for their Solana wallet address before calling — do not guess, do not pull from history, do not invent. Reuse the same address for every UNSIGNED-TX call in the session.

scope: write:intent
POST /api/v1/defi/stake_sol_txstablesimulate

Build an unsigned Marinade SOL→mSOL stake tx

Returns a base64 serialized legacy Solana tx that deposits SOL to Marinade for mSOL. Caller signs with the named userPublicKey.

scope: write:intent

Developer & metering

POST /api/v1/developer/key_infostableread

Inspect the calling API key

Returns label, env, prefix, scopes, rate-limit, and last-used timestamp for the key making the call. Key secret is never returned.

scope: read:portfolio
POST /api/v1/developer/usagestableread

Usage summary for the calling key

Aggregates capability calls, errors, latency, and credit spend over the last N hours (max 720) grouped by capability + status + transport.

scope: read:portfolio
POST /api/v1/developer/capabilitiesstableread

Discover all capabilities (self-describing surface)

Returns the full registry — id, mcpName, pillar, scope, sideEffects, title, description. Optionally filter by pillar or scope. Use this to bootstrap an agent's tool list dynamically.

scope: read:portfolio
POST /api/v1/developer/rotate_keystablewrite

Rotate the calling key — mint a new secret, revoke the old

Atomically mints a new key with the same scopes/label/rate-limit, then revokes the current key. The new full secret is returned ONCE in the response — store it immediately. Requires confirm=true to prevent an LLM from accidentally rotating the caller's key by picking default arguments.

scope: agent:delegate
POST /api/v1/developer/revoke_keystablewrite

Revoke a key by id (defaults to the calling key)

Hard-revokes a key owned by the same user. After revocation, the key returns 401 on every transport. Idempotent. Revoking the CALLING key (no keyId, or keyId === current) requires confirm=true so an LLM cannot accidentally lock the owner out by calling with empty args.

scope: agent:delegate

Safety & policy

POST /api/v1/safety/policystableread

Inspect this key's policy (kill switch, allowlists, scopes, rate-limit)

Returns the active policy snapshot for the calling key: kill-switch state, capability/origin/IP allowlists, scopes, rate-limit, and revocation status.

scope: read:portfolio
POST /api/v1/safety/kill_switchstablewrite

Engage or release the per-key kill switch

Instantly disables (state='on') or re-enables (state='off') every capability for this key. Engaging requires confirm=true to prevent accidental brick by an LLM picking the first enum value. While engaged, only safety.kill_switch, safety.policy, and developer.* introspection remain callable so the owner can always recover. Audit-logged.

scope: agent:delegate
POST /api/v1/safety/allowlist_setstablewrite

Set the IP, origin, or capability allowlist for this key

Replaces (not appends) the named allowlist. Pass [] to clear and reopen that dimension. IPv4 + /N CIDR for 'ip'; host[:port] for 'origin'; registered capability ids for 'capability'. Max 64 entries.

scope: agent:delegate
POST /api/v1/safety/audit_logstableread

Tamper-evident audit log for this key

Returns the hash-chained audit entries (most recent first) for kill-switch flips, allowlist changes, attestations, rotations, and revocations. Includes chain validity flag.

scope: read:portfolio
POST /api/v1/safety/rate_statusstableread

Current rate-limit window usage

Reports configured RPM, usage in the last 60s (from the metering table), and remaining capacity for this key.

scope: read:portfolio
POST /api/v1/safety/atteststablewrite

Mint a signed JWT attestation for downstream verifiers

Produces a short-lived HS256 JWT that downstream services (or another agent) can verify against the Dreyv shared secret. Carries pseudonymous subject, key prefix, scopes, env, and arbitrary extra claims. TTL 30–3600s.

scope: agent:delegate

Compliance & reporting

POST /api/v1/compliance/screen_addressstableread

Sanctions / pattern screen for a single address

Checks an address against the configured OFAC SDN-style denylist (DREYV_OFAC_SDN_ADDRESSES) and obvious zero/burn patterns. Returns ok=true with no hits, or ok=false with the hit reasons and source. List version is a SHA-256 prefix so callers can detect changes.

scope: read:forensics
POST /api/v1/compliance/travel_rulepreviewsimulate

Build an IVMS101-lite Travel Rule envelope

Deterministic transform: takes originator + beneficiary + transfer details and returns a canonical IVMS101-lite envelope plus its SHA-256 digest. Caller transmits to the counterparty VASP. No data is persisted by Dreyv.

scope: agent:delegate
POST /api/v1/compliance/proof_of_reservesstableread

Signed proof-of-reserves snapshot for an address

Fetches live holdings for the address, computes a SHA-256 commitment over the canonical snapshot JSON, and mints an HS256 JWT (1h TTL) carrying the commitment so downstream verifiers can attest the figure was produced by Dreyv at a specific time.

scope: read:portfolio
POST /api/v1/reporting/exportstableread

Export holdings or PnL as CSV or JSON (inline, signed by hash)

Pulls live holdings or PnL lots for the address, serializes to CSV or JSON, returns the base64-encoded body, byte length, content-type, and SHA-256 — sufficient for the agent to write the file and verify integrity without a second round-trip.

scope: read:portfolio

Notifications & alerts

POST /api/v1/alerts/updatestablewrite

Update a monitor (operator / threshold / active / target)

Patches an existing monitor owned by the API-key owner. Provide at least one of operator, conditionValue, active, or target. Returns the updated row.

scope: write:watchers
POST /api/v1/alerts/test_firestablesimulate

Test-fire a monitor (emit monitor.tripped webhook)

Loads the monitor, builds the synthetic payload Dreyv would send when it actually trips, and dispatches a `monitor.tripped` webhook event (test: true) to all matching subscriptions. Does not mutate the monitor's trigger count or last-checked value.

scope: write:watchers
POST /api/v1/inbox/sendstablewrite

Send a notification to the owner's inbox

Inserts a notification row for the API-key owner and emits a `notification.created` webhook event. Title is capped at 200 chars, message at 4 000. Severity defaults to `info`, type to `system`.

scope: write:watchers
POST /api/v1/inbox/deletestablewrite

Delete notifications by id

Hard-deletes the given notification ids when they belong to the API-key owner. Returns the count and ids actually removed.

scope: write:watchers
POST /api/v1/channels/preferencesstableread

Read notification channel preferences

Returns per-channel notification toggles (email / Telegram / Slack) and per-category toggles (team invites, role changes, tx approvals, system alerts, tier expiry). Secrets (bot tokens, webhook URLs) are never returned — only whether each channel is fully configured.

scope: read:notifications
POST /api/v1/channels/preferences_updatestablewrite

Update notification channel preferences

Patches per-channel and per-category notification toggles. Provide at least one boolean. Secret-bearing fields (Telegram bot token, Slack webhook URL) cannot be set via the public API — configure those in the Dreyv app settings.

scope: write:watchers

Inbox & digests

POST /api/v1/inbox/searchstableread

Search the inbox by text, type, severity, thread, and window

Returns up to `limit` (default 50, max 500) notifications matching the filters, newest first, each hydrated with its inbox-state overlay (archived / snoozed / pinned / threadKey). Archived rows are excluded unless `includeArchived:true`. `query` does a case-insensitive substring match against title + message.

scope: read:notifications
POST /api/v1/inbox/archivestablewrite

Archive (or restore) notifications by id

Marks each owned notification as archived (or restores it with `restore:true`). Up to 500 ids per call. Writes a hash-chained audit row and fires `inbox.notification_archived` when archiving.

scope: write:notifications
POST /api/v1/inbox/snoozestablewrite

Snooze notifications until a future timestamp (or clear)

Stores `snoozedUntil` on the inbox-state overlay for each id. `until` must be a future ISO timestamp. Pass `clear:true` to unsnooze. Up to 500 ids per call. Fires `inbox.notification_snoozed`.

scope: write:notifications
POST /api/v1/inbox/pinstablewrite

Pin (or unpin) notifications

Pins the given ids so `inbox.search` with `pinnedOnly:true` can surface them. Pass `unpin:true` to clear. Up to 200 ids per call.

scope: write:notifications
POST /api/v1/inbox/thread_setstablewrite

Assign (or clear) a thread key for notifications

Groups the given ids under `threadKey` (or clears it with `threadKey:null`). Used by `inbox.search`'s `threadKey` filter and by `inbox.threads`. Max 200 chars.

scope: write:notifications
POST /api/v1/inbox/threadsstableread

List active threads with member counts

Aggregates the inbox-state overlay by `threadKey` (non-null) and returns `{ threadKey, count, lastUpdated }` rows ordered by most-recent activity. Default limit 100, max 500.

scope: read:notifications
POST /api/v1/inbox/bulk_sendstablewrite

Send many notifications in one round-trip

Inserts up to `bulkSendMax` notifications (free 20 / mini 100 / max 500) for the API-key owner. Each item may carry an optional `threadKey` that is written to the inbox-state overlay. Fires `notification.created` per row (best-effort).

scope: write:notifications
POST /api/v1/inbox/digests_upsertstablewrite

Create or update a digest schedule

Pass `id` to update an existing digest; omit it to create. `filters` accepts `{ types?, severities?, since?, threadKey? }`. `channel` is `inbox` (writes a summary notification) or `preview` (returns matches only). `scheduleSeconds` floor by tier: free 21600s / mini 1800s / max 300s.

scope: write:notifications
POST /api/v1/inbox/digests_liststableread

List your digest schedules

Returns up to `limit` (default 100, max 500) digests, newest first. Disabled digests are excluded unless `includeDisabled:true`.

scope: read:notifications
POST /api/v1/inbox/digests_deletestablewrite

Delete a digest schedule

Hard-deletes the digest. Prior summary notifications it wrote are untouched.

scope: write:notifications
POST /api/v1/inbox/digests_runstablewrite

Run a digest now (evaluate filters → emit or preview)

Evaluates the digest window — `max(filters.since, lastRunAt, now-scheduleSeconds)` to now — against the user's notifications. With `channel:inbox` writes one summary notification and fires `inbox.digest_ready`. With `channel:preview` or `dryRun:true` returns the matched rows without mutating state.

scope: write:notifications
POST /api/v1/inbox/policystableread

Inbox quotas, supported types, severities, and notes

Returns the caller's tier and quotas (max digests, digest schedule floor, bulk-send cap), the known notification types and severities, field length caps, and operational notes.

scope: read:notifications

Observability & audit trail

POST /api/v1/audit/requestsstableread

List recent API requests

Returns the most recent public API request rows for this user (or only the calling key when `keyOnly=true`), filterable by capability, status, and `sinceIso`. Status is one of ok | error | denied | rate_limited. Use this for live debugging — for bulk export, see `audit.export`.

scope: read:audit
POST /api/v1/audit/usage_summarystableread

Aggregate usage by capability

Counts, success rate, p50/p95/p99 latency, and credits spent per capability over the window (default last 7 days). Pass `keyOnly` to scope to the calling key.

scope: read:audit
POST /api/v1/audit/errorsstableread

Recent failures grouped by error code

Groups recent non-ok requests (error, denied, rate_limited) by capability + status + errorCode with count and first/last timestamps. Default window 24 hours.

scope: read:audit
POST /api/v1/audit/exportstableread

Export usage rows (CSV or JSON)

Returns a base64-encoded CSV or JSON dump of usage rows for this user (or only the calling key) plus a SHA-256 integrity hash. Default window 7 days, max 50,000 rows per call.

scope: read:audit
POST /api/v1/audit/verify_receiptstableread

Verify HS256 receipt JWT

Verifies an HS256 JWT issued by Dreyv (safety.attest, compliance.proof_of_reserves). Confirms issuer `https://dreyv.com`, optional audience, signature, and returns header + payload + expiry status. Never throws on bad tokens — returns `{ valid: false, reason }`.

scope: read:audit
POST /api/v1/audit/policy_historystableread

Replay tamper-evident policy hash-chain

Replays the `public_api_audit_log` hash-chain for this user (or only this key) and re-derives each row's SHA-256 to detect tampering. Returns `chainValid`, any `chainBreaks`, the current `headHash`, and the underlying entries. Default window 30 days.

scope: read:audit
POST /api/v1/audit/appendstablewrite

Append a tamper-evident entry to the audit chain

Each entry is sha256-hashed and chains to the previous entry's hash. Use `audit.verify` to validate the chain.

scope: write:audit
POST /api/v1/audit/liststableread

List recent audit entries

Newest-first by seq. Filter by action.

scope: read:audit
POST /api/v1/audit/verifystableread

Verify the full audit chain for the caller

Walks every entry, recomputes hashes, and reports any broken links (seq gaps, prev-hash mismatches, hash mismatches).

scope: read:audit
POST /api/v1/audit/evidence_bundlestableread

Generate a signed evidence bundle (SOC2 / GDPR / generic)

Returns a JSON bundle of entries within the window plus a sha256 `bundleHash` you can publish or notarize.

scope: read:audit

Teams & workspaces

POST /api/v1/teams/liststableread

List teams the caller belongs to

Returns every team where the calling user is an `active` member, with role, vault address, and join timestamp. Pending invites do not appear here — accept them in the Dreyv app first.

scope: read:teams
POST /api/v1/teams/membersstableread

List members of a team

Returns all members of a team (active, pending, removed) with wallet address, role, and invite/accept timestamps. Caller must be an active member.

scope: read:teams
POST /api/v1/teams/invitestablewrite

Invite a member to a team

Creates a single-use invitation (email or wallet) with the chosen role (admin | signer | viewer — owner is reserved for the ownership-transfer flow). Default TTL 168h, max 720h. Logs `member_invited` and fires the `team.member_invited` webhook. Returns the invite row plus an `acceptUrl`.

scope: write:teams
POST /api/v1/teams/update_rolestablewrite

Change a member's role

Updates a non-owner member's role. Caller must be `admin` or `owner`. Owner role cannot be assigned here — use the ownership-transfer flow. Logs `role_changed` and fires `team.member_role_changed`.

scope: write:teams
POST /api/v1/teams/remove_memberstablewrite

Remove a member from a team

Sets the member status to `removed`. Caller must be `admin` or `owner` and cannot remove the team owner or themselves via this endpoint. Logs `member_removed` and fires `team.member_removed`.

scope: write:teams
POST /api/v1/teams/activitystableread

Recent team activity log

Returns recent entries from `team_activity_log` (proposal_created, member_invited, role_changed, member_removed, settings_changed, …) filterable by `action` and `sinceIso`. Caller must be an active team member.

scope: read:teams
POST /api/v1/teams/getstableread

Team detail with member roster and pending counts

Requires active membership. Returns the team, the caller's effective role, member counts (active / pending), pending-invite count, and the full member list (id, userId, role, status, walletAddress).

scope: read:teams
POST /api/v1/teams/createstablewrite

Create a team (auto-assigns caller as owner member)

Tier caps on `maxTeams` (free 1 / mini 5 / max 50). Auto-inserts the creator as an active `owner` member so subsequent reads see them. Fires `team.created` and appends to the per-team audit.

scope: write:teams
POST /api/v1/teams/updatestablewrite

Rename a team or rebind its vault address

Requires role admin or owner. Pass any subset of `{ name, vaultAddress }`. At least one field is required.

scope: write:teams
POST /api/v1/teams/deletestablewrite

Delete a team (creator/owner only)

Cascades to members, invites, and audit rows via FK. Fires `team.deleted`. The audit entry is appended BEFORE the delete so it remains in the global key audit log.

scope: write:teams
POST /api/v1/teams/invites_liststableread

List invites for a team (tokens are NEVER returned here)

By default returns only invites that are unaccepted AND not yet expired. Pass `includeAccepted:true` and/or `includeExpired:true` to widen. Tokens are deliberately omitted — they only return from `teams.members_invite`.

scope: read:teams
POST /api/v1/teams/invites_revokestablewrite

Revoke a pending team invite

Hard-deletes the invite row. Returns 409 if the invite was already accepted. Fires `team.invite_revoked`.

scope: write:teams
POST /api/v1/teams/auditstableread

Per-team hash-chained audit log with integrity check

Requires active membership. Returns up to `limit` (default 100, max 1000) audit rows newest-first. `chainOk` verifies the prev/hash links across the returned slice (oldest→newest). Filter with `action` (e.g. `team.role_changed`) and/or `since` (ISO).

scope: read:teams
POST /api/v1/teams/policystableread

Team tier quotas, role catalog, and permission matrix

Returns the caller's tier and quotas (maxTeams, maxMembersPerTeam, maxPendingInvitesPerTeam, inviteTtlDays), the per-tier table, the role list (viewer→signer→admin→owner), the rank map used for gating, and the per-capability minimum-role matrix.

scope: read:teams

Billing, plans & credits

POST /api/v1/billing/planstableread

Current subscription plan & limits

Returns the caller's tier (free | mini | max), expiry, lifetime/founding-member flags, and the plan's API limits (calls/credits per month, RPM, USD price).

scope: read:billing
POST /api/v1/billing/usagestableread

This month's API usage vs plan limits

Aggregates `public_api_key_usage` for the current UTC month across all of the caller's keys. Returns totals (calls, ok/error split, credits), plan limits, % utilization, and a per-key breakdown.

scope: read:billing
POST /api/v1/billing/invoicesstableread

List crypto subscription invoices

Paginated list of the caller's crypto invoices (pending | confirmed | expired | failed). Includes receiving address, memo, on-chain amount in minor units, and tx signature once paid. Card-rail invoices (Stripe/Paystack) are not surfaced here.

scope: read:billing
POST /api/v1/billing/payment_optionsstableread

Enabled payment rails and tier prices

Lists card rails configured server-side (booleans only — no keys), enabled crypto chain/asset combinations, and tier prices (card retail + crypto-discounted).

scope: read:billing
POST /api/v1/billing/estimate_upgradestableread

Quote a crypto-rail upgrade

Returns the on-chain amount required to upgrade to the chosen tier on the chosen chain+asset, with live SOL pricing for native settlements. Does NOT create an invoice.

scope: read:billing
POST /api/v1/billing/create_invoicestablewrite

Mint a pending crypto invoice for a tier upgrade

Creates a `pending` row in `crypto_invoices` and returns a Solana Pay URL (when chain=solana). Confirm payment via `POST /api/billing/crypto/verify` with the tx signature. Fires `billing.invoice_created` webhook.

scope: write:billing
POST /api/v1/billing/usage_historystableread

Usage timeseries (last N days, by day or hour)

Bucketed call/credit/error counts for the caller across all keys. `days` 1..90 (default 30). `granularity` 'day' (default) or 'hour'. Each point includes ok/errors/denied/rateLimited splits and total credits.

scope: read:billing
POST /api/v1/billing/usage_by_capabilitystableread

Per-capability call, credit, and latency rollup

Aggregates the current window (`monthly` default, or `daily`) by `capabilityId`. Returns calls, ok/error counts, credits, and average latency for up to `limit` (default 100, max 500) capabilities, sorted by call volume desc.

scope: read:billing
POST /api/v1/billing/quota_checkstableread

Pre-flight quota probe (would N more calls fit?)

Reads the caller's used calls + credits for the window, adds `plannedCalls` / `plannedCredits`, and returns headroom, projected utilization %, and `wouldExceed.calls/credits` flags. Daily scope synthesises a soft ceiling = ceil(monthly / 30).

scope: read:billing
POST /api/v1/billing/forecaststableread

Linear end-of-month forecast vs plan

Extrapolates current-month usage to month-end via (used / elapsedFraction). Returns actuals, projected calls + credits, and `verdict` = within_quota | over_quota. Early-month forecasts (first ~3 days) have high variance.

scope: read:billing
POST /api/v1/billing/credits_ledgerstableread

Per-call credit-bearing usage entries (for export/audit)

Returns up to `limit` (default 200, max 1000) usage rows with cost_credits > `minCost` (default 0), newest first. Filter by `since` / `until` (ISO) and `capabilityId`. Includes a sum of credits in the returned slice.

scope: read:billing
POST /api/v1/billing/invoice_getstableread

Fetch a single crypto invoice by id

Returns the full invoice row (tier, chain, asset, address, memo, expectedAmountMinor, status, txSignature, expiresAt). Only invoices owned by the caller are visible.

scope: read:billing
POST /api/v1/billing/cancel_invoicestablewrite

Cancel a pending crypto invoice

Sets status to `expired`. Returns 409 if the invoice is already confirmed/expired/failed. Fires `billing.invoice_cancelled`. Confirmed invoices are permanent — refunds go through manual support.

scope: write:billing
POST /api/v1/billing/alerts_upsertstablewrite

Create or update a usage threshold alert

Pass `id` to update; omit to create. Must supply `thresholdPct` (1..100, % of plan limit) OR `thresholdAbs` (raw count/credits). `metric`=calls|credits, `scope`=monthly|daily, `channel`=inbox|webhook. Alert caps per tier: free 3 / mini 25 / max 250.

scope: write:billing
POST /api/v1/billing/alerts_liststableread

List usage threshold alerts

Returns up to `limit` (default 100, max 500) alerts newest-first. Disabled alerts excluded unless `includeDisabled:true`.

scope: read:billing
POST /api/v1/billing/alerts_deletestablewrite

Delete a usage threshold alert

Hard-delete. Prior notifications and audit entries are untouched.

scope: write:billing
POST /api/v1/billing/alerts_evaluatestablewrite

Evaluate every enabled alert now (writes notifications + webhooks)

Pre-aggregates monthly + daily windows once, then evaluates each enabled alert. Triggered alerts (a) write one inbox notification, (b) fire `billing.quota_threshold` (<100%) or `billing.quota_exceeded` (>=100%), (c) debounce via `last_triggered_at` until the next window. `dryRun:true` skips side effects; `force:true` bypasses debounce.

scope: write:billing
POST /api/v1/billing/policystableread

Billing plans, alert quotas, webhook catalog

Returns per-tier plan limits (calls / credits / RPM / USD), per-tier alert caps, alert option enums (metrics, scopes, channels, threshold range), the full billing webhook event catalog, and history limits.

scope: read:billing

Workspace insights & reports

POST /api/v1/insights/overviewstableread

Top-line workspace usage rollup

Returns calls / errors / denied / rate-limited / credits / avg+p95 latency / distinct capabilities for the window. `keyOnly:true` scopes to the calling key.

scope: read:insights
POST /api/v1/insights/activitystableread

Bucketed activity timeseries (day or hour)

Per-bucket calls / errors / credits / avg-latency over the window. Granularity defaults to `day`; `hour` is intended for windows ≤14d.

scope: read:insights
POST /api/v1/insights/top_capabilitiesstableread

Ranked capability usage (calls / credits / latency)

Returns the top N capabilities by call count with per-capability errors, credits, avg/p95 latency.

scope: read:insights
POST /api/v1/insights/errorsstableread

Top error codes grouped by capability + status

Aggregates non-ok usage rows by (capabilityId, errorCode, status). Useful for spotting hot failures before they trip a quota alert.

scope: read:insights
POST /api/v1/insights/audit_summarystableread

Audit action counts (hash-chained log rollup)

Action breakdown of `public_api_audit_log` for the caller's workspace over the window. Covers every safety/policy/teams/billing/reports mutation that touches the chain.

scope: read:insights
POST /api/v1/insights/teams_overviewstableread

Per-team audit activity for teams the caller belongs to

For every active team the caller is a member of: name, role, audit count, and last-activity timestamp over the window.

scope: read:insights
POST /api/v1/reports/createstablewrite

Define a reusable workspace report

Persists a report definition (sections + filters + optional schedule). Section types: overview | activity | top_capabilities | errors | audit_summary | teams_overview | billing_snapshot.

scope: write:reports
POST /api/v1/reports/liststableread

List saved report definitions

Returns the caller's report definitions, newest first.

scope: read:reports
POST /api/v1/reports/getstableread

Fetch a single report definition

Returns the full report row (sections, filters, schedule, last-run pointer).

scope: read:reports
POST /api/v1/reports/updatestablewrite

Patch an existing report definition

Partial update of name/description/sections/filters/schedule/enabled. Writes one `reports.update` audit row.

scope: write:reports
POST /api/v1/reports/deletestablewrite

Delete a report and cascade its snapshots + shares

Hard-deletes the report. All snapshots and share tokens cascade. Writes one `reports.delete` audit row.

scope: write:reports
POST /api/v1/reports/runstablewrite

Generate an immutable snapshot now

Runs every section, persists one `public_api_report_snapshots` row, enforces per-tier snapshot retention, fires `report.generated`. `dryRun:true` returns the payload without persisting.

scope: write:reports
POST /api/v1/reports/snapshots_liststableread

List snapshots for a report (summary only)

Returns snapshot metadata (window, summary counts, size) without the full payload — fetch the payload via `reports.snapshot_get`.

scope: read:reports
POST /api/v1/reports/snapshot_getstableread

Fetch one snapshot (full payload)

Returns the immutable section-by-section payload of a single snapshot.

scope: read:reports
POST /api/v1/reports/sharestablewrite

Mint a tokenised read-only public link for a snapshot

Issues a one-time-returned token + URL (`/r/{token}`). Optional `password` is hashed (sha256). TTL is clamped per tier. Fires `report.shared`.

scope: write:reports
POST /api/v1/reports/shares_liststableread

List share tokens (token values are never echoed)

Returns share metadata (expiresAt, viewCount, revokedAt). The token itself is only returned once at `reports.share` time.

scope: read:reports
POST /api/v1/reports/share_revokestablewrite

Revoke a share token immediately

Sets `revoked_at = now()` on the share. Fires `report.share_revoked`. Idempotent.

scope: write:reports
POST /api/v1/reports/policystableread

Tier limits, section catalog, webhook events

Returns per-tier caps (maxReports, maxSnapshotsPerReport, maxShares, shareMaxDays, minScheduleSeconds, maxWindowDays), allowed section types, payload size cap, webhook event list, and share URL template.

scope: read:reports

Marketplace & catalog

POST /api/v1/marketplace/publishers_upsertstablewrite

Create or update the caller's publisher profile

Each workspace has at most one publisher row. Slug must be globally unique. Required before creating any listing.

scope: write:marketplace
POST /api/v1/marketplace/publishers_getstableread

Fetch a publisher profile (own or by slug)

Returns the caller's profile when called without args, or any publisher by slug. Support email is redacted unless caller is the owner.

scope: read:marketplace
POST /api/v1/marketplace/publishers_apply_verificationstablewrite

Submit publisher verification request

Files a verification request with optional evidence URL/notes. Reviewed within 72h SLA. Idempotent once verified.

scope: write:marketplace
POST /api/v1/marketplace/listings_createstablewrite

Create a draft listing + initial version

Creates a `draft` listing under the caller's publisher and seeds a 0.1.0 (or supplied) version row with hashed manifest. Tier caps enforced.

scope: publish:marketplace
POST /api/v1/marketplace/listings_updatestablewrite

Update a listing; optionally snapshot a new version

Mutating `manifest` on a non-draft listing requires `bumpVersion` (creates an immutable `public_api_listing_versions` row).

scope: publish:marketplace
POST /api/v1/marketplace/listings_submitstablewrite

Submit a draft (or suspended) listing for review

Transitions draft|suspended → pending. Idempotent on pending.

scope: publish:marketplace
POST /api/v1/marketplace/listings_publishstablewrite

Self-serve publish a draft or pending listing

Validates capability kind manifests require `manifest.bindings`. Fires `marketplace.listing_published`.

scope: publish:marketplace
POST /api/v1/marketplace/listings_unpublishstablewrite

Revert a published listing to draft

Sets `unpublished_at = now()` and fires `marketplace.listing_unpublished`. Installs are preserved but new installs blocked.

scope: publish:marketplace
POST /api/v1/marketplace/listings_getstableread

Fetch a listing by id or (publisherSlug, listingSlug)

Returns listing + publisher + caller's install row. Drafts are only visible to the owner. Pass `id`, OR both `publisherSlug` and `listingSlug`.

scope: read:marketplace
POST /api/v1/marketplace/listings_searchstableread

Browse the public catalog with filters and sort

Public-by-default search across published listings. `includeUnpublished:true` scopes to caller's own drafts.

scope: read:marketplace
POST /api/v1/marketplace/listings_versions_liststableread

List immutable version history for a listing

Returns version, changelog, manifest hash, yank status. Manifest body itself is not echoed (fetch via listings_get as owner).

scope: read:marketplace
POST /api/v1/marketplace/listings_installstablewrite

Install a listing for the caller

Creates (or reactivates) an install row pinned to the current published version. Paid listings require `acceptPaid:true`. Fires `marketplace.listing_installed` to both installer and publisher.

scope: write:marketplace
POST /api/v1/marketplace/listings_uninstallstablewrite

Uninstall a previously installed listing

Sets install.status=uninstalled, decrements installCount (floored at 0), fires `marketplace.listing_uninstalled`.

scope: write:marketplace
POST /api/v1/marketplace/installs_liststableread

List the caller's installs (active by default)

Joins listing title/slug/kind/currentVersion. Supports status filter.

scope: read:marketplace
POST /api/v1/marketplace/reviews_upsertstablewrite

Create or update the caller's review on a listing

Verified-purchase only (install required). Owners cannot review their own listings. Recomputes listing rating aggregate. Fires `marketplace.review_posted` on first create.

scope: write:marketplace
POST /api/v1/marketplace/reviews_liststableread

List public (non-hidden) reviews on a listing

Paginated, most-recent first. Hidden/moderated reviews are excluded.

scope: read:marketplace
POST /api/v1/marketplace/reviews_deletestablewrite

Delete the caller's own review

Hard-deletes and recomputes the listing rating aggregate.

scope: write:marketplace
POST /api/v1/marketplace/policystableread

Tier limits, allowed enums, webhook events

Returns per-tier caps (maxListings, maxVersionsPerListing, maxInstalls, maxReviewsPerDay, paidListingsAllowed), enum vocabularies, slug/semver regexes, and the webhook event list.

scope: read:marketplace

SLA & status pages

POST /api/v1/sla/pages_createstablewrite

Create a new status page

Creates an owner-private status page. Slug must be globally unique, kebab-case, 1–64 chars. Custom branding requires Mini+ tier.

scope: publish:sla
POST /api/v1/sla/pages_updatestablewrite

Update status page metadata, branding, components, or visibility

Owner-only partial update. Toggling isPublic does not stamp publishedAt — use sla.pages_publish for that.

scope: publish:sla
POST /api/v1/sla/pages_publishstablewrite

Mark a status page public and stamp publishedAt

Once published, anonymous visitors can view the page and its incidents/maintenance via the public read API.

scope: publish:sla
POST /api/v1/sla/pages_unpublishstablewrite

Hide a status page from public access

Sets isPublic=false. publishedAt is preserved as the original publish stamp.

scope: publish:sla
POST /api/v1/sla/pages_getstableread

Fetch a status page by id or slug (owner view)

Returns the owner's full page record. For the unauthenticated public view, use the public /status/{slug} route. Provide either `id` or `slug`.

scope: read:sla
POST /api/v1/sla/pages_liststableread

List the caller's status pages

Newest-first list of pages owned by the caller.

scope: read:sla
POST /api/v1/sla/pages_deletestablewrite

Hard-delete a status page and all dependent records

Cascades to incidents, updates, maintenance windows, and subscribers. Cannot be undone.

scope: publish:sla
POST /api/v1/sla/incidents_openstablewrite

Open a new incident on a status page

Creates an incident in `investigating` status. Optionally posts the first update body atomically. Counts against the per-tier monthly incident quota.

scope: write:sla
POST /api/v1/sla/incidents_updatestablewrite

Post a status update on an open incident

Adds a timeline entry, transitions the incident status, and stamps resolvedAt when status='resolved'. Emits `status.incident_updated` (or `status.incident_resolved`).

scope: write:sla
POST /api/v1/sla/incidents_liststableread

List incidents owned by the caller

Newest-first by startedAt. Filter by pageId and/or status.

scope: read:sla
POST /api/v1/sla/incidents_getstableread

Fetch a single incident with its full update timeline

Returns the incident record plus its updates in reverse-chronological order.

scope: read:sla
POST /api/v1/sla/maintenance_schedulestablewrite

Schedule a maintenance window

Posts a future maintenance window. scheduledEnd must be after scheduledStart. Emits `status.maintenance_scheduled`.

scope: write:sla
POST /api/v1/sla/maintenance_updatestablewrite

Update a maintenance window (status, title, timings)

Transitioning status to in_progress or completed emits matching webhook events.

scope: write:sla
POST /api/v1/sla/maintenance_liststableread

List maintenance windows owned by the caller

Newest-first by scheduledStart. Filter by pageId and/or status.

scope: read:sla
POST /api/v1/sla/maintenance_cancelstablewrite

Cancel a scheduled maintenance window

Sets status=cancelled. Does not delete the record.

scope: write:sla
POST /api/v1/sla/subscribers_addstablewrite

Add an email or webhook subscriber to a status page

Mints a single-use confirm token. autoConfirm=true is permitted only because the caller is the page owner — set it false to require email/webhook confirmation.

scope: write:sla
POST /api/v1/sla/subscribers_confirmstablewrite

Confirm a subscriber with the mailed/posted token

Idempotent — re-confirming an already-confirmed subscriber is a no-op.

scope: write:sla
POST /api/v1/sla/subscribers_liststableread

List subscribers on one of the caller's status pages

Owner-only. Defaults to active (non-unsubscribed) subscribers.

scope: read:sla
POST /api/v1/sla/subscribers_removestablewrite

Unsubscribe a subscriber (soft delete)

Stamps unsubscribedAt; record is retained for audit. Re-adding the same (channel,target) requires a fresh add call.

scope: write:sla
POST /api/v1/sla/targets_liststableread

List SLA targets visible to the caller plus tier defaults

Returns user-specific overrides, global defaults, the caller's tier, and the canonical default-target row for that tier.

scope: read:sla
POST /api/v1/sla/credits_computestablewrite

Compute and upsert an SLA credit ledger entry for a period

Compares observed uptime against the caller's tier target. Emits `sla.credit_issued` when credit > 0. Upserts on (user, periodMonth).

scope: write:sla
POST /api/v1/sla/credits_liststableread

List the caller's SLA credit ledger

Newest-first by createdAt. Statuses: pending, approved, paid, denied.

scope: read:sla
POST /api/v1/sla/policystableread

Tier limits, allowed enums, credit schedule, webhook events

Returns the caller's tier, per-tier caps (maxPages, maxIncidentsPerMonth, maxSubscribersPerPage, allowCustomBranding), default SLA targets per tier, enum vocabularies, the SLA credit schedule (gap → credit%), slug/period regexes, and the webhook event list.

scope: read:sla

Data contracts & schema registry

POST /api/v1/schemas/registerstablewrite

Register a new schema version (creates the schema on first call)

Stores an immutable schema version under (namespace, name). Version must strictly increase by semver. Auto-computes compatibility (`backward` if no breaking changes) and a breaking-change diff vs the previous latest. Emits `schema.registered` on first version and `schema.version_published` on every version.

scope: publish:contracts
POST /api/v1/schemas/liststableread

List schemas owned by the caller

Newest-updated first. Excludes deprecated schemas unless `includeDeprecated` is true.

scope: read:contracts
POST /api/v1/schemas/getstableread

Fetch a schema and one of its versions

Look up by id OR (namespace,name). When `version` is omitted, returns the schema's latest version.

scope: read:contracts
POST /api/v1/schemas/versions_liststableread

List versions of a schema

Newest-first by createdAt. Includes yank metadata.

scope: read:contracts
POST /api/v1/schemas/deprecatestablewrite

Mark a schema as deprecated

Sets `deprecatedAt` on the schema. Existing contracts continue to validate but the schema is hidden from default lists. Emits `schema.deprecated`.

scope: publish:contracts
POST /api/v1/schemas/yank_versionstablewrite

Yank a single schema version (soft-disable)

Stamps `yankedAt` and `yankReason`. The row remains for audit; downstream contracts pinned to that version should be migrated.

scope: publish:contracts
POST /api/v1/schemas/validatestableread

Validate a payload against a specific schema version

Returns `{ valid, errors[] }`. Errors include JSON-pointer-like paths, keyword, and message. Defaults to the schema's latest version.

scope: read:contracts
POST /api/v1/schemas/diffstableread

Diff two versions of a schema

Returns all field-level changes plus a `breakingChanges` filter and resulting `compatibility` level. Breaking = required_added, type_changed, enum_value_removed, constraint_tightened, pattern_changed, additional_properties tightening, items_changed (non-object).

scope: read:contracts
POST /api/v1/schemas/compatibility_checkstableread

Dry-run compatibility check for a candidate schema

Like `schemas.diff` but the new side is a draft schema not yet stored. Use in CI to gate breaking changes before calling `schemas.register`.

scope: read:contracts
POST /api/v1/contracts/createstablewrite

Bind a schema version to a runtime subject (event / capability / endpoint)

Creates a validation contract. `mode='validate'` is read-only; `warn` records a `warn` violation; `enforce` records `error` and surfaces `enforced=true` to callers. Emits `contract.created`.

scope: write:contracts
POST /api/v1/contracts/updatestablewrite

Update a contract's pinned version, mode, or active flag

Partial update. Switching `schemaVersion` requires the version to exist on the bound schema. Emits `contract.updated`.

scope: write:contracts
POST /api/v1/contracts/liststableread

List the caller's contracts

Filter by subjectType / subject / activeOnly. Newest-updated first.

scope: read:contracts
POST /api/v1/contracts/getstableread

Fetch a contract by id

Owner-only.

scope: read:contracts
POST /api/v1/contracts/deletestablewrite

Delete a contract (cascades violation log)

Hard delete. Emits `contract.deleted`.

scope: write:contracts
POST /api/v1/contracts/validatestableread

Validate a payload against a resolved contract

Resolve the contract by id, name, or active (subjectType,subject). Records a violation when invalid and the contract mode is `warn`/`enforce` (or `recordViolation=true` is passed). Emits `contract.violation` on recorded violations.

scope: read:contracts
POST /api/v1/contracts/violations_liststableread

List recent contract violations

Newest-first. Filter by contractId. Each row includes an excerpted payload (cap 2 KiB) and the error list.

scope: read:contracts
POST /api/v1/contracts/policystableread

Tier limits, supported JSON-Schema keywords, diff semantics, events

Returns per-tier caps (maxSchemas, maxVersionsPerSchema, maxContracts, maxViolationsRetained, maxSchemaBytes), enum vocabularies, semver/name regexes, the supported JSON-Schema keyword subset, the breaking-change classification table, and the webhook event list.

scope: read:contracts

Privacy & data rights

POST /api/v1/privacy/consentsstableread

List the caller's consent record

Returns each privacy topic (telemetry, marketing, ai_training, third_party_sharing, session_replay) with whether it is granted, who last changed it, and when. Topics with no row default to `granted: false`.

scope: read:privacy
POST /api/v1/privacy/update_consentstablewrite

Grant or revoke a privacy consent topic

Upserts a consent row for the caller, stamps the source (api/ui/import), appends a hash-chained audit entry, and fires `privacy.consent_changed`.

scope: write:privacy
POST /api/v1/privacy/requestsstableread

List the caller's data-subject requests

Returns DSAR rows for the caller (export | delete | rectify | restrict) with status, due-by, and any artifact SHA. Default order is most recent first.

scope: read:privacy
POST /api/v1/privacy/submit_requeststablewrite

Submit a data-subject request (DSAR)

Files an export | delete | rectify | restrict request. Returns a request id with a 30-day statutory due-by. Deletion requests fire `privacy.deletion_requested` so admins can action it; the row stays `pending` until fulfilled.

scope: write:privacy
POST /api/v1/privacy/export_datastablewrite

Generate a portable JSON export of the caller's data

Snapshots the caller's account, keys (hashes only — never plaintext), webhooks, recent usage, audit log, teams, invoices, consents, and DSAR history into a single JSON document. Returns base64 + sha-256, marks any matching pending export request fulfilled, and fires `privacy.export_ready`.

scope: write:privacy
POST /api/v1/privacy/retention_policystableread

Dreyv's per-table retention windows

Returns Dreyv's public retention policy: per-table maximum retention windows, lawful basis, controller/processor designation, and the address for written DSAR escalations.

scope: read:privacy

Storage & artifacts

POST /api/v1/storage/putstablewrite

Upload an artifact to Dreyv-managed storage

Streams a base64-encoded body to R2-backed object storage and registers a pointer in `public_api_artifacts`. Enforces per-tier quota (free / mini / max), per-object cap (25 MiB), and computes sha-256. Returns the artifact id and a presigned download URL. Fires `storage.artifact_created` and appends a hash-chained audit entry. If quota would be exceeded, fires `storage.quota_exceeded` and refuses the write with 413.

scope: write:storage
POST /api/v1/storage/liststableread

List the caller's artifacts

Returns non-deleted artifacts owned by the caller, newest first. Optional `prefix` filters by filename, `keyOnly` restricts to artifacts created with the current key.

scope: read:storage
POST /api/v1/storage/getstableread

Issue a short-lived presigned download URL

Looks up the artifact, verifies caller ownership, and returns a presigned GET URL valid for `ttlSeconds` (default 600, max 3600). The URL streams directly from R2 with the original filename and mime as Content-Disposition.

scope: read:storage
POST /api/v1/storage/statstableread

Fetch artifact metadata without downloading

Returns the artifact row (filename, mime, bytes, sha-256, metadata, visibility, createdAt) without issuing a download URL.

scope: read:storage
POST /api/v1/storage/deletestablewrite

Soft-delete an artifact and purge its R2 object

Marks `deleted_at` on the artifact row, removes the underlying R2 object best-effort, appends an audit entry, and fires `storage.artifact_deleted`. Idempotent: deleting an already-deleted artifact returns the same row without error.

scope: write:storage
POST /api/v1/storage/quotastableread

Storage usage and tier quota for the caller

Aggregates live byte and object counts against the tier-specific quota (free: 100 MiB / 100 objects, mini: 5 GiB / 5,000 objects, max: 50 GiB / 50,000 objects) and returns percent-utilised plus a single-object cap (25 MiB).

scope: read:storage
POST /api/v1/storage/policystableread

Dreyv storage SLA, retention, and accepted MIME policy

Returns the storage tier matrix, retention windows, presign TTL bounds, accepted MIME prefixes, single-object cap, and durability target. Public, deterministic — safe to cache.

scope: read:storage

Search & indexing

POST /api/v1/search/querystableread

Cross-pillar full-text search over the caller's surface

Tokenized AND-match (case-insensitive) across selectable indexes — audit, artifacts, notifications, watchers, monitors, deliveries, proposals, usage. Every hit is strictly scoped to the caller (userId). Returns ranked, deduped hits with `{ index, id, title, snippet, ts, score, meta }`. Supports time window (`since`/`until`) and per-call `limit` up to 200.

scope: read:search
POST /api/v1/search/recentstableread

Unified reverse-chronological timeline across indexes

Returns the caller's most recent events across the same eight indexes, merged and sorted newest-first. Use this as the default firehose when no query is needed (e.g. activity tabs).

scope: read:search
POST /api/v1/search/facetsstableread

Per-index hit counts for an optional query and window

Aggregates how many rows in each index match the optional `q` and time window. Useful for badge counts and pre-flighting an expensive `search.query` call.

scope: read:search
POST /api/v1/search/suggeststableread

Autocomplete distinct field values the caller has used

Returns up to `limit` distinct, recent values for the requested field (`capability_id`, `action`, `filename`, `event`, `notification_type`) whose value starts with `prefix` (case-insensitive). Strictly scoped to the caller.

scope: read:search
POST /api/v1/search/lookupstableread

Resolve an opaque id to its owning index

Given a UUID or numeric id, probes each searchable index for a row owned by the caller and returns the first match. Lets clients build deep-links without remembering which table an id came from.

scope: read:search
POST /api/v1/search/indexesstableread

Manifest of searchable indexes and their fields

Returns the static manifest of indexes available to `search.query` / `search.recent`, including which text fields are scanned, the timestamp column used for ordering, and the underlying table.

scope: read:search
POST /api/v1/search/policystableread

Search engine limits, operators, and scoring contract

Returns the public search contract: max query length, max token count, AND-only operator semantics, snippet length, default + max limit, and the deterministic scoring rule (token coverage × recency decay). Safe to cache.

scope: read:search
POST /api/v1/search/indexstablewrite

Index (or update) a document for cross-entity search

Idempotent on (entityType, entityId). Postgres builds the tsvector automatically with A/B weighting (title vs body).

scope: write:search
POST /api/v1/search/querystableread

Full-text search across indexed entities

Uses Postgres `plainto_tsquery` ranking. Optionally scope to a single `entityType`.

scope: read:search
POST /api/v1/search/saved_createstablewrite

Create or update a saved search

Upserts on (userId, name). `pinned` controls list ordering.

scope: write:search
POST /api/v1/search/saved_liststableread

List saved searches (pinned first)

Pinned entries appear first, then by most recently updated.

scope: read:search
POST /api/v1/search/saved_deletestablewrite

Delete a saved search

Hard delete.

scope: write:search

Status, SLO & deprecations

POST /api/v1/status/healthstableread

Overall + per-component health summary

Computes an aggregate health status (`operational` | `degraded` | `partial_outage` | `major_outage`) from rolling error rates and latency p95 over the last 15 minutes of `public_api_key_usage`, segmented by pillar and infra component (db, r2, webhooks, rate-limiter). Includes the worst-offending component and a human-readable summary line.

scope: read:status
POST /api/v1/status/componentsstableread

Registered components and their live status

Returns every component Dreyv monitors (each pillar + infra: postgres, r2-storage, webhook-dispatcher, rate-limiter, audit-chain) with current status, p50/p95 latency, error rate, sample size, and the SLO they're measured against. Sample window is configurable (default 15m, max 24h).

scope: read:status
POST /api/v1/status/uptimestableread

Rolling uptime % over 1h / 24h / 7d / 30d windows

Derives uptime from `public_api_key_usage` outcomes (`ok` / `rate_limited` count as up; `error` counts as down) bucketed into minute buckets. A minute is `down` if >50% of calls in that minute errored. Returns the up-minute ratio across each window. Aggregate across all callers — Dreyv-wide signal, not per-key.

scope: read:status
POST /api/v1/status/incidentsstableread

Active and recently-resolved incidents

Detects incidents by scanning the last `lookbackHours` (default 24, max 168) of usage rows for contiguous runs of degraded minutes per component. Returns active incidents (still degraded in the last 5m) and recently-resolved ones with start/end timestamps, impacted component, peak error rate, and affected request count.

scope: read:status
POST /api/v1/status/slostableread

Per-component SLO targets and live burn rate

Returns the canonical Dreyv SLOs (availability + latency-p95 targets per component) alongside the live 1h and 6h error budget burn rates. A burn rate >1 in either window signals the budget is being consumed faster than the period allows.

scope: read:status
POST /api/v1/status/deprecationsstableread

Capability deprecation feed with sunset dates

Returns the curated list of deprecated capabilities and fields with announced-at, sunset-at, replacement capability id, and migration notes. Drives the `Sunset` HTTP header and SDK warning toggles. Stable, deterministic — safe to cache.

scope: read:status
POST /api/v1/status/changelogstableread

Public API changelog (reverse chronological)

Returns the curated public changelog: every phase release, capabilities added, breaking changes (always called out), security fixes, schema migrations. One canonical feed driving `/developers/api` and the `What's new` banner.

scope: read:status
POST /api/v1/status/versionstableread

API version, capability counts, and build identity

Returns the public API version (`v1`), current capability registry count by pillar/side-effect/status, deprecation count, supported transports, and the build commit (when available via `VERCEL_GIT_COMMIT_SHA`). Useful for SDK compat checks and `If-Match`-style negotiation.

scope: read:status
POST /api/v1/meta/versionstableread

Dreyv MCP server version, tool/pillar counts, and schema metadata

[META] Returns server name + semver, MCP protocol schema version, total tool count, pillar count, scope list, and a sideEffect breakdown (read / unsigned-tx / write / pay). Call this first to know what you're connected to. Includes links to OpenAPI, manifest, REST base, and changelog.

scope: read:status
POST /api/v1/meta/capabilities_overviewstableread

300+ tools mapped to 39 pillars with execution-label tallies

[META] Compact map of every tool grouped by pillar, each tagged with executionLabel (READ / UNSIGNED-TX / WRITE / PAY) so agents know what each call will do before invoking. Filter by pillar or executionLabel to narrow. Safer than tools/list for first-pass discovery.

scope: read:status
POST /api/v1/meta/intents_howtostableread

End-to-end execution playbooks (swap, intents, stake, autonomous pay)

[META] Step-by-step flows for the four execution paths: Jupiter swap, persistent compile→sign→submit intent (multisig-friendly), Marinade SOL stake, and autonomous SKU purchase via dreyv_economy_pay. Includes the non-custodial signing model, preflight tips, and structured error semantics.

scope: read:status
POST /api/v1/meta/instructionsstableread

Server orientation text (custody model, tool tags, discovery)

[META] Returns the same orientation block we'd return in MCP `initialize.instructions` if the transport supported it: who we are, the [READ]/[UNSIGNED-TX]/[WRITE]/[PAY] tag system, how to discover, how to authenticate.

scope: read:status

Jobs & scheduling

POST /api/v1/jobs/schedulestablewrite

Schedule a recurring or one-shot capability run

Persists a job that invokes another registered capability on a schedule. `kind:'recurring'` requires `intervalSeconds` (>= tier floor: free 15m / mini 5m / max 1m); `kind:'one_shot'` requires `runAt` (ISO). The payload is validated against the target capability's input schema at schedule time and again at every run. Per-tier active-job quota enforced (free 5 / mini 50 / max 500). Cannot schedule `jobs.*` capabilities (loop guard).

scope: write:jobs
POST /api/v1/jobs/liststableread

List scheduled jobs (optionally filtered)

Returns up to `limit` (default 100, max 500) of the caller's jobs, newest first. Optional filters: `status` (`active` | `paused` | `cancelled` | `completed`) and `capabilityId` (exact match).

scope: read:jobs
POST /api/v1/jobs/getstableread

Fetch a single job and (optionally) its recent runs

Returns the full job row plus, when `includeRuns:true`, the most recent run history (up to `runLimit`, default 20, max 200). Strictly scoped to the caller's userId.

scope: read:jobs
POST /api/v1/jobs/cancelstablewrite

Cancel a scheduled job

Marks the job `cancelled`. Idempotent: a job already in a terminal state (`cancelled` | `completed`) is returned with `alreadyTerminal:true` instead of erroring. Emits the `job.cancelled` webhook and writes a hash-chained audit row.

scope: write:jobs
POST /api/v1/jobs/run_nowstablewrite

Execute a job immediately (out-of-band of its schedule)

Synchronously invokes the job's capability with its stored payload and records a `triggered_by:'manual'` run row. Does NOT advance the recurring schedule's `nextRunAt`. Returns the run id, status, duration, and truncated result (<=16 KiB) or error. Emits `job.completed` or `job.failed`.

scope: write:jobs
POST /api/v1/jobs/runsstableread

List job runs across one or all jobs

Returns up to `limit` (default 50, max 500) run rows newest first. Optional filters: `jobId` to scope to a single job, and `status` (`queued` | `running` | `ok` | `failed` | `cancelled`).

scope: read:jobs
POST /api/v1/jobs/retrystablewrite

Retry a failed run

Re-executes the same payload as a new attempt (attempt = original + 1) up to the job's `maxRetries`+1 cap. Only runs with `status:'failed'` are eligible. Records a `triggered_by:'retry'` row and emits the same completion/failure webhooks as a scheduled run.

scope: write:jobs
POST /api/v1/jobs/policystableread

Scheduler limits, intervals, and trigger taxonomy

Returns the caller's tier, its per-tier job quota and minimum interval, the 30-day interval ceiling, payload (32 KiB) and result-capture (16 KiB) byte limits, the retry ceiling, the valid trigger sources, and the canonical job/run status enums. Stable and cache-safe.

scope: read:jobs

Exports & reports

POST /api/v1/exports/sourcesstableread

List supported export sources, formats, and columns

Returns the catalog of every data source you can export (audit, usage, deliveries, notifications, watchers, monitors, proposals, jobs, runs, artifacts), the columns each source projects, the read-scope each source effectively requires, and the supported serialization formats (`csv` | `jsonl`). Stable and cache-safe.

scope: read:exports
POST /api/v1/exports/createstablewrite

Generate a CSV/JSONL export of one of your data sources

Synchronously serializes the caller's rows for the given `source` (filtered by `since` / `until` ISO timestamps and optional `status` / `capabilityId`) into `csv` or `jsonl`. Body stored inline (cap 4 MiB) with sha-256, row count, and `truncated` flag. Per-tier active-export quota enforced (free 5 / mini 50 / max 500) and per-tier row cap (free 5k / mini 50k / max 500k). Emits `export.created` then `export.completed` (or `export.failed`) and writes a hash-chained audit row.

scope: write:exports
POST /api/v1/exports/liststableread

List your generated exports (metadata only)

Returns up to `limit` (default 100, max 500) of the caller's exports, newest first. Body is NOT included. Filter by `source` or `status` (`pending` | `completed` | `failed` | `purged`).

scope: read:exports
POST /api/v1/exports/getstableread

Fetch a single export's metadata

Returns the full metadata row (label, source, format, status, rowCount, byteSize, sha256, truncated, filters, expiresAt, createdAt, purgedAt) for one export. The body itself is fetched via `exports.download`. Strictly scoped to the caller's userId.

scope: read:exports
POST /api/v1/exports/downloadstableread

Download an export body (base64-encoded)

Returns the stored body as base64 along with `contentType` (`text/csv` or `application/x-ndjson`), a safe `filename`, the `sha256`, and `byteSize`. Only `status:'completed'` exports are downloadable; purged (410) and expired (410) exports surface explicit errors. Re-downloads do not advance any quota.

scope: read:exports
POST /api/v1/exports/purgestablewrite

Purge an export's body while retaining its metadata

Sets `status:'purged'`, drops the inline body, and stamps `purgedAt`. Metadata (row count, byte size, sha-256, filters) is preserved for auditability. Idempotent — already-purged rows return `alreadyPurged:true`. Writes a hash-chained audit row.

scope: write:exports
POST /api/v1/exports/statsstableread

Aggregate counts and bytes per source/status

Returns per (source, status) totals — count, totalBytes, totalRows — across every export you've ever generated, plus your current tier, active-export quota usage, max-row cap, and TTL.

scope: read:exports
POST /api/v1/exports/policystableread

Export engine limits, formats, and retention

Returns the caller's tier and its quotas (active exports, max rows, TTL days), the 4 MiB body cap, default TTL, supported formats and sources, and the canonical status enum. Stable and cache-safe.

scope: read:exports

Tags, labels & annotations

POST /api/v1/tags/createstablewrite

Create a tag (idempotent on normalized name)

Creates a user-scoped tag with a normalized lowercase `name` (the lookup key), the original-cased `display`, a `color` from the 14-preset palette (default `violet`), and an optional `description` and free-form `metadata`. Idempotent on `(userId, name)`: re-creating returns the existing row with `alreadyExisted:true`. Per-tier tag quota enforced (free 25 / mini 250 / max 2500). Writes a hash-chained audit row.

scope: write:tags
POST /api/v1/tags/liststableread

List your tags with link counts

Returns up to `limit` (default 200, max 1000) of the caller's tags, newest first, each hydrated with its current `linkCount` (number of entities attached). Optional case-insensitive `query` does a substring match against the normalized name.

scope: read:tags
POST /api/v1/tags/updatestablewrite

Update a tag's display, color, description, or metadata

Patches the named/uuid'd tag. `name` (the lowercase lookup key) is immutable — pass a new `display` to relabel without breaking existing links. `color` must come from the preset palette. Writes a hash-chained audit row recording which fields changed.

scope: write:tags
POST /api/v1/tags/deletestablewrite

Delete a tag and cascade-remove its links

Removes the tag and every link attached to it. `id` accepts either the tag uuid or its normalized name. The number of cascaded links is reported as `removedLinks` and recorded in the audit row.

scope: write:tags
POST /api/v1/tags/attachstablewrite

Attach a tag to any entity (idempotent on the triple)

Links the tag to `(entityType, entityId)` with an optional `note` (<= 1000 chars). `entityType` is one of address|tx|capability|job|run|artifact|proposal|watcher|monitor|export|notification|delivery|webhook|custom. `entityId` is opaque text (uuid, numeric id, address, tx hash, capability id, freeform). Idempotent on `(tagId, entityType, entityId)`: a duplicate attach returns the existing row, and a non-null `note` will be updated in-place (`noteUpdated:true`). Per-tag and per-entity quotas enforced.

scope: write:tags
POST /api/v1/tags/detachstablewrite

Detach a tag from an entity

Removes the link for the `(tag, entityType, entityId)` triple. Idempotent: if no such link exists the response is `{ deleted:false, alreadyDetached:true }`. Writes a hash-chained audit row when an actual delete occurs.

scope: write:tags
POST /api/v1/tags/entitiesstableread

List entities for a tag, or tags for an entity

Two modes. (1) `tag` only → returns up to `limit` entities attached to that tag (`mode:'by_tag'`). (2) `entityType` + `entityId` → returns every tag attached to that entity (`mode:'by_entity'`), with the tag row joined inline. Default limit 200, max 1000.

scope: read:tags
POST /api/v1/tags/policystableread

Tagging engine limits, palette, and entity taxonomy

Returns the caller's tier and its quotas (max tags, max links per tag, max tags per entity), the full 14-color preset palette, the 14-value entity-type taxonomy, and per-field length caps. Stable and cache-safe.

scope: read:tags

Feature flags & experiments

POST /api/v1/flags/createstablewrite

Create a typed feature flag with ordered rules

Creates a user-scoped feature flag. `key` is normalized to a lowercase slug (immutable, unique per user). `type` is one of boolean|string|number|json and is enforced on `defaultValue` and every rule's `value`. `rules` is an ordered list of `{ when: { attr: { op, value } }, value, note? }`; the first rule whose `when` map fully matches the eval context wins. Returns 409 if the key already exists. Tier quotas: free 10 flags / 8 rules, mini 100 / 32, max 1000 / 128. Writes a hash-chained audit row.

scope: write:flags
POST /api/v1/flags/liststableread

List your flags with rule counts

Returns up to `limit` (default 200, max 1000) of the caller's flags, newest first, each hydrated with `ruleCount`. Archived flags are excluded unless `includeArchived:true`. Optional case-insensitive `query` does a substring match against the normalized key.

scope: read:flags
POST /api/v1/flags/getstableread

Fetch a single flag by uuid or key

Returns the full flag row including its rule list and metadata. `id` accepts either the flag uuid or its normalized key.

scope: read:flags
POST /api/v1/flags/updatestablewrite

Patch a flag's display, default, rules, or enabled state

Patches the named/uuid'd flag. `key` and `type` are immutable — re-validates `defaultValue` and every `rules[i].value` against the existing type. Per-tier rule cap re-checked when `rules` is supplied. Writes a hash-chained audit row recording which fields changed.

scope: write:flags
POST /api/v1/flags/archivestablewrite

Archive (soft-disable) or restore a flag

Sets `archivedAt=now()` and forces `enabled=false` so subsequent evaluations always return the default with reason `archived`. Pass `restore:true` to clear `archivedAt` and resume normal evaluation. Audit row written either way.

scope: write:flags
POST /api/v1/flags/deletestablewrite

Permanently delete a flag and its evaluation log

Hard-deletes the flag; the cascade also drops every row in the evaluation log. The number of removed evaluation rows is reported as `removedEvaluations` and recorded in the audit row. For non-destructive disable, use `flags.archive`.

scope: write:flags
POST /api/v1/flags/evaluatestableread

Server-evaluate one flag against a context

Loads the flag and walks its rules in order against `context`. Operators: eq, neq, in, nin, gt, gte, lt, lte, contains, startsWith, endsWith, regex, exists, percent. `percent` buckets via sha256(`${flagKey}:${ruleIndex}` + actual) for stable rollout. Returns `{ value, matchedRule, reason }` where reason is one of `rule_match` | `default` | `disabled` | `archived`. Evaluation is logged unless `log:false`.

scope: read:flags
POST /api/v1/flags/bulk_evaluatestableread

Evaluate up to 100 flags against a shared context

One-roundtrip evaluation for multiple keys. Returns `{ results: { [key]: { found, value, matchedRule, reason, ... } } }` plus a `missing` list of keys with no matching flag. Same evaluation semantics and logging behavior as `flags.evaluate`.

scope: read:flags
POST /api/v1/flags/historystableread

Recent evaluations for a flag with rule distribution

Returns up to `limit` (default 100, max 500) of the most recent evaluation rows for the flag, plus a `distribution` map counting how often each `rule_N` (or `default`) was the winning branch. Each row carries the redacted context (<=2KiB) and its canonical sha256 hash for replay comparison.

scope: read:flags
POST /api/v1/flags/policystableread

Flag-engine limits, types, and operator catalog

Returns the caller's tier and quotas (max flags, max rules per flag, daily evaluation log cap), the four supported types, the full operator catalog, per-field length caps, and evaluation semantics notes. Stable and cache-safe.

scope: read:flags

Observability: traces, spans & metrics

POST /api/v1/obs/trace_ingeststablewrite

Ingest an OTel-style trace (1 trace, N spans)

Stores a trace and all spans atomically. Idempotent on (userId, traceId): re-ingesting updates the trace summary and adds any missing spans.

scope: write:obs
POST /api/v1/obs/trace_liststableread

List recent traces

Newest-first. Filter by service or status.

scope: read:obs
POST /api/v1/obs/span_liststableread

List spans for a trace (ordered by start time)

Returns every span tied to the trace, ordered by `startNs` ascending.

scope: read:obs
POST /api/v1/obs/metric_ingeststablewrite

Ingest a batch of metric points (counter/gauge/histogram)

Up to 5000 points per call. Labels are arbitrary JSON.

scope: write:obs
POST /api/v1/obs/metric_querystableread

Query metric points and aggregate stats over a window

Returns raw points plus min/max/avg/last summary. Default window is the last 24h.

scope: read:obs

Rate limits & quotas

POST /api/v1/limits/policy_createstablewrite

Create a rate-limit policy (per key, user, route, or IP)

Defines `limitCount` requests per `windowSec` with optional `burst`. `action` chooses what happens on overflow: `throttle`, `reject`, or `log`.

scope: write:limits
POST /api/v1/limits/policy_liststableread

List rate-limit policies

Newest-updated first.

scope: read:limits
POST /api/v1/limits/policy_updatestablewrite

Update a rate-limit policy (partial)

Patch `enabled`, `limitCount`, `windowSec`, `burst`, or `action`.

scope: write:limits
POST /api/v1/limits/policy_deletestablewrite

Delete a rate-limit policy

Cascades to its usage rows.

scope: write:limits
POST /api/v1/limits/checkstablewrite

Check & consume a rate-limit bucket

Atomically increments the bucket if allowed, returns `{ allowed, limit, remaining, resetAt }`. Buckets are aligned to fixed windows.

scope: write:limits
POST /api/v1/limits/usagestableread

Recent usage rows for a policy

Newest-first.

scope: read:limits

Data residency & region pinning

POST /api/v1/regions/liststableread

List supported regions

Static catalog of region codes you can pin resources to.

scope: read:residency
POST /api/v1/residency/policy_getstableread

Get the caller's residency policy

Returns the default region, allowed-region list, and strict-mode flag. Returns sensible defaults (with `inherited:true`) if no policy is set.

scope: read:residency
POST /api/v1/residency/policy_setstablewrite

Set the caller's residency policy

Validates regions against the supported list. `defaultRegion` must be in `allowedRegions`. Upserts in place.

scope: write:residency
POST /api/v1/residency/pinstablewrite

Pin a resource to a specific region

Refuses regions not in the caller's `allowedRegions`. Idempotent on (resourceType, resourceId).

scope: write:residency
POST /api/v1/residency/pin_liststableread

List pinned resources

Filter by `resourceType` and/or `region`.

scope: read:residency

Digests & multi-channel fan-out

POST /api/v1/digests/channel_createstablewrite

Create a digest channel (slack / telegram / email / webhook)

Stores the channel target plus optional quiet-hours window (`HH:MM` UTC). Use a `secretRef` to point at a stored signing token instead of inlining secrets.

scope: write:digests
POST /api/v1/digests/channel_liststableread

List digest channels

Filter by `kind` or `enabled` state.

scope: read:digests
POST /api/v1/digests/channel_deletestablewrite

Delete a digest channel

Schedules referencing the channel will skip it on next run.

scope: write:digests
POST /api/v1/digests/schedule_createstablewrite

Create a digest schedule (cron → channels)

Validates that all `channelIds` belong to the caller and that `cron` has 5 fields. The runner picks up the next-due schedule.

scope: write:digests
POST /api/v1/digests/schedule_liststableread

List digest schedules

Newest-updated first.

scope: read:digests
POST /api/v1/digests/send_nowstablewrite

Send an immediate digest to a single channel

Respects quiet hours. Records the send intent in the audit log so delivery is traceable.

scope: write:digests

Workflows & DAG automations

POST /api/v1/workflows/createstablewrite

Create a workflow (trigger → DAG of nodes)

Validates the DAG: unique node ids, all `next` refs resolved, no cycles, ≤100 nodes. Node `kind` ∈ {capability, branch, delay, noop}.

scope: write:workflows
POST /api/v1/workflows/liststableread

List workflows

Newest-updated first.

scope: read:workflows
POST /api/v1/workflows/getstableread

Get a workflow by id

Returns the full definition (trigger + nodes).

scope: read:workflows
POST /api/v1/workflows/deletestablewrite

Delete a workflow

Cascades to runs.

scope: write:workflows
POST /api/v1/workflows/runstablewrite

Execute a workflow (topological order, records a run)

Topologically orders nodes, executes them, and stores a `workflow_run` row with per-node log. Capability nodes are queued (not executed inline) so long fan-outs don't block the API call; pass `dryRun:true` to only validate the plan.

scope: write:workflows
POST /api/v1/workflows/runs_liststableread

List recent workflow runs

Newest-first. Filter by `workflowId` and/or `status`.

scope: read:workflows

Entitlements & plan enforcement

POST /api/v1/entitlements/liststableread

List every feature, its plan value, and active override

Returns the caller's tier plus a per-feature breakdown: `planValue`, `override`, `effectiveValue`, `source`.

scope: read:entitlements
POST /api/v1/entitlements/checkstableread

Check whether a feature is allowed (optionally with a requested numeric value)

For boolean features, returns the value. For numeric features, returns `allowed = requestedValue ≤ effectiveValue`.

scope: read:entitlements
POST /api/v1/entitlements/override_setstablewrite

Set or update an entitlement override (optionally time-boxed)

Upserts on (userId, feature). `value` can be boolean, number, string, or object. `expiresAt` makes the override temporary.

scope: write:entitlements
POST /api/v1/entitlements/override_liststableread

List active entitlement overrides

Newest-updated first. Expired overrides remain visible so you can clean them up.

scope: read:entitlements
POST /api/v1/entitlements/override_deletestablewrite

Delete an entitlement override

Subsequent checks fall back to the plan value.

scope: write:entitlements

Roadmap

Phases 1 to 31 shipped pillars A through AO (identity, portfolio, market, execution, forensics, agentic, storage, jobs, exports, tags, flags, inbox, teams, billing, insights, marketplace, SLA, contracts). Phases 32 to 39 add pillars AP to AX: observability with OTel-style traces and metrics, a hash-chained audit log with SOC2/GDPR evidence bundles, configurable rate-limit policies, data residency and region pinning, multi-channel digests (Slack/Telegram/email/webhook) with quiet hours, cross-entity full-text search with saved queries, a workflow DAG engine with topologically ordered runs, and plan-aware entitlements with per-user overrides.