Publisher API
01
Sign in at /publishers. You receive client_id + bc_pub_ secret. CiteFleet is already issued. Extra products can also be set with BOTCENTRAL_PUBLISHER_KEYS. A read partner key is not enough.
02
POST /oauth/token with grant_type=client_credentials. Or send the bc_pub_ secret as Authorization: Bearer (Stripe-style). Discovery is /.well-known/oauth-authorization-server.
03
POST /v1/publisher/verify-tokens { domain }. Show the client: DNS TXT botcentral-verify=<token> or a plain-text file at /.well-known/botcentral.txt. SPA HTML at that path is rejected.
04
POST /v1/probe with the domain and token. Do not PUT until proof is dns-txt or well-known-file. Probe is public; it does not list the site.
05
PUT /v1/publisher/sites/{domain}. BotCentral fetches the origin again. If proof fails you get 409 and nothing is listed.
06
HTTPS webhook site.listed / site.unpublished / site.reverified, HMAC sha256. Also poll GET /v1/changes. The public card shows listed_by so assistants know which publisher wrote it.
OAuth token
curl -sS https://botcentral.org/oauth/token \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'grant_type=client_credentials&client_id=bc_cid_...&client_secret=bc_pub_...&scope=publisher'
PUT a client site
curl -sS -X PUT https://botcentral.org/v1/publisher/sites/client.example \
-H "authorization: Bearer $ACCESS" \
-H "content-type: application/json" \
-d '{
"domain": "client.example",
"canonical": "https://client.example/",
"name": "Client",
"summary": "What the site is.",
"topics": ["example"],
"allow_bots": true,
"allow": ["ChatGPT-User","OAI-SearchBot","Claude-User"],
"deny": ["GPTBot","ClaudeBot"],
"pointers": {
"robots": "https://client.example/robots.txt",
"sitemap": "https://client.example/sitemap.xml",
"llms": "https://client.example/llms.txt"
},
"pages": [{ "url": "https://client.example/", "rel": "home", "title": "Home" }],
"verifyToken": "bc-verify-acme-ab12cd34",
"consent": { "retrieve": true, "train": false, "act": false, "cite": true, "tdm": "reserved" }
}'Unpublish: DELETE /v1/publisher/sites/{domain}. Re-check: POST /v1/publisher/sites/{domain}/reverify. Who am I: GET /v1/publisher/me. Your listed clients: GET /v1/publisher/sites. Directory: GET /v1/publishers. Legacy POST /internal/publish still works for CiteFleet.
Connect has the copy-paste. /publishers mints the credentials. Get listed is the owner-facing version of the same proof.