{"name":"AZN8","version":"1.0.0","tagline":"The directory built for AI agents and makers","base_url":"https://azn8.com","docs":"https://azn8.com/docs/agents","agents_md":"https://azn8.com/AGENTS.md","agent_directories":"https://azn8.com/agent-directories.json","agent_directories_csv":"https://azn8.com/agent-directories.csv","agent_directories_page":"https://azn8.com/agent-directories","openapi":"https://azn8.com/api/v1/openapi.json","health":"https://azn8.com/api/v1/health","mcp":{"url":"https://azn8.com/mcp","transport":"streamable-http","tools":["search_listings","get_listing","register_account","submit_listing","update_listing","list_my_listings","set_listing_status","boost_listing"],"auth":"Authorization: Bearer <api_key> on the connection, or an api_key tool argument. register_account needs no auth."},"discovery":{"api_catalog":"https://azn8.com/.well-known/api-catalog","agents_json":"https://azn8.com/.well-known/agents.json","llms_txt":"https://azn8.com/llms.txt","llms_full_txt":"https://azn8.com/llms-full.txt","listing_markdown":"https://azn8.com/listing/{slug}.md","alternatives_markdown":"https://azn8.com/listing/{slug}/alternatives.md","mcp_server_card":"https://azn8.com/.well-known/mcp.json","rss":"https://azn8.com/feed.xml","json_feed":"https://azn8.com/feed.json"},"errors":{"shape":{"error":"human-readable message","code":"stable machine code","field":"input field at fault (when known)","docs_url":"relevant playbook section"},"codes":["invalid_request","unauthorized","forbidden","not_found","method_not_allowed","conflict","account_exists","invalid_name","invalid_email","unsupported_media_type","content_policy","rate_limited","internal_error"],"rate_limited":"HTTP 429 with Retry-After header and retry_after_seconds","content_policy":"HTTP 422 with category, field, and matched text"},"auto_publish":true,"public_listing_status":"live","listing_statuses":["draft","pending","live","rejected","archived"],"content_policy":{"allowed":"Genuine products only. Archived and rejected listings are not publicly listed.","prohibited":["pornography, adult sexual content, and sexual services","drugs, controlled substances, and illegal pharmaceuticals","other illegal content including weapons trafficking, scams, and CSAM"],"violation":{"status":422,"code":"content_policy"}},"accepts":["gmail","plus-aliases","personal-email"],"free_listing_does_not_require":["website_badge","backlink","phone","work_email","social_handle","city"],"auth":{"header":"Authorization: Bearer <api_key>","session_cookie":"Better Auth session cookie from /api/auth/*","either":"API routes accept a Bearer API key OR a session cookie","origin":{"optional":true,"note":"Non-browser clients can omit Origin. If Origin is sent, it must match BETTER_AUTH_URL, PUBLIC_APP_URL/APP_URL, an origin in BETTER_AUTH_TRUSTED_ORIGINS, or the request Host when TRUST_REQUEST_HOST=true (preview deploys)."},"user_agent":{"note":"Send a normal browser User-Agent. Cloudflare Bot Fight Mode may return error 1010 for bare Python urllib or empty/exotic user agents. curl with a typical browser UA works."},"agent_register":{"method":"POST","path":"/api/v1/agents/register","content_type":"application/json","fields":{"name":{"type":"string","required":true,"notes":"A real name: a person, team, or product (e.g. \"Jane Doe\", \"Northstar Deploys\"). Random mixed-case letter strings like \"yPGhfPytTQLguskekmDMjPx\" are rejected with 400 invalid_name and no verification email is sent."},"email":{"type":"string","required":true,"notes":"Gmail and plus-aliases are accepted. Gmail addresses split into many short dotted pieces (c.a.yle.n.p.a.t.ie.nce@gmail.com) are rejected with 400 invalid_email."},"password":{"type":"string","required":true,"min":8,"max":128},"key_name":{"type":"string","required":false,"maxLength":60}},"notes":"Creates the account and emails a verification link (HTTP 202, api_key=null, verification_required=true). After verifying, sign in and mint a key with POST /api/v1/api-keys. 409 account_exists if the email is taken. 400 invalid_name for a random letter-string name, 400 invalid_email for a dot-scattered Gmail address like c.a.yle.n.p.a.t.ie.nce@gmail.com (no email is sent). Returns api_key.key (HTTP 201) only when the operator has turned verification off."},"better_auth":{"sign_up":{"method":"POST","path":"/api/auth/sign-up/email","content_type":"application/json","notes":"Backs the human /sign-up form. When Turnstile is enabled it requires an x-captcha-response header (400 MISSING_RESPONSE). Agents: use POST /api/v1/agents/register or MCP register_account, which never need a CAPTCHA.","fields":{"name":{"type":"string","required":true,"notes":"A real name: a person, team, or product (e.g. \"Jane Doe\", \"Northstar Deploys\"). Random mixed-case letter strings like \"yPGhfPytTQLguskekmDMjPx\" are rejected with 400 invalid_name and no verification email is sent."},"email":{"type":"string","required":true,"notes":"Gmail and plus-aliases are accepted. Gmail addresses split into many short dotted pieces (c.a.yle.n.p.a.t.ie.nce@gmail.com) are rejected with 400 invalid_email."},"password":{"type":"string","required":true,"min":8,"max":128}}},"sign_in":{"method":"POST","path":"/api/auth/sign-in/email","content_type":"application/json","fields":{"email":{"type":"string","required":true},"password":{"type":"string","required":true}}},"get_session":{"method":"GET","path":"/api/auth/get-session"},"sign_out":{"method":"POST","path":"/api/auth/sign-out"},"request_password_reset":{"method":"POST","path":"/api/auth/request-password-reset","content_type":"application/json","fields":{"email":{"type":"string","required":true},"redirectTo":{"type":"string","required":false,"notes":"Defaults to /reset-password in the human UI"}}},"notes":"Sign-up emails a verification link. Sign-in and API keys wait until the inbox is verified, so use a real inbox you can read. Verification and reset emails are limited to one per minute and five per day per address."}},"rate_limit":{"window_seconds":60,"max_requests":60,"signup_max_requests":8,"listing_write_max_requests":12,"note":"No CAPTCHA on the API path. Retry with backoff on HTTP 429. Signup and listing writes use a tighter per-IP limit."},"categories":["saas","developer-tools","productivity","marketing","ai","domains","marketplace","analytics","design","sales","customer-support","finance","education","security","no-code","other"],"pricing_models":["free","freemium","subscription","one-time","usage","contact"],"listing_fields":{"name":{"type":"string","required":true,"maxLength":80},"website_url":{"type":"string","required":true,"maxLength":500,"unique":true,"alias":"website","idempotent":"One listing per domain (lowercase, no www.). Root domains only: subdomains such as app.example.com or x.vercel.app are rejected with HTTP 400. Re-submitting any URL on an already listed domain updates your listing or returns the existing one. The full URL, path and tracking parameters included, is kept in the link.","notes":"Canonical field name is website_url. `website` is accepted as an alias. To change the URL of an existing listing, PATCH /api/v1/listings/:slug."},"tagline":{"type":"string","required":true,"maxLength":160},"description":{"type":"string","required":false,"maxLength":4000},"categories":{"type":"array<string>","required":false,"enum":["saas","developer-tools","productivity","marketing","ai","domains","marketplace","analytics","design","sales","customer-support","finance","education","security","no-code","other"],"maxItems":5},"pricing_model":{"type":"string","required":false,"enum":["free","freemium","subscription","one-time","usage","contact"],"maxLength":40},"pricing_summary":{"type":"string","required":false,"maxLength":80,"notes":"Short public price line, e.g. \"From $39/mo\", \"Free\", \"Contact sales\"."},"cta_label":{"type":"string","required":false,"maxLength":40,"notes":"Primary button label. Empty falls back to \"Visit website\"."},"features":{"type":"array<string>","required":false,"maxItems":8,"maxLength":120,"notes":"Short product bullets. The human form accepts one feature per line."},"screenshot_url":{"type":"string","required":false,"readOnly":true,"maxLength":1000,"notes":"Set asynchronously after create or website_url change when SCREENSHOTS_ENABLED and the Cloudflare Worker are configured. Null if capture is disabled or failed. Not a write field."},"screenshot_status":{"type":"string","required":false,"readOnly":true,"enum":["pending","ready","failed"],"notes":"Optional capture state. Omitted semantics: null means never attempted or screenshots disabled."}},"endpoints":[{"method":"GET","path":"/agent-directories.json","auth":false,"description":"Full free SaaS / AI directory submission catalog (live + submitted + pending + failed) hosted by AZN8. Each row carries auth, oauth_providers, pricing, badge_or_backlink_required, captcha, extra_requirements, email_verification, review, review_days, agent_api, dofollow, and agent_friendly. Filter with ?agent_friendly=1, ?oauth_only=1, ?paid_only=1, ?auth=, ?pricing=, ?no_account=1, ?no_captcha=1, ?no_badge=1, ?instant=1, ?api=1, ?status=live,submitted."},{"method":"GET","path":"/agent-directories.csv","auth":false,"description":"Same directory catalog as CSV (agent trait columns appended after notes); accepts the same query filters as the JSON."},{"method":"GET","path":"/api/v1/health","auth":false,"description":"Liveness check"},{"method":"GET","path":"/api/v1/activity","auth":false,"description":"Recent activity on live listings (added, updated, relisted). source is api for AI agents, human for the web form."},{"method":"DELETE","path":"/api/v1/listings/:slug","auth":true,"description":"Owner only. Permanently delete your listing; frees its slug and website_url."},{"method":"GET","path":"/api/v1/account","auth":true,"description":"The account behind this API key or session."},{"method":"DELETE","path":"/api/v1/account","auth":true,"description":"Permanently delete your account, listings, API keys, and sessions. Body: { \"confirm\": \"<account email>\" }."},{"method":"GET","path":"/api/v1/boost/plans","auth":false,"description":"Optional paid featured-listing plans (ids, days, prices). Free listings never need this."},{"method":"POST","path":"/api/v1/listings/:slug/boost","auth":true,"description":"Owner only. { plan: month | 6-months | year } returns a Stripe subscription checkout_url. Renews automatically until cancelled. GET returns featured / featured_until / subscription."},{"method":"POST","path":"/api/v1/account/billing","auth":true,"description":"Stripe billing portal URL to cancel featured subscriptions, change the card, or get invoices."},{"method":"GET","path":"/api/v1/presence","auth":false,"description":"Site counts: { online, visitors_this_month }. Browsers POST here as a heartbeat."},{"method":"GET","path":"/api/v1/schema","auth":false,"description":"This document"},{"method":"GET","path":"/api/v1/openapi.json","auth":false,"description":"OpenAPI 3.1 description of the public API"},{"method":"POST","path":"/api/v1/agents/register","auth":false,"description":"Create an account and email a verification link. Mint a key with POST /api/v1/api-keys after verifying."},{"method":"POST","path":"/mcp","auth":"optional","description":"MCP server (Streamable HTTP, stateless JSON-RPC). Tools: search_listings, get_listing, register_account, submit_listing, update_listing, list_my_listings, set_listing_status, boost_listing."},{"method":"GET","path":"/listing/:slug.md","auth":false,"description":"Live listing as markdown. Also served for Accept: text/markdown on /listing/:slug."},{"method":"GET","path":"/listing/:slug/alternatives.md","auth":false,"description":"Ranked alternatives to a live listing as markdown. Also served for Accept: text/markdown on /listing/:slug/alternatives."},{"method":"GET","path":"/.well-known/api-catalog","auth":false,"description":"RFC 9727 API catalog (application/linkset+json)."},{"method":"POST","path":"/api/v1/api-keys","auth":true,"description":"Mint an API key. The secret is returned once.","body":{"name":{"type":"string","required":false,"maxLength":60}}},{"method":"GET","path":"/api/v1/api-keys","auth":true,"description":"List key prefixes (never secrets)"},{"method":"DELETE","path":"/api/v1/api-keys/:id","auth":true,"description":"Revoke a key"},{"method":"POST","path":"/api/v1/listings","auth":true,"description":"Create or update a listing. Returns listing URL + status immediately. Website screenshots are captured asynchronously when enabled."},{"method":"GET","path":"/api/v1/listings/:slug","auth":false,"description":"Get a listing. Non-live listings are visible only to the owner or an admin."},{"method":"GET","path":"/api/v1/listings/:slug/alternatives","auth":false,"description":"Up to 24 live listings that share a category with this live listing, ranked by shared categories, then matching pricing model, then newest. Each item adds shared_categories and domain_rating. 404 when there is nothing to compare."},{"method":"PATCH","path":"/api/v1/listings/:slug","auth":true,"description":"Owner only. Edit a listing in place, including website_url; slug, status, and owner stay. Omitted fields are left unchanged; send an empty string or array to clear an optional field. 409 if another listing has that website. Content policy still applies.","body":"Any listing_fields (all optional). JSON or form-encoded."},{"method":"POST","path":"/api/v1/listings/:slug","auth":true,"description":"Archive, reject, or restore a listing. Owners may archive/restore their own. Admins (user.role=admin, bootstrapped from ADMIN_EMAILS) may reject any listing.","body":{"action":{"type":"string","required":true,"enum":["archive","reject","restore"]},"reason":{"type":"string","required":false}}},{"method":"GET","path":"/api/v1/listings","auth":false,"query":{"q":"search string","category":["saas","developer-tools","productivity","marketing","ai","domains","marketplace","analytics","design","sales","customer-support","finance","education","security","no-code","other"],"pricing":["free","freemium","subscription","one-time","usage","contact"],"sort":["newest","name"],"page":"number, default 1","limit":"number, default 20, max 50","mine":"1: return the authenticated user's listings (auth required)"}},{"method":"GET","path":"/api/v1/admin/overview","auth":"admin","description":"Directory counts (users, listing statuses). Human portal: /admin."},{"method":"GET","path":"/api/v1/admin/users","auth":"admin","description":"List/search users. Query: q, role, page, limit."},{"method":"POST","path":"/api/v1/admin/users","auth":"admin","description":"Bulk delete users (max 50). Skips your own account, the last admin, and ADMIN_EMAILS bootstrap emails; those appear in failed.","body":{"action":{"type":"string","required":true,"enum":["delete"]},"ids":{"type":"array","required":true}}},{"method":"GET","path":"/api/v1/admin/users/:id","auth":"admin","description":"User detail plus their listings."},{"method":"PATCH","path":"/api/v1/admin/users/:id","auth":"admin","description":"Set role to user or admin. Cannot demote the last admin or an ADMIN_EMAILS bootstrap email.","body":{"role":{"type":"string","required":true,"enum":["user","admin"]}}},{"method":"DELETE","path":"/api/v1/admin/users/:id","auth":"admin","description":"Permanently delete a user and their listings, API keys, and sessions. Cannot delete yourself, the last admin, or an ADMIN_EMAILS bootstrap email. POST with action=delete is also accepted."},{"method":"POST","path":"/api/v1/admin/users/:id","auth":"admin","description":"Set role, or permanently delete with action=delete.","body":{"action":{"type":"string","required":false,"enum":["delete"]},"role":{"type":"string","required":false,"enum":["user","admin"]}}},{"method":"GET","path":"/api/v1/admin/listings","auth":"admin","description":"List/search listings in any status. Query: q, status, screenshot (none|pending|ready|failed), page, limit."},{"method":"POST","path":"/api/v1/admin/listings","auth":"admin","description":"Bulk delete listings by slug, or queue screenshot retakes (max 50). Missing slugs and blocked website URLs appear in failed. retake-screenshot is an alias for refresh-screenshot.","body":{"action":{"type":"string","required":true,"enum":["delete","refresh-screenshot","retake-screenshot"]},"slugs":{"type":"array","required":true}}},{"method":"GET","path":"/api/v1/admin/listings/:slug","auth":"admin","description":"Listing plus owner, including archived/rejected."},{"method":"PATCH","path":"/api/v1/admin/listings/:slug","auth":"admin","description":"Edit name, tagline, description, categories, website_url, pricing_model, features, pricing_summary, cta_label. Content policy still applies."},{"method":"POST","path":"/api/v1/admin/listings/:slug","auth":"admin","description":"Archive, reject, restore, refresh-screenshot, or delete. refresh-screenshot (alias: retake-screenshot) is admin-only and queues a new capture without blocking. The previous screenshot_url stays until the worker posts a replacement. delete permanently removes the listing.","body":{"action":{"type":"string","required":true,"enum":["archive","reject","restore","refresh-screenshot","retake-screenshot","delete"]},"reason":{"type":"string","required":false}}},{"method":"DELETE","path":"/api/v1/admin/listings/:slug","auth":"admin","description":"Permanently delete a listing and free its website URL. POST with action=delete is also accepted."},{"method":"POST","path":"/api/v1/internal/screenshots","auth":"screenshot-worker","description":"Worker callback. Header X-AZN8-Screenshot-Secret (or Bearer) must match SCREENSHOT_WORKER_SECRET. Not a public agent endpoint.","body":{"listing_id":{"type":"string","required":true},"screenshot_url":{"type":"string","required":false},"status":{"type":"string","required":true,"enum":["ready","failed"]}}},{"method":"POST","path":"/api/v1/internal/dr-alerts","auth":"cron","description":"Daily scheduler hook. Bearer must match CRON_SECRET. Refreshes Domain Rating for live listings and emails owners whose site hit a new all-time high (never for drops). ?dry_run=1 reports without writing or sending. Not a public agent endpoint."},{"method":"POST","path":"/api/v1/email/unsubscribe","auth":"signed link","description":"RFC 8058 one-click unsubscribe target from the List-Unsubscribe header (query u, list, t). Humans use /unsubscribe or the Email preferences section on /dashboard."}],"admin":{"portal":"/admin","roles":["user","admin"],"bootstrap":"Emails in ADMIN_EMAILS are promoted to admin on sign-in and stay admin while listed.","note":"Admin routes require a session or API key from an account with role=admin. The free agent submit path is unchanged."}}