Connect your sources (start here)

Everything in 4789 comes from sources you add. Pick yours:

Media server

Settings, Add your source, Media server. Enter your Jellyfin, Plex, or Emby address (like https://media.example.com) and sign in. Your whole library appears in about a minute.

Catalogs and add-ons

Have a manifest link from a community add-on? Settings, Add your source, Catalogs and addons, then paste the link. Existing links from other hub apps work as-is; unknown fields are ignored and unsupported features are skipped without breaking anything.

Your files

Settings, Add your source, Your files. Pick videos from the Files app or your device and they join the library, artwork and all.

Live TV

Paste the playlist link your provider gave you (Xtream or M3U). Channels get organized automatically.

If a source will not connect: check the address starts with https://, that the server is reachable from your phone, and that sign-in details are right. Still stuck? We answer email.

Everything below this point is the technical protocol reference, written for developers who build add-ons.

Developers

The Add-on Protocol

A small, open, manifest-based JSON API for connecting a catalogue service to 4789. A service publishes one manifest.json and answers a handful of predictable JSON endpoints. That's the whole protocol, any language, no SDK.

Overview

The manifest-and-resource pattern here is a conventional REST-style JSON API design used widely across self-hosted media tooling. It is an interoperability convention, not technology proprietary to 4789 or any single vendor. These docs describe exactly the shape 4789 consumes so any compatible service works.

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, and subtitles. 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

  1. Open 4789 and go to add a source.
  2. Paste your manifest.json URL.
  3. 4789 fetches the manifest, registers your catalogs, and routes matching ids to your endpoints.
A service is addable if it declares at least one catalog, or a stream, meta, or subtitles resource. A manifest with none of those is inert and is rejected.

Manifest reference

Tolerant by contract. A manifest that parses at all yields a usable service. Only 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

FieldTypeNotes
idstringStable, unique service id (e.g. com.example.catalog). The only required field.
versionstringOptional semantic version.
namestringHuman-readable display name.
descriptionstringShort description shown to the user.
typesstring[]Content types served, e.g. ["movie","series"]. Absent = serves any type.
idPrefixesstring[]The id namespaces this service owns, e.g. ["tt","ex:"]. Drives routing.
resources(string | object)[]Which resources are served.
catalogsobject[]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

FieldTypeNotes
idstringCatalog id, unique within your service.
typestringContent type, e.g. movie / series.
namestringOptional display name (falls back to id).
pageSizeintOptional items-per-page hint (default 50).
showInHomeboolOptional. Suggests featuring the shelf on the home surface.
extraobject[]Optional supported query extras, commonly genre, search, skip, year, language, country.
Legacy shape accepted. Instead of structured 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.

ExampleMeaning
tt0111161A standard public film/series id (the widely-used tt namespace).
tmdb:603A public catalogue-database numeric id.
ex:1234A 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.

If your ids map onto public 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-Control headers on catalog and meta responses keep browsing fast; keep stream responses short-lived.
  • Pagination. Honour the skip extra and your catalog's pageSize so large shelves load in pages.

Best practices

  • Be tolerant and stable. Keep your id and id namespace stable across versions; never recycle ids for different titles.
  • Return good previews. Always include poster on metas[] items; a wall of artwork is only as good as the posters you return.
  • Fail soft. Return an empty metas/streams array 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 idPrefixes so 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: [email protected].