Back to partner brief

Engineering handoff

host39 integration guide

Tier 1–3 wiring, API contracts, credentials, and pilot runbook. API base: https://api.rora.carmel.so

Start here

host39 engineering — Tier 1 first.

Immediate next steps

  1. Wire Tier 1 checkbox → POST webhook (we send NANDA_WEBHOOK_SECRET)
  2. Render response.reliability in AgentFacts / card UI
  3. Use GET …/registration as fallback if webhook times out
  4. Add report_url link (Tier 2) — ready when Tier 1 works

host39 users should not create Agent Status accounts at register time. Implement tiers in order: Tier 1 first, then add Tier 2 links, then Tier 3 for power users. Checklists show a progress bar plus Done / Todo badges for each item. The pilot runbook below is the engineering path to Tier 1 — resolve smoke test first, then webhook wiring.

Integration tiers

Tier 1 → 2 → 3.

Tier 1

Agent Status shipped — waiting on host39 wiring

Reliability field in host39 (no Agent Status login)

User checks “Enable reliability monitoring” on host39. host39 fires webhook; Agent Status starts probes. host39 polls status API and renders verdict + uptime in the card / AgentFacts reliability field. User never leaves host39.

host39 — todos

0 / 6 done
  • TodoAdd checkbox on card publish: “Enable reliability monitoring”
  • TodoOn save → POST …/webhooks/card-published (event: monitoring.enabled, enable_monitoring: true)
  • TodoFallback: poll GET …/registration until monitoring_state = active
  • TodoPoll GET …/status every 1–5 min → copy response.reliability into AgentFacts
  • TodoOptional: POST …/agents/validate on checkbox toggle (preview before save)
  • TodoConfirm probe auth + send 10–20 pilot URNs

Agent Status — todos

5 / 5 done
  • DoneWebhook onboard + monitoring.disabled pause
  • DoneRegistration + status APIs with reliability block
  • DonePre-flight validate endpoint (POST …/agents/validate)
  • Done26 automated tests passing
  • DoneLive on api.rora.carmel.so + secrets shared with host39

APIs

  • POST https://api.rora.carmel.so/api/partner/nanda/webhooks/card-published
  • POST https://api.rora.carmel.so/api/partner/nanda/agents/validate
  • GET https://api.rora.carmel.so/api/partner/nanda/agents/{locator}/registration
  • GET https://api.rora.carmel.so/api/partner/nanda/agents/{locator}/status

reliability field (copy into AgentFacts)

{
  "provider": "agentstatus.dev",
  "monitoring": "active",
  "verdict": "UP",
  "status": "operational",
  "uptime_pct": 98.2,
  "pass_rate": 0.94,
  "last_checked_at": "2026-07-06T17:30:00Z",
  "reliability_label": "Operational",
  "report_url": "https://agentstatus.dev/nanda/status?locator=...",
  "badge_url": "https://api.rora.carmel.so/.../badge.svg",
  "claim_url": "https://agentstatus.dev/nanda/claim?token=..."
}

Tier 2

Agent Status shipped — host39 adds links

Public report + badge embed

host39 shows “View full report” (report_url) and optional SVG badge. User opens read-only report — no login.

host39 — todos

0 / 3 done
  • TodoAdd link: reliability.report_url → “View full report”
  • TodoOptional embed: <script src="https://api.rora.carmel.so/static/widget/nanda_badge.js" data-locator="…">
  • TodoOptional: <img src="reliability.badge_url"> in card preview

Agent Status — todos

4 / 4 done
  • DonePublic report page: agentstatus.dev/nanda/status?locator={URN}
  • DonePublic status API: GET …/status/public (no auth)
  • DoneSVG badge: GET …/badge.svg
  • DoneEmbed script: static/widget/nanda_badge.js

APIs

  • GET https://api.rora.carmel.so/api/partner/nanda/agents/{locator}/status/public
  • GET https://api.rora.carmel.so/api/partner/nanda/agents/{locator}/badge.svg
  • https://agentstatus.dev/nanda/status?locator={URN}
  • https://api.rora.carmel.so/static/widget/nanda_badge.js

Tier 3

