Freshness Validator

API documentation

Everything you need to call Freshness Validator over REST or the Model Context Protocol (MCP). This page is also available as plain Markdown.

Base URL

https://api.myuiaf.com

Authentication

Send your agent's API key in the Authorization header. Keys look like fv1.<agent_id>.<key_id>.<secret>. The key identifies the agent, so agent_id in a request body is optional. If you include it, it must match the key's agent, or the request fails with 403.

Authorization: Bearer fv1.acme-bot.1a2b3c4d.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are shown once, when we create them. Store them like any other secret. A revoked key stops working immediately. Ten failed attempts from one IP address within ten minutes block that address for fifteen minutes.

Credits

Each agent has a wallet denominated in US dollars. A validation call deducts the per-call rate for the agent's tier (see pricing). Registering a source costs twice the per-call rate. credits_deducted and remaining_balance are in dollars. The wallet endpoint reports the balance as balance_usdc, which means dollar-denominated credits, not an on-chain balance.

Validate context

POST /api/v1/validate

Checks how fresh a piece of context is. Returns a score from 0 to 100, a stale flag, flagged issues, and suggestions. How the score is built.

FieldDescription
contextRequired. The text the agent holds, up to 50,000 characters.
source_urlOptional. The public https page the context came from. Register it first to get change detection and content matching.
last_validatedOptional. ISO 8601 time the agent last fetched or validated this context. Without it, the time-decay signal is skipped.
metadata.content_typeOptional. verbatim (default) or paraphrase for summaries.
metadata.freshness_policyOptional. How long context stays fresh: 1h to 8760h, or 1d to 365d. Default 24h.
agent_idOptional. Must match the key's agent if present.
{
  "context": "The new rate limit is 100 req/min",
  "source_url": "https://example.com/api-docs",
  "last_validated": "2026-11-01T00:00:00Z",
  "metadata": {
    "content_type": "verbatim",
    "freshness_policy": "24h"
  }
}

Response, 200 OK

{
  "freshness_score": 72,
  "is_stale": false,
  "confidence": 0.85,
  "source_match": { "method": "overlap", "overlap": 0.95 },
  "flagged_issues": [],
  "suggestions": [],
  "source_status": "monitored",
  "source_hash": "a1b2c3d4e5f6...",
  "source_last_checked": "2026-11-04T14:00:00.000Z",
  "source_last_changed": null,
  "context_hash": "9f8e7d6c5b4a...",
  "validated_at": "2026-11-04T14:30:00.000Z",
  "credits_deducted": 0.03,
  "remaining_balance": 4.97
}
FieldMeaning
freshness_score0 to 100. 80 and above is fresh, 70 to 79 is a warning, below 70 is stale.
is_staleTrue when the score is below 70.
confidence0 to 1. Higher when the source is monitored with stored text, was checked recently, and you sent last_validated.
source_match.methodexact, overlap (5-word shingles), keyword (paraphrase), or none (no stored source text).
source_statusunmonitored, monitored, or error (recent checks of the source failed).
source_hashSHA-256 of the source's normalized text at its last check, or null.
context_hashSHA-256 of your normalized context. The context itself is never stored.

Response, 402 insufficient credits. The freshness result is still returned and nothing is charged.

{
  "freshness_score": 72,
  "is_stale": false,
  "...": "same fields as above",
  "credits_deducted": 0,
  "remaining_balance": 0.01,
  "error": "Insufficient credits",
  "code": "insufficient_credits"
}

Register a source

POST /api/v1/source/register

Registers a public https URL for monitoring. The page is fetched once immediately as a baseline, then re-checked on the schedule you choose. Costs twice the per-call rate.

{
  "source_url": "https://example.com/compliance-policy",
  "check_frequency": "24h"
}

check_frequency is one of 1h, 6h, 24h (default), 7d, 30d.

  • Registering a URL you already monitor only updates its frequency, and is free.
  • If the page cannot be fetched, nothing is registered and nothing is charged (422).
  • If the wallet cannot cover the cost, the request fails with 402 and nothing is registered.
  • An agent can monitor up to 100 sources.

Response, 200 OK

{
  "source_id": "src_3fa9c21b7d",
  "source_url": "https://example.com/compliance-policy",
  "check_frequency": "24h",
  "last_checked": "2026-11-04T14:00:00.000Z",
  "last_changed": null,
  "next_check": "2026-11-05T14:00:00.000Z",
  "status": "monitored",
  "change_count": 0,
  "last_error": null,
  "created": true,
  "credits_charged": 0.06,
  "remaining_balance": 4.91
}

List and remove sources

GET /api/v1/sources

Returns { "sources": [ ... ] } with the same fields as the register response. Free.

DELETE /api/v1/sources/:source_id

Stops monitoring a source and deletes its stored text. Free. Returns { "removed": true, "source_id": "..." }.

