Overview
The protocol has three parts:
- Manifest. A single JSON document at a stable URL that declares the service's id, the resources it serves, the content types it covers, the id prefixes it owns, and any browsable catalogs.
- Resources. The four endpoints a service may answer:
catalog,meta,stream, andsubtitles. A service implements only the ones it needs. - IDs. A simple, prefixed string convention that lets 4789 route each request to the service that owns it.
4789 is a client. It reads a manifest you point it at and renders what the endpoints return. It does not host, aggregate, index, or distribute any content, developers who build and operate compatible services are responsible for their services and the data they return.
Getting started
A compatible service is just an HTTP server that returns JSON. Here is the smallest useful example: a manifest plus one browsable catalog and its detail endpoint.
1. Decide what your service offers
Pick which resources you implement: catalog (browsable shelves), meta (detail for one title), stream (playable links), and subtitles (external tracks). A browse-only service can ship catalog + meta alone.
2. Publish a manifest
Serve a JSON document at a stable URL ending in /manifest.json:
GET https://example.com/manifest.json
{
"id": "com.example.myservice",
"version": "1.0.0",
"name": "My Catalog Service",
"description": "A small example service.",
"types": ["movie", "series"],
"idPrefixes": ["ex:"],
"resources": ["catalog", "meta"],
"catalogs": [
{ "type": "movie", "id": "featured", "name": "Featured" }
]
}
4789 normalises the URL by dropping a trailing /manifest.json to get your resource base: every other endpoint hangs off that base.
3. Answer the resource endpoints
Given base https://example.com, 4789 builds predictable URLs:
GET {base}/catalog/{type}/{id}.json
GET {base}/meta/{type}/{id}.json
GET {base}/stream/{type}/{id}.json
GET {base}/subtitles/{type}/{id}.json
4. Connect it in 4789
- Open 4789 and go to add a source.
- Paste your
manifest.jsonURL. - 4789 fetches the manifest, registers your catalogs, and routes matching ids to your endpoints.
stream, meta, or subtitles resource. A manifest with none of those is inert and is rejected.Manifest reference
id is required; every other field is optional and defaulted. A single malformed entry in resources or catalogs is dropped, never fatal.Top-level fields
| Field | Type | Notes |
|---|---|---|
id | string | Stable, unique service id (e.g. com.example.catalog). The only required field. |
version | string | Optional semantic version. |
name | string | Human-readable display name. |
description | string | Short description shown to the user. |
types | string[] | Content types served, e.g. ["movie","series"]. Absent = serves any type. |
idPrefixes | string[] | The id namespaces this service owns, e.g. ["tt","ex:"]. Drives routing. |
resources | (string | object)[] | Which resources are served. |
catalogs | object[] | Browsable shelves. |
Resources
Each entry is either a bare string or an object that narrows the manifest-level types / prefixes for that one resource.
"resources": [
"catalog",
{ "name": "stream", "types": ["movie"], "idPrefixes": ["tt"] }
]
A stream or subtitles resource that declares no prefixes claims all ids (the permissive default), so it is queried for any matching title.
Catalog entry
| Field | Type | Notes |
|---|---|---|
id | string | Catalog id, unique within your service. |
type | string | Content type, e.g. movie / series. |
name | string | Optional display name (falls back to id). |
pageSize | int | Optional items-per-page hint (default 50). |
showInHome | bool | Optional. Suggests featuring the shelf on the home surface. |
extra | object[] | Optional supported query extras, commonly genre, search, skip, year, language, country. |
extra, a catalog may send extraSupported and extraRequired as string arrays. 4789 normalises both forms into the same model.Resource endpoints
Four resources, four predictable URL shapes. All hang off your resource base. Every endpoint returns JSON and takes no request body.
catalog
Returns a page of lightweight items for a browsable shelf. Query extras are appended as path segments before .json:
GET {base}/catalog/{type}/{id}.json
GET {base}/catalog/{type}/{id}/genre=Action.json
GET {base}/catalog/{type}/{id}/genre=Action&skip=100.json
{
"metas": [
{ "id": "ex:1234", "type": "movie", "name": "Example Title",
"poster": "https://.../poster.jpg", "releaseInfo": "2024" }
]
}
Extra values must be percent-encoded. Each metas[] item is a preview; the full item is fetched later via meta.
meta
Returns the full detail record for one title.
GET {base}/meta/{type}/{id}.json
{
"meta": {
"id": "ex:1234", "type": "movie", "name": "Example Title",
"description": "A short synopsis.", "genres": ["Drama"],
"releaseInfo": "2024", "runtime": "128 min",
"poster": "https://.../poster.jpg",
"background": "https://.../backdrop.jpg"
}
}
For a series, include an episode list in videos[]:
"videos": [
{ "id": "ex:1234:1:1", "title": "Pilot", "season": 1, "episode": 1,
"released": "2024-01-05", "thumbnail": "https://.../ep.jpg" }
]
stream
Returns playable links for a title or a specific episode.
GET {base}/stream/series/ex:1234:1:1.json
{
"streams": [
{ "url": "https://your-server.example/media/file.mkv",
"name": "My Service", "title": "1080p · x265",
"behaviorHints": { "filename": "file.mkv", "videoSize": 2147483648 } }
]
}
A url is treated as ephemeral and is never persisted by the client. behaviorHints.filename helps pick the right playback path.
subtitles
Returns external subtitle tracks.
GET {base}/subtitles/{type}/{id}.json
{
"subtitles": [
{ "id": "sub-1", "url": "https://.../en.srt", "lang": "eng" },
{ "id": "sub-2", "url": "https://.../es.srt", "lang": "spa" }
]
}
ID format & routing
Every title has a string id. The id's leading prefix tells 4789 which service owns it, so a request goes to the right endpoint.
| Example | Meaning |
|---|---|
tt0111161 | A standard public film/series id (the widely-used tt namespace). |
tmdb:603 | A public catalogue-database numeric id. |
ex:1234 | A service-specific id in your own ex: namespace. |
4789 treats tt and tmdb: as standard prefixes: titles with these ids can be resolved by the default catalogue path, so many services can contribute streams to them. Any other prefix is an owned namespace: a title in it is routed only to the service that declares that prefix.
tt/tmdb: identifiers, use those; your streams then merge in for titles users already browse. If your service has its own identifier space, pick a short unique prefix and own it via idPrefixes, and implement a meta resource so detail resolves.A series episode id appends season and episode to the series id, colon-separated: ex:1234:1:5 is season 1, episode 5. 4789 builds this automatically.
Hosting
- HTTPS. Serve the manifest and all endpoints over HTTPS so the app can reach them from any network.
- CORS. Return
Access-Control-Allow-Origin: *(or a scoped origin) so the client can read your JSON. - Content type. Respond with
application/json. No request body is ever sent to your endpoints. - Caching. Sensible
Cache-Controlheaders on catalog and meta responses keep browsing fast; keepstreamresponses short-lived. - Pagination. Honour the
skipextra and your catalog'spageSizeso large shelves load in pages.
Best practices
- Be tolerant and stable. Keep your
idand id namespace stable across versions; never recycle ids for different titles. - Return good previews. Always include
posteronmetas[]items; a wall of artwork is only as good as the posters you return. - Fail soft. Return an empty
metas/streamsarray rather than an error when you have nothing; one bad entry should never blank a shelf. - Narrow with prefixes. If your service only answers for certain ids, declare
idPrefixesso 4789 doesn't query you for everything. - Respect the user. Don't require credentials to browse unless you must; keep responses small and quick.
Add-on Protocol Terms of Use
These terms apply to anyone who builds, hosts, or operates a service compatible with the 4789 Add-on Protocol.
- You are solely responsible for your service, its availability, the data it returns, and for holding any rights necessary to offer it.
- You must comply with all applicable laws, including copyright and intellectual-property law, and with the terms of any upstream API you use.
- 4789 does not endorse, vet, control, or certify third-party services. Compatibility does not imply any relationship with, or approval by, 4789.
- You must not present your service in a way that implies it is operated or endorsed by 4789, or misuse the 4789 name or marks.
- Users are responsible for the sources they choose to connect. Nothing here should be read as advice that any particular use is lawful in a given jurisdiction.
Questions: support@4789library.com.