Agent Status shipped — host39 adds claim_url link

Full dashboard — opt-in

Power users click claim_url → validate token → open dashboard for alerts, drift, studio. Not required at register.

host39 — todos

0 / 3 done
  • TodoAdd link: reliability.claim_url → “Manage alerts & metrics”
  • TodoPass owner_email in webhook when available
  • TodoDo not require Agent Status signup at checkbox time

Agent Status — todos

6 / 6 done
  • DoneClaim token issued on webhook create (30-day expiry)
  • DoneGET …/claim/{token} — validate + dashboard URLs
  • DoneClaim landing: agentstatus.dev/nanda/claim?token=…
  • DonePOST …/claim/{token}/redeem + GET …/start magic handoff
  • DoneAuto-attach on signup/verify via nanda_claim_token
  • DoneEmail invite when owner_email provided on webhook

APIs

  • claim_url in webhook + status responses
  • GET https://api.rora.carmel.so/api/partner/nanda/claim/{token}
  • POST https://api.rora.carmel.so/api/partner/nanda/claim/{token}/redeem
  • https://agentstatus.dev/nanda/claim?token={token}
  • https://platform.agentstatus.dev/rora/agents/{agent_id}

Credentials — who generates what

ItemGenerated byShared withTierNotes
NANDA_INDEX_BASE_URLAgent Status (config)Tier 1https://api.nandaindex.org — public resolve endpoint, no secret.
NANDA_WEBHOOK_SECRETAgent Statushost39Tier 1HMAC secret — host39 signs webhook body: X-Nanda-Signature: sha256=…
NANDA_STATUS_API_TOKENAgent Statushost39Tier 1Read token — header X-Nanda-Api-Token on registration + status polls.
Pilot URNs (10–20)NANDA / host39Agent StatusTier 1Live locators with published host39 cards.

Quick curl — webhook + status poll

# Tier 1 — enable monitoring (replace SECRET; agent_card_url = live host39 card JSON)
BODY='{"event":"monitoring.enabled","locator":"urn:ai:domain:moonbakery39.com:agent:orders","enable_monitoring":true,"agent_card_url":"https://agentcards.host39.org/moonbakery39.com/orders.json","display_name":"Moon Bakery Orders Agent","status":"active"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$NANDA_WEBHOOK_SECRET" | awk '{print $2}')

curl -sS -w "\nHTTP %{http_code}\n" -X POST "https://api.rora.carmel.so/api/partner/nanda/webhooks/card-published" \
  -H "Content-Type: application/json" \
  -H "X-Nanda-Signature: sha256=$SIG" \
  -d "$BODY" | jq .
curl -sS -w "\nHTTP %{http_code}\n" \
  "https://api.rora.carmel.so/api/partner/nanda/agents/urn:ai:domain:moonbakery39.com:agent:orders/status" \
  -H "X-Nanda-Api-Token: $NANDA_STATUS_API_TOKEN" | jq .

Resolve flow

Four hops — live demo URN.

Confirmed against production using urn:ai:domain:moonbakery39.com:agent:orders (May 2026). Same four hops as nandaindex.org/resolve.

1

NANDA Index resolve

GET https://api.nandaindex.org/api/v1/resolve?locator=urn%3Aai%3Adomain%3Amoonbakery39.com%3Aagent%3Aorders

{
  "locator": "urn:ai:domain:moonbakery39.com:agent:orders",
  "identifier": "orders",
  "index_record": {
    "registry_url": "https://agentcards.host39.org/moonbakery39.com/orders.json",
    "media_type": "application/a2a-agent-card+json",
    "identifier": "urn:ai:domain:moonbakery39.com:agent:orders",
    "ttl_seconds": 86400,
    "display_name": "Moon Bakery",
    "status": "active"
  }
}

When media_type contains agent-card, registry_url is the direct card JSON URL (host39 SMB path). Re-resolve after ttl_seconds expires.

2

Agent source (host39)

GET https://agentcards.host39.org/moonbakery39.com/orders.json

