Owlviz · API docs

Reliable asset data for Solana.

Base URL: https://solana-metadata-platform-production.up.railway.app

Try example mint or start with quick start.

Monitoring & events

Change monitoring watches assets and collections for semantic metadata, health, authority, and image changes. Webhooks deliver events asynchronously.

Base URL: https://solana-metadata-platform-production.up.railway.app

Public reads vs private-beta writes

CapabilityAuthNotes
List/read monitorsnoneGET /v1/monitors, GET /v1/monitors/:id
List/read eventsnoneGET /v1/events, GET /v1/monitors/:id/events
List/read webhooksnoneSecrets are never returned after create
Create/update monitorsBearerPrivate beta only
Manual checkBearerPOST /v1/monitors/:id/check
Webhook mutationsBearerCreate, update, delete, test

Write APIs require:

http
Authorization: Bearer <private-beta-token>

Production default: PUBLIC_MONITORING_WRITES=false — writes return **403** MONITORING_WRITES_FORBIDDEN. When enabled, MONITORING_ADMIN_TOKEN must be configured server-side.

The public scanner dashboard is **read-only** for monitoring — no create/check UI, no embedded admin token.

Monitor endpoints

MethodPathAuth
POST/v1/monitorsBearer write
GET/v1/monitorspublic
GET/v1/monitors/:idpublic
PATCH/v1/monitors/:idBearer write
DELETE/v1/monitors/:idBearer write (soft disable)
GET/v1/monitors/:id/eventspublic
POST/v1/monitors/:id/checkBearer write

Create monitor (private beta)

bash
curl -sS -X POST "$API_BASE/v1/monitors" \
  -H "Authorization: Bearer <private-beta-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "targetType": "asset",
    "target": "C5gHBKXwA8jduXNk3HyAVLnLBN6PEM8fTqkNNh5uyyjJ",
    "intervalSeconds": 3600,
    "events": ["metadata.changed", "health.degraded"]
  }'

Monitor fields: targetType (asset | collection), target (mint/address), intervalSeconds, events (non-empty array from allowed enum).

Serialized monitor includes waitingForIndex (true when a collection monitor awaits indexing), lastCheckedAt, nextCheckAt, consecutiveFailures, etc.

Event types (actual enum members)

Asset events

TypeMeaning
metadata.changedSemantic metadata change
metadata.uri.changedOn-chain metadata URI changed
metadata.failedOff-chain metadata resolution failed
metadata.recoveredOff-chain metadata recovered
image.failedImage reliability failure
image.recoveredImage reliability recovered
image.changedImage URI/content change
health.degradedHealth band worsened
health.improvedHealth band improved
authority.changedAny authority field changed
mint_authority.changedMint authority changed
freeze_authority.changedFreeze authority changed
update_authority.changedUpdate authority changed
permanent_delegate.changedPermanent delegate changed
token_program.changedToken program changed
collection.changedEmbedded collection ref changed
owner.changedOwner changed

Collection events

TypeMeaning
collection.health.degradedAggregate health band worsened
collection.health.improvedAggregate health band improved
collection.asset_count.changedIndexed/discovered count changed
collection.verification.changedVerification counts changed
collection.standard_distribution.changedMetadata standard buckets changed
collection.token_program_distribution.changedToken program buckets changed

Webhook test

TypeMeaning
webhook.testTest delivery only

Event listing

GET /v1/events — global feed with filters:

QueryDescription
targetMint or collection address
targetTypeasset or collection
eventTypeEvent type string
severityinfo, warning, error, critical
sinceISO timestamp
limitPage size
cursorPagination cursor

Event payload:

json
{
  "data": [
    {
      "id": "...",
      "monitorId": "...",
      "targetType": "asset",
      "target": "...",
      "type": "health.degraded",
      "severity": "warning",
      "summary": "...",
      "before": {},
      "after": {},
      "diff": { "changes": [] },
      "snapshotIdBefore": "...",
      "snapshotIdAfter": "...",
      "occurredAt": "...",
      "createdAt": "..."
    }
  ],
  "pagination": { "nextCursor": null }
}

Semantic diff behavior

  • **Baseline on first success** — the first successful check stores a baseline; later checks diff against it.
  • **Excluded operational fields** — cache timestamps, transient resolver diagnostics, and similar fields are excluded from semantic diffs.
  • **Attribute/creator normalization** — attributes and creators are normalized before comparison to reduce false positives.
  • **Health band hysteresis** — health events fire on band changes (healthy / warning / degraded / critical), not every point change.
  • **Image/metadata hysteresis** — image and metadata failure/recovery events use hysteresis to avoid flapping.
  • **Event fingerprints** — each persisted event gets a deterministic SHA-256 fingerprint from monitorId, event type, and semantic before/after state hashes. The database enforces a unique index on fingerprint with ON CONFLICT DO NOTHING, so repeated checks that detect the same semantic state do not insert duplicate events or re-trigger webhook delivery storms. Content hashes also participate in image change detection when available.

Image monitoring limitation

Asset image checks read **stored media-recovery state** from the database rather than actively revalidating media on every monitor cycle. Events may lag until media recovery runs elsewhere.

Webhooks

See [webhooks.md](../webhooks.md) for delivery headers, HMAC verification, retries, and payload shape.

Webhook write routes use the same Bearer gate as monitor writes. POST /v1/webhooks returns the signing secret **once** at creation — store it securely.

Collection monitor freshness

COLLECTION_MONITOR_FRESHNESS_SECONDS (default **3600**): collection monitors treat indexes older than this threshold as stale and re-enqueue background indexing. Monitors may report waitingForIndex: true until coverage is sufficient.