Monitoring rules

  • Source URLs must be public https addresses. IP addresses, localhost, private network names, and custom ports are refused, and redirects are checked the same way.
  • We fetch pages with the user agent FreshnessValidator/1.0 (+https://myuiaf.com). Requests time out after 10 seconds, and we read at most the first 1 MB.
  • HTML, plain text, JSON, and XML are supported. PDFs and other binary types are not.
  • JavaScript is not run. We see the page as the server sends it.
  • A scheduled sweep runs every 15 minutes and checks the sources that are due.
  • A source counts as changed when the SHA-256 of its normalized text differs from the previous check. Markup, scripts, and styles are ignored; any text edit counts.
  • After 3 failed checks in a row a source's status becomes error. We retry hourly, and it recovers on the first success.

Wallet

GET /api/v1/wallet/:agent_id

Returns the balance, usage, and the 20 most recent transactions. An agent's key can read its own wallet only.

{
  "agent_id": "acme-bot",
  "tier": "early_testing",
  "balance_usdc": 4.91,
  "total_credited": 5,
  "total_spent": 0.09,
  "total_refunded": 0,
  "calls_this_month": 3,
  "calls_per_month_limit": 1000,
  "price_per_call": 0.03,
  "transactions": [
    {
      "tx_id": "001793000000000-a1b2c3",
      "type": "charge",
      "amount": 0.03,
      "balance_after": 4.91,
      "description": "validate",
      "created_at": "2026-11-04T14:30:00.000Z"
    }
  ]
}

MCP setup

The service exposes an MCP endpoint over HTTP using JSON-RPC 2.0 (Streamable HTTP, JSON responses only). initialize, ping, and tools/list are public. tools/call requires your agent's API key in the Authorization header.

POST https://api.myuiaf.com/mcp
content-type: application/json

Supported protocol versions: 2025-06-18, 2025-03-26, 2024-11-05. Notifications get an empty 202. Batch requests are not supported, and GET /mcp returns 405 because there is no server-initiated stream.

1. Initialize

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": {},
    "clientInfo": { "name": "my-agent", "version": "1.0.0" }
  }
}

If you send a version the server supports, it answers with the same version. Otherwise it answers with 2025-06-18, and you decide whether to continue.

2. List tools

{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
ToolArgumentsSame as
validatecontext (required), source_url, last_validated, metadata, agent_idPOST /api/v1/validate
register_sourcesource_url (required), check_frequency, agent_idPOST /api/v1/source/register

The tools/list response carries the full JSON Schema for each tool. Read it at runtime so your client always uses the current definitions.

3. Call a tool

curl -X POST https://api.myuiaf.com/mcp \
  -H "Authorization: Bearer fv1.acme-bot.1a2b3c4d.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "content-type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "register_source",
      "arguments": { "source_url": "https://example.com/api-docs", "check_frequency": "6h" }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "validate",
    "arguments": {
      "context": "The API rate limit is 100 req/min",
      "source_url": "https://example.com/api-docs"
    }
  }
}

The result's content[0].text holds the same JSON the REST endpoint returns. isError is true when the call failed (bad arguments, no credits to register, and so on). A validate call with no credits is not an error: the result is returned with "code": "insufficient_credits" in the text.

Without a valid key, tools/call answers HTTP 401 with a JSON-RPC error, code -32001, and a WWW-Authenticate header.

Client configuration

Most MCP clients accept a remote server URL plus headers. Field names differ between clients, so check your client's documentation. A typical entry looks like this:

{
  "mcpServers": {
    "freshness-validator": {
      "url": "https://api.myuiaf.com/mcp",
      "headers": { "Authorization": "Bearer fv1.acme-bot.1a2b3c4d.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
    }
  }
}

Rate limits

TierRequests per minuteCalls per month
Early Testing101,000
Growing Product3010,000
Scale Revenue100100,000
Enterprise1,000Unlimited

Every authenticated request counts toward the per-minute limit. Every validate call counts as one call toward the monthly allowance, whether or not it was charged, and a source registration counts as two. Going over the per-minute limit returns 429 with code rate_limited and a Retry-After header. Going over the monthly allowance returns 429 with code monthly_limit_reached.

Errors

Errors are JSON: { "error": "human-readable message", "code": "machine_code" }.

StatusMeaning
400A field is missing or invalid (invalid_context, invalid_source_url, invalid_json, and similar).
401The API key is missing, invalid, or revoked.
402Not enough credits. validate still returns its result; registering a source does not proceed.
403agent_mismatch, a key used on another agent's data, or source_limit_reached.
404Unknown path, agent, or source.
405Wrong method (for example GET /mcp).
413Request body over 256 KB, or context over 50,000 characters.
422The source could not be used (source_unreachable, source_http_error, source_unsupported_type, source_empty, source_redirect_blocked).
429rate_limited, monthly_limit_reached, or temporarily_blocked after repeated failed logins.
500Internal server error.

Client examples

There are no packaged SDKs yet. The API is plain JSON over HTTPS, so any HTTP client works.

Python

import requests

resp = requests.post(
    "https://api.myuiaf.com/api/v1/validate",
    headers={"Authorization": "Bearer fv1.acme-bot.1a2b3c4d.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
    json={
        "context": "The API rate limit is 100 req/min",
        "source_url": "https://example.com/api-docs",
    },
    timeout=30,
)
result = resp.json()
print(result["freshness_score"], result["is_stale"])

TypeScript

const res = await fetch("https://api.myuiaf.com/api/v1/validate", {
  method: "POST",
  headers: {
    Authorization: "Bearer fv1.acme-bot.1a2b3c4d.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    context: "The API rate limit is 100 req/min",
    source_url: "https://example.com/api-docs",
  }),
});
const result = await res.json();
console.log(result.freshness_score, result.is_stale);

Operator endpoints

These are used by the service operator and need the operator key. An agent key receives 401. They are listed so you know how accounts are set up. You do not call them yourself.

EndpointPurpose
POST /api/v1/agents/:agent_id/keysCreates the agent if needed and issues an API key, shown once.
GET /api/v1/agents/:agent_id/keysLists an agent's keys (never the secrets).
DELETE /api/v1/agents/:agent_id/keys/:key_idRevokes a key. The key's own agent may also call it.
PUT /api/v1/agents/:agent_idSets the agent's tier.
POST /api/v1/depositCredits a wallet by hand.
POST /api/v1/webhook/stripeReceives signed Stripe events and credits card payments. Not for agents.

Machine-readable files