{
  "name": "Moon Bakery Orders Agent",
  "url": "https://time.moonbakery39.com",
  "authentication": { "schemes": ["Bearer"] },
  "capabilities": { "streaming": false, "pushNotifications": false },
  "_meta": {
    "identifier": "urn:ai:domain:moonbakery39.com:agent:orders",
    "publicUrl": "https://agentcards.host39.org/moonbakery39.com/orders.json",
    "hostedBy": "host39.org"
  }
}

Agent Status reads url as the runtime probe target. name and authentication inform dashboard display and future scoped probes.

3

Runtime agent card

GET https://time.moonbakery39.com

{
  "name": "Time Agent",
  "url": "https://time.moonbakery39.com",
  "authentication": { "schemes": ["none"] },
  "skills": [{
    "id": "get-time",
    "name": "getTime",
    "inputModes": ["text"],
    "outputModes": ["text"]
  }]
}

Optional when hop 2 JSON already contains a usable url. Demo agent serves a time skill at the same base URL.

4

Runtime invocation (outside-in probe)

POST https://time.moonbakery39.com/run

{
  "message": {
    "role": "user",
    "parts": [{ "type": "text", "text": "what time is it?" }]
  }
}

Mapped to Rora request_format a2a_jsonrpc. Verdict aggregated across 2,500+ residential probe nodes.

Partner APIs

Agent Status endpoints.

Card published webhook (Tier 1)

host39 → Agent Status

POST https://api.rora.carmel.so/api/partner/nanda/webhooks/card-published

X-Nanda-Signature: sha256=<hmac-sha256-hex of raw body>

{
  "event": "monitoring.enabled",
  "locator": "urn:ai:domain:moonbakery39.com:agent:orders",
  "enable_monitoring": true,
  "agent_card_url": "https://agentcards.host39.org/moonbakery39.com/orders.json",
  "display_name": "Moon Bakery Orders Agent",
  "status": "active",
  "owner_email": "owner@example.com"
}

202 Accepted — resolves URN, creates/updates monitoring record, returns reliability + claim_url. Also supports monitoring.disabled and card.deleted.

Status + registration (Tier 1 poll)

host39 → Agent Status

GET https://api.rora.carmel.so/api/partner/nanda/agents/{locator}/status GET https://api.rora.carmel.so/api/partner/nanda/agents/{locator}/registration

X-Nanda-Api-Token: <NANDA_STATUS_API_TOKEN>

// status response includes:
{
  "locator": "urn:ai:…",
  "verdict": "UP",
  "reliability": { "provider": "agentstatus.dev", "uptime_pct": 98.2, ... }
}

Use registration as fallback when webhook times out. Poll status every 1–5 min for AgentFacts.

Open with NANDA / host39

  • Rate limits on resolve + card fetch at scale
  • Probe auth: confirm scoped credentials for pilot URNs
  • Enterprise registry hop-2 patterns (/agents/{id} vs AI catalog document)

Dashboard access

Dashboard access — who sees what

Webhook onboarding (Tier 1) does not create Agent Status dashboard logins. The partner APIs are the Tier 1 surface for host39.

AudienceAccess
host39 card UI (Tier 1)Partner registration + status APIs only — no Agent Status login
Public visitors (Tier 2)report_url and GET …/status/public — no login
Individual card owner (Tier 3)reliability.claim_url → signup at platform.agentstatus.dev → dashboard scoped to that one agent (redeem attaches agent to their account)
NANDA / host39 pilot ops (fleet view)Not automatic from the webhook. Agent Status provisions a shared pilot ops login at platform.agentstatus.dev so ops can list all webhook-onboarded pilot agents. Request access from Agent Status — do not use the public signup flow for fleet visibility.

Tier 3 claim is per-agent. A separate pilot ops account is required to see the full pilot fleet in one dashboard view.

Acceptance test

Tier 1–3 E2E — how we measure success

Tier 1 is done when steps 1–6 pass on the demo locator (moonbakery39 orders). Tier 2 adds steps 7–8 and 10–12 (public report + host39 links). Tier 3 adds steps 9 and 13–16 (claim → dashboard for one agent). Agent Status ran steps 1–10 and claim API (9) live on 2026-07-06; host39 must pass 11–12 and 16 before Tier 2/3 are declared wired.

