Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Surfing Dog Search Score API
version: '1'
license:
name: ODbL-1.0 (the directory data; town names and centres © GeoNames, CC BY 4.0)
url: https://opendatacommons.org/licenses/odbl/1-0/
summary: A public directory of businesses an AI can ask, book or buy from, through their own inboxes and other agent doors.
description: 'Everything here is a public read, no sign-in, the same answer for everyone (a free directory key only lets a list page deeper than 200 places), except two calls about door reports: POST /v1/door-reports, where an agent says how a business''s door went, and GET /v1/door-reports/me, signed, which answers the signer''s own reporting credit. Signed reads may count against a contributor''s own allowance; the answers are the same. The directory''s order is published at GET /v1/ranking and cannot be bought; every query parameter only leaves businesses out. This API takes no orders or payments and books nothing itself; each result lists the business''s own doors (a listing''s doors, or a member''s protocols). A business this network found on its own website is marked as found, with the date it was checked; only facts it published itself are shown, never a phone or an email. Errors are RFC 9457 problem documents. The protocol behind this API, with its JSON Schemas and test vectors, is public; the MCP server at /mcp offers the same reads as tools, and can carry a person''s request to a business''s own inbox when the business allows it.'
servers:
- url: https://network.surfingdog.ai
tags:
- name: score
description: 'The agentic score: how far an agent can go with a business, by published rules.'
paths:
/v1/score-rules:
get:
operationId: getScoreRules
tags:
- score
summary: The rules of the agentic score
description: 'How far an AI agent can go with a business through the doors it published: the capabilities, their groups and weights, what applies to each kind of business, the formula, the grades and the fixes. The agentic score never changes the directory''s order.'
parameters:
- name: version
in: query
schema:
type: integer
minimum: 1
responses:
'200':
description: The score rules.
content:
application/json:
schema:
type: object
description: The score rules document.
externalDocs:
description: Its JSON Schema
url: https://github.com/surfingdogai/inbox/blob/main/packages/spec/schemas/score-rules.json
required:
- version
- status
- groups
- profiles
- formula
properties:
version:
type: integer
minimum: 1
status:
type: string
groups:
type: array
items:
type: object
profiles:
type: array
items:
type: object
formula:
type: string
'404':
$ref: '#/components/responses/Problem'
/leaderboard.json:
get:
operationId: getLeaderboard
tags:
- score
summary: Businesses where an agent can book, order or sign up first, then by agentic…
description: 'By category group, country and place, or over everyone: whether an agent can book, order or sign up there (yes, then partly, then no or not applicable), then agentic score, then the most recent check, then domain. A real business (a member, a business that registered or whose owner proved the site is theirs, or one whose site our crawler matched to its entry on public maps or in a business register: real_business; GET /v1/score-rules, listing) carries a label, and real=1 lists only them. With can, the agent-ready businesses of every kind (software and AI companies too) where an agent can do that one thing, the same businesses GET /v1/stats counts; with ready=1, every agent-ready business. A business is named on leaderboards, and its result page may be indexed, once it is agent-ready (askable or above). A local business we found is named once we match its site to its entry on public maps or registers, or its owner proves the site is theirs. Parked, empty or taken-over sites never are. Others are counted, not named. If your site tells AI systems not to use or train on its content, we don''t name you or list you publicly. It is not the directory''s search order.'
parameters:
- name: category
in: query
description: A category group (GET /v1/categories).
schema:
type: string
- name: country
in: query
description: A two-letter country code.
schema:
type: string
pattern: ^[A-Za-z]{2}$
- name: place
in: query
description: A place in that country, in lower case with hyphens; needs country.
schema:
type: string
pattern: ^[a-z0-9-]{1,80}$
- name: can
in: query
description: 'A capability: the agent-ready businesses where an agent can do it.'
schema:
type: string
enum:
- message
- book
- order
- signup
- cancel
- change
- negotiate
- catalogue
- availability
- pay
- track
- return
- name: ready
in: query
description: '1: every agent-ready business. Left out with can.'
schema:
type: string
enum:
- '1'
- name: real
in: query
description: '1: the real businesses among the agent-ready (GET /v1/score-rules, listing), the businesses GET /v1/stats counts as real_businesses_verified; with can, those that can do it.'
schema:
type: string
enum:
- '1'
- name: page
in: query
schema:
type: integer
minimum: 1
maximum: 20
responses:
'200':
description: One page of fifty named businesses, and how many more were checked.
content:
application/json:
schema:
type: object
externalDocs:
description: Its JSON Schema
url: https://github.com/surfingdogai/inbox/blob/main/packages/spec/schemas/leaderboard.json
required:
- scope
- rules
- order
- order_by
- not_search_order
- total
- named_total
- unnamed
- page
- next_page
- named
properties:
scope:
type: object
rules:
type: object
order:
type: string
order_by:
type: array
items:
type: string
not_search_order:
type: string
total:
type: integer
named_total:
type: integer
description: How many of total the board names, over all its pages; unnamed is the rest.
unnamed:
type: integer
page:
type: integer
next_page:
type:
- integer
- 'null'
named:
type: array
items:
type: object
software_and_ai:
type: integer
'400':
$ref: '#/components/responses/Problem'
/b/{domain}.json:
get:
operationId: getCheckResult
tags:
- score
summary: Where one business's check stands, and its agentic score
description: 'A business''s result: not checked, queued, checking, scoring, done, blocked, failed, hidden by its owner, or not checkable. Once done: its agentic score and grade by the published rules, the five answers (can an agent message, book, order, cancel or negotiate there) each with its door and date, every capability, the fixes worth most points, its rank, and whether its page may be indexed; and its doors'' history from this network''s own reads (door_history). Reading it never starts a check: POST /check or the MCP tool check_business does. A different spelling of the domain is redirected to its canonical one.'
parameters:
- name: domain
in: path
required: true
description: The business's registrable domain, such as salon.example.
schema:
type: string
maxLength: 253
responses:
'200':
description: The result.
content:
application/json:
schema:
type: object
externalDocs:
description: Its JSON Schema
url: https://github.com/surfingdogai/inbox/blob/main/packages/spec/schemas/check-result.json
required:
- domain
- url
- state
- indexable
properties:
domain:
type: string
url:
type: string
format: uri
state:
type: string
enum:
- not_checked
- queued
- checking
- scoring
- done
- blocked
- failed
- hidden
- not_checkable
agentic_score:
type: integer
minimum: 0
maximum: 100
grade:
type: string
enum:
- A
- B
- C
- D
- E
answers:
type: array
items:
type: object
capabilities:
type: array
items:
type: object
fixes:
type: array
items:
type: object
indexable:
type: boolean
pay_with:
type: array
items:
$ref: '#/components/schemas/PayWith'
description: 'How the business is paid, each way with its evidence: declared by the business, or seen on its own pages and files.'
door_history:
$ref: '#/components/schemas/DoorHistory'
'301':
description: 'Another spelling of the domain: the canonical result.'
'404':
description: This site cannot be checked.
components:
schemas:
Problem:
type: object
required:
- type
- title
- status
properties:
type:
type: string
title:
type: string
status:
type: integer
code:
type: string
description: 'What to do differently, when a status alone does not say: cursor_expired, not_found, too_large, rate_limited, category_unresolved, page_too_deep.'
detail:
type: string
instance:
type: string
candidates:
type: array
items:
type: string
description: 'category_unresolved: up to 5 categories to try.'
DoorHistory:
type: object
description: 'On a single business: what this network''s own reads found of the doors it shows, kept for good, one line a day (protocol §4.8). Each door type at each host, the doors there now first: the first and last day a read found it answering and on how many days, and for a door read every day (a member''s, a claimed listing''s), from 14 checked days, its daily checks: a day is answered only when every check that day answered; one missed (failed, or blocked while the door is down by the network''s reads) or refused (blocked otherwise) is not. A read an agent''s report or the business asked for is no check, and no agent''s report enters any of it. Absent when there is nothing to say.'
externalDocs:
description: Its test vectors
url: https://github.com/surfingdogai/inbox/blob/main/packages/spec/vectors/door-history.json
required:
- as_of
- doors
properties:
as_of:
type: string
format: date
doors:
type: array
items:
type: object
required:
- type
- host
- first_seen
- last_seen
- days_seen
- current
properties:
type:
type: string
pattern: ^(inbox|mcp|a2a|openapi|api|ucp|acp|nlweb|webhook|other|platform:[a-z0-9-]{1,40})$
description: A door type (inbox, mcp, a2a, platform:shopify and the rest).
host:
type: string
first_seen:
type: string
format: date
description: The first UTC day a read of ours found a door of this type at this host answering.
last_seen:
type: string
format: date
description: The last such day.
days_seen:
type: integer
minimum: 1
description: 'On how many days a read of ours found it answering: a door the crawl reads about once a month is seen on few days, and nothing is said of the days between.'
current:
type: boolean
description: Whether a door of this type is at this host now.
checks:
type: object
description: 'A door read every day, from 14 checked days: the first and last day checked, the days checked, answered and refused, and the last 90 days ending on as_of (a every check answered, x one was missed, b one was refused and none missed, . no check).'
required:
- first_checked
- last_checked
- days
- answered
- refused
- recent
properties:
first_checked:
type: string
format: date
last_checked:
type: string
format: date
days:
type: integer
minimum: 1
answered:
type: integer
minimum: 0
refused:
type: integer
minimum: 0
description: Days on which a check was refused (blocked while the door was not down) and none missed.
recent:
type: string
pattern: ^[axb.]{90}$
summary:
type: string
description: 'With checks, in English: "Answered every check on 41 of 42 days checked since 3 Oct 2026 (97%)." The share is floored, the days refused follow it ("(75%; refused our check on 5)"), and it says "from … to …" when the last check is more than 7 days old. Informative: another network may word it its own way.'
PayWith:
type: object
description: 'One way to pay a business, with the strongest evidence for it. declared: the business said so itself (its registration, its inbox''s manifest, its own structured data, its own UCP or ACP profile, an agent payment protocol its door''s document declares in its structure). seen: not its own word: a payment provider''s own code, button, payment icon or file on its site, a door''s document naming an agent payment protocol in passing, or its shop platform''s standard UCP profile (via ucp). A page''s words are never evidence. A card brand says a card: card comes with the brand''s evidence. ucp_handlers names the UCP protocol, not a way a person pays; pages for people leave it out.'
required:
- token
- label
- src
- via
properties:
token:
type: string
description: A payment token of GET /v1/attributes.
label:
type: string
description: Its English name.
src:
type: string
enum:
- declared
- seen
via:
type: string
pattern: ^[a-z][a-z0-9_]*$
description: 'Where the evidence is: registration, manifest, schema_org, ucp, acp, door, file or page today; a later network may add another, so read one you do not know as evidence of its src.'
url:
type: string
format: uri
description: 'Where the evidence is: the page, file, profile, manifest or door. Absent for a registration.'
responses:
Problem:
description: An RFC 9457 problem document.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
externalDocs:
description: The network protocol
url: https://github.com/surfingdogai/inbox/blob/main/docs/protocol/network.md