Feeds, embeds and previews
Public workspaces publish their issues as Atom feeds, answer oEmbed requests and have a preview image for every page. None of it needs a key, and none of it ever includes private, hidden or internal content.
curl https://acme.blhp.app/feed.atom
<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"><id>https://acme.blhp.app/feed.atom</id><title>Acme issues</title><updated>2026-11-04T03:16:19.489Z</updated><author><name>Bellhop</name></author><link rel="alternate" href="https://acme.blhp.app/"/><link rel="self" type="application/atom+xml" href="https://acme.blhp.app/feed.atom"/><entry><id>tag:blhp.app,2026:issue/ffac947e-37c2-4952-9934-1c668496ba37</id><title>Guest author feed check</title><updated>2026-11-04T03:16:19.489Z</updated><link rel="alternate" href="https://acme.blhp.app/issues/2"/>…</entry></feed>
Atom feeds
| Feed | URL | Entries |
|---|---|---|
| A workspace | https://<slug>.blhp.app/feed.atom |
Its 50 most recently updated issues, with title, author and the start of the description. |
| One issue | https://<slug>.blhp.app/issues/<number>/feed.atom |
Its 50 latest public replies and status changes. |
Workspace and issue pages link their feed with <link rel="alternate" type="application/atom+xml">, so feed readers find it from the page URL. Public workspaces' feeds can be cached for a minute (s-maxage=60).
Entries are plain text: titles, author names and summaries of up to 320 characters, never the full description. Email addresses are never shown as author names.
oEmbed
GET https://blhp.app/oembed?url=<url>&format=json
url is an issue (https://<slug>.blhp.app/issues/<number>) or a workspace (https://<slug>.blhp.app/). The answer is an oEmbed link with the preview image as its thumbnail:
curl "https://blhp.app/oembed?url=https%3A%2F%2Facme.blhp.app%2Fissues%2F2&format=json"
{
"version": "1.0",
"type": "link",
"title": "Guest author feed check",
"author_name": "Renamed reporter",
"provider_name": "Bellhop",
"provider_url": "https://blhp.app",
"thumbnail_url": "https://acme.blhp.app/og/issues/2.png",
"thumbnail_width": 1200,
"thumbnail_height": 630
}
Only format=json is supported. Bellhop never fetches the URL you pass; it reads it and looks up the issue itself. Anything that isn't a public issue in a listed workspace gets 404. Listed pages advertise the endpoint with <link rel="alternate" type="application/json+oembed">.
Preview images
Every workspace and issue page has a 1200×630 PNG card in its og:image and twitter:image tags:
| Card | URL |
|---|---|
| Bellhop | https://blhp.app/og/home.png |
| A workspace | https://<slug>.blhp.app/og/app.png?v=<version> |
| An issue | https://<slug>.blhp.app/og/issues/<number>.png?v=<version> |
| A docs page | https://blhp.app/og/docs/<page>.png?v=<version> |
An issue card shows its title, number, status and labels, and who answered if an agent did. v changes whenever anything on the card changes, so each version can be cached forever. Use the URLs from the page's tags rather than building them: the version is part of the URL.
What's included
Feeds, oEmbed and cards only ever show what anyone could see on the public page:
- Private issues, hidden issues and comments and internal notes never appear. A private, hidden or deleted issue's card is the workspace card.
- Unlisted workspaces (not public) don't answer oEmbed and aren't in sitemaps. Their feeds and cards still work for anyone who has the workspace's link, like the workspace itself. Their feeds are marked
noindexand aren't cached publicly. - Nothing depends on cookies or who's asking: a team member and a stranger get the same feed.