Demo locator: urn:ai:domain:moonbakery39.com:agent:orders. Tier 1: steps 1–6 · Tier 2: steps 7–8, 10–12 (11–12 host39) · Tier 3: steps 9, 13–16 (16 host39).

Prerequisites — Agent Status

  • Partner API live at api.rora.carmel.so
  • Webhook secret + status token shared with host39 (out of band)

Prerequisites — NANDA / host39

  • 5–10 pilot URNs with live host39 cards
  • Webhook handler with HMAC signature verification
  • Status poll or webhook fallback for registration state
1

NANDA Index resolve

Confirm the locator resolves in the public Index.

LOCATOR="urn:ai:domain:moonbakery39.com:agent:orders"
curl -sS -w "\nHTTP %{http_code}\n" \
  "https://api.nandaindex.org/api/v1/resolve?locator=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$LOCATOR'))")" | jq .

Verify

  • HTTP 200
  • Response includes index_record with registry_url or card_url
  • Note: Index registry_url may lag card moves — always fetch the live card URL host39 serves (step 2)
2

Fetch agent card (host39)

Load the published card JSON — this is what Agent Status probes.

curl -sS -w "\nHTTP %{http_code}\n" \
  "https://agentcards.host39.org/moonbakery39.com/orders.json" | jq .

Verify

  • HTTP 200
  • Card JSON includes url (runtime base) and name
  • Card _meta.identifier matches the URN locator
3

Runtime smoke (outside-in)

Confirm the runtime accepts an A2A-style invocation.

curl -sS -w "\nHTTP %{http_code}\n" -X POST "https://time.moonbakery39.com/run" \
  -H "Content-Type: application/json" \
  -d '{"message":{"role":"user","parts":[{"type":"text","text":"what time is it?"}]}}'

Verify

  • HTTP 200 (or structured auth error — confirms reachability)
  • Response body is non-empty JSON
4

Signed webhook — enable monitoring

Tier 1 onboarding path. host39 fires this on checkbox save.

# Tier 1 — enable monitoring (replace SECRET; agent_card_url = live host39 card JSON)
BODY='{"event":"monitoring.enabled","locator":"urn:ai:domain:moonbakery39.com:agent:orders","enable_monitoring":true,"agent_card_url":"https://agentcards.host39.org/moonbakery39.com/orders.json","display_name":"Moon Bakery Orders Agent","status":"active"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$NANDA_WEBHOOK_SECRET" | awk '{print $2}')

curl -sS -w "\nHTTP %{http_code}\n" -X POST "https://api.rora.carmel.so/api/partner/nanda/webhooks/card-published" \
  -H "Content-Type: application/json" \
  -H "X-Nanda-Signature: sha256=$SIG" \
  -d "$BODY" | jq .

Verify

  • HTTP 202 Accepted
  • Response includes agent_id and reliability block
  • reliability.claim_url present when owner_email omitted (Tier 3 opt-in later)
5

Registration poll (fallback gate)

Use when webhook succeeds but UI needs explicit registration state.

curl -sS -w "\nHTTP %{http_code}\n" \
  "https://api.rora.carmel.so/api/partner/nanda/agents/urn:ai:domain:moonbakery39.com:agent:orders/registration" \
  -H "X-Nanda-Api-Token: $NANDA_STATUS_API_TOKEN" | jq .

Verify

  • monitoring_state is registered or active
  • probe_state may be pending_first_probe until the first scheduled probe (~60 min)
6

Status poll → AgentFacts reliability field

Copy response.reliability into host39 card UI every 1–5 minutes.

curl -sS -w "\nHTTP %{http_code}\n" \
  "https://api.rora.carmel.so/api/partner/nanda/agents/urn:ai:domain:moonbakery39.com:agent:orders/status" \
  -H "X-Nanda-Api-Token: $NANDA_STATUS_API_TOKEN" | jq .

Verify

  • reliability.provider = agentstatus.dev
  • reliability.verdict + reliability_label present
  • reliability.report_url and reliability.badge_url populated
7

Public status API (Tier 2 gate)

No auth — same reliability shape for public report pages.

curl -sS -w "\nHTTP %{http_code}\n" \
  "https://api.rora.carmel.so/api/partner/nanda/agents/urn:ai:domain:moonbakery39.com:agent:orders/status/public" | jq .reliability

Verify

  • HTTP 200 without X-Nanda-Api-Token
  • reliability block matches authenticated status poll
8

Badge SVG (Tier 2 gate)

Optional embed; shows Pending until first probe completes.

curl -sS -w "\nHTTP %{http_code}\n" \
  "https://api.rora.carmel.so/api/partner/nanda/agents/urn:ai:domain:moonbakery39.com:agent:orders/badge.svg" \
  -o /tmp/nanda-badge.svg && head -c 200 /tmp/nanda-badge.svg

Verify

  • HTTP 200, Content-Type image/svg+xml
  • SVG renders verdict label (e.g. Pending, UP)
9

Claim token validate — API (Tier 3 gate)

API check before the browser claim flow (steps 13–16). Token comes from step 6 reliability.claim_url.

# Copy token from reliability.claim_url (step 6) — URL ends with ?token=…
TOKEN="{claim_token}"
curl -sS "https://api.rora.carmel.so/api/partner/nanda/claim/$TOKEN" | jq .

Verify

  • valid: true
  • Response includes agent_id, signup_url, dashboard_url, redeem_url
  • claim_url in status poll matches agentstatus.dev/nanda/claim?token=…
10

Public report page (Tier 2 gate)

Open reliability.report_url from step 6 in a browser — same page host39 will link to. No login.

# Same URL as reliability.report_url from step 6 — open in browser (no login)
open "https://agentstatus.dev/nanda/status?locator=urn%3Aai%3Adomain%3Amoonbakery39.com%3Aagent%3Aorders"

# Smoke: public report page loads (same URL as reliability.report_url)
curl -sS -o /dev/null -w "HTTP %{http_code}\n" \
  "https://agentstatus.dev/nanda/status?locator=urn%3Aai%3Adomain%3Amoonbakery39.com%3Aagent%3Aorders"

Verify

  • HTTP 200
  • Page shows agent name, URN, verdict / reliability label, and last checked time
  • URL matches reliability.report_url from the status poll exactly
11

host39 — “View full report” link (Tier 2 gate)

Wire the card UI to reliability.report_url from the status poll. Link must open the same page as step 10.

{# Use reliability.report_url from status poll — do not hardcode #}
<a href="{{ reliability.report_url }}" target="_blank" rel="noopener noreferrer">
  View full report
</a>

Verify

  • “View full report” visible on the host39 card / AgentFacts surface
  • href equals reliability.report_url (not a hardcoded or different URL)
  • Opens agentstatus.dev/nanda/status?locator=… in a new tab
12

host39 — badge on card (Tier 2 optional)

Optional SVG badge next to the reliability field — img from poll or live widget.

{# Option A — img from status poll (simplest) #}
<a href="{{ reliability.report_url }}" target="_blank" rel="noopener noreferrer">
  <img src="{{ reliability.badge_url }}" alt="Agent reliability status" height="28" />
</a>

{# Option B — live widget (auto-links to report_url) #}
<script
  src="https://api.rora.carmel.so/static/widget/nanda_badge.js"
  data-locator="urn:ai:domain:moonbakery39.com:agent:orders"
  data-report-url="https://agentstatus.dev/nanda/status?locator=urn%3Aai%3Adomain%3Amoonbakery39.com%3Aagent%3Aorders"
  async
></script>

Verify

  • Badge renders on the host39 card (img or widget)
  • Badge label matches current verdict (e.g. Pending, UP, Client error)
  • Clicking badge opens the same report URL as step 10
13

Claim landing page (Tier 3 gate)

Open reliability.claim_url from step 6 in a browser — public claim preview before signup.

# Copy full URL from: jq -r .reliability.claim_url  (step 6)
# Shape: https://agentstatus.dev/nanda/claim?token={claim_token}
open "https://agentstatus.dev/nanda/claim?token={claim_token}"

Verify

  • Page loads without login
  • Shows agent name, locator, and current reliability label
  • Continue / signup links present (not a 404 or expired token)
14

Signup or login with claim handoff

New user: complete signup at platform.agentstatus.dev with nanda_claim_token prefilled. Existing user: log in then redeem.

# From step 9 response — new user signup URL
# platform.agentstatus.dev/auth/signup?source=nanda&claim_token=…&locator=…

# Existing logged-in user — attach agent (Bearer session token)
curl -sS -X POST "https://api.rora.carmel.so/api/partner/nanda/claim/{claim_token}/redeem" \
  -H "Authorization: Bearer $RORA_SESSION_TOKEN" | jq .

Verify

  • Signup verify completes with nanda_claim_token (auto-redeem on verify)
  • OR POST …/redeem returns ok: true for an existing session
  • Do not use the pilot ops account (nanda@…) for this gate — that sees the fleet, not a single claim
15

Dashboard — one agent attached

Redeemer's account sees exactly the claimed agent — alerts, drift, full metrics. Not the full pilot fleet.

# From step 9 response — dashboard deep link
# platform.agentstatus.dev/rora/agents/{agent_id}?source=nanda&locator=…

curl -sS "https://platform.agentstatus.dev/api/rora/agents" \
  -H "Authorization: Bearer $RORA_SESSION_TOKEN" | jq '.agents | map({id, name})'

Verify

  • Dashboard opens to the claimed agent (agent_id from step 9)
  • GET /api/rora/agents lists this agent under the redeemer's rora_signup_id
  • Agent does not appear on a different user's account until they claim separately
16

host39 — claim link + optional owner_email (Tier 3 gate)

Wire claim_url on the card for power users. Pass owner_email on webhook when known for magic invite.

{# Use reliability.claim_url from status poll — not dashboard URL directly #}
<a href="{{ reliability.claim_url }}" target="_blank" rel="noopener noreferrer">
  Manage alerts &amp; metrics
</a>

# Webhook — optional owner invite
# Optional on webhook — enables magic invite for card owner (Tier 3)
{
  "event": "monitoring.enabled",
  "locator": "urn:ai:domain:moonbakery39.com:agent:orders",
  "enable_monitoring": true,
  "agent_card_url": "https://agentcards.host39.org/moonbakery39.com/orders.json",
  "owner_email": "owner@example.com"
}

Verify

  • “Manage alerts & metrics” (or similar) links to reliability.claim_url
  • Tier 1 checkbox still does not require Agent Status signup
  • When owner_email is sent, GET …/claim/{token}/start redirects owner toward signup/login with token prefilled

Tier 1 delivery checklist

Agent Status engineering

4 / 5 done
  • DonePartner API live on api.rora.carmel.so
  • DonePartner credentials issued to host39 (webhook secret + status token)
  • DoneResolve client + scheduler TTL re-resolve
  • DoneTier 2 report + badge and Tier 3 claim flow shipped (API + pages)
  • TodoPilot: 10–20 host39 URNs onboarded via Tier 1 webhook

NANDA / host39 delivers

1 / 4 done
  • TodoTier 1 checkbox → webhook wired
  • Todoreliability field rendered in AgentFacts / card UI
  • TodoList of pilot URNs with live cards
  • DoneDemo URN validated (moonbakery orders)

Success criteria (after E2E passes)

CheckTarget
Tier 1 E2E (steps 1–6)Pass on moonbakery39 orders demo URN
Tier 2 E2E (steps 7–8, 10–12)Public report + host39 View full report (+ optional badge)
Tier 3 E2E (steps 9, 13–16)Claim flow + host39 Manage alerts link (+ optional owner_email)
host39 Tier 1 UIreliability field renders from status poll
Pilot agents onboarded≥ 10 URNs via webhook
Observation window≥ 7 days
Probe runs per agent≥ 15 (medium confidence)
Re-resolve on TTLNo stale endpoint after card edit

Rollback

  • Pause via webhook: event: monitoring.disabled
  • Or pause agent: PATCH /api/rora/agents/{id} status: paused
  • No NANDA-side changes required for rollback

Partner API lives on https://api.rora.carmel.so. Webhook URL: POST https://api.rora.carmel.so/api/partner/nanda/webhooks/card-published. Secrets shared out-of-band with host39 engineering.