Extend Cinder

Cinder is built on a fully extensible plugin architecture. Add your own metadata sources and download providers through simple JSON configs — no programming required.

📖

Metadata Providers

Fetch book info — covers, descriptions, ratings, genres — and populate the Discover tab with curated sections.

⬇️

Download Sources

Search for ebook files from any API — direct downloads or debrid-powered sources. Your configs, your sources.

Getting Started

Adding a new source to Cinder takes just a few steps:

1

Write a JSON config

Define how Cinder should call an API — the URL, response format, and field mappings. See the schemas below.

2

Import it into the app

Go to Settings → Metadata Sources or Settings → Download Sources and import via URL, paste JSON, or load a file.

3

Test your config

Use the built-in Test Connection button to verify your source works. Cinder will show you result counts and any errors.

💡 Both metadata providers and download sources use the same import methods: From URL (paste a link to hosted JSON) or Paste JSON (paste the raw config directly).

Metadata Providers

Metadata providers tell Cinder how to fetch book information from external APIs. They power two features: search (finding book metadata by title/author) and discovery (homepage sections like trending, new releases, etc.).

Cinder ships with built-in providers (Apple Books, Open Library), but you can add your own to pull metadata from any API that returns JSON.

Schema Overview

A metadata provider config is a JSON object with these top-level fields:

FieldTypeRequiredDescription
idstringUnique identifier (kebab-case, e.g. "my-provider")
namestringDisplay name shown in the app
versionstringSemver string (e.g. "1.0.0")
descriptionstringShort description shown in UI
iconstringIonicons icon name (e.g. "library")
capabilitiesobjectWhat this provider supports
authobjectAuthentication config
searchobject*Search endpoint config
discoverobject*Discovery sections config
rateLimitobjectRate limiting config

* Required if the corresponding capability is enabled.

Capabilities

The capabilities object declares what your provider supports:

JSON
{
  "capabilities": {
    "search": true,
    "discover": true
  }
}
  • search — Provider can search for books by query. Requires a search config.
  • discover — Provider can serve discovery sections for the Discover tab. Requires a discover config.
⚠️ If you set a capability to true but don't provide the matching config object, validation will fail on import.

Response & Mapping

resultsPath

Dot-notation path to the results array in the JSON response:

  • { "results": [...] }"results"
  • { "docs": [...] }"docs"
  • { "data": { "books": [...] } }"data.books"
  • Root is the array → "" or "."

Dot-Notation Mapping

Use dot notation to access nested fields and array elements in each result object:

  • "title"result.title
  • "author_name.0"result.author_name[0] (first element)
  • "file.cover_url"result.file.cover_url
  • "formats.application/epub+zip"result.formats["application/epub+zip"]

BookMetadata Fields

These are all the fields Cinder understands for book metadata. Map them in your response.mapping:

FieldTypeDescription
titlestringBook title (required)
authorstringPrimary author name (required)
authorsstring[]Array of all author names
subtitlestringBook subtitle
coverstringCover image URL
descriptionstringBook description / synopsis
isbnstringISBN number
publisherstringPublisher name
publishedYearstringYear of publication
pagesnumberPage count
languagestringLanguage code
genresstring[]Genre / subject tags (trimmed to 5)
ratingnumberAverage rating
ratingsCountnumberNumber of ratings
seriesNamestringSeries name (e.g. "Harry Potter")
seriesPositionstringPosition in series (e.g. "3")

Discovery Configuration

Discovery sections appear in the Discover tab. Each section has its own request and optional response config:

JSON
"discover": {
  "sections": [
    {
      "id": "trending",
      "title": "📈 Trending Now",
      "icon": "trending-up",
      "request": {
        "method": "GET",
        "url": "https://api.example.com/trending?limit=20"
      },
      "response": {
        "type": "json",
        "resultsPath": "works",
        "mapping": {
          "title": "title",
          "author": "author_name.0",
          "cover": "cover_i"
        }
      }
    }
  ]
}
💡 If a discovery section omits its own response config, it inherits the mapping from the search config — saves you from duplicating the mapping!

Template Variables

Use these placeholders in your url and body fields — Cinder substitutes them at runtime:

VariableReplaced With
{QUERY}The user's search text
{USER_API_KEY}User's configured API key
{PAGE}Current page number (pagination)

All values are URL-encoded automatically. Unused variables are silently removed.

Authentication

If the API requires authentication, add an auth object:

JSON
"auth": {
  "type": "apiKey",
  "headerName": "X-API-Key",
  "userProvided": true
}
  • "apiKey" — Sends X-API-Key header (or custom headerName)
  • "bearer" — Sends Authorization: Bearer <key>
  • "basic" — Sends Authorization: Basic <key>
  • "none" — No authentication

When userProvided is true, users configure their key in-app (stored securely in device keychain).

⚠️ Auth requires HTTPS — Cinder rejects HTTP URLs with authentication to prevent API key leakage.

Example Config

A complete metadata provider with search and discovery:

JSON
{
  "id": "my-book-api",
  "name": "My Book API",
  "version": "1.0.0",
  "description": "Search and discover books via My Book API",
  "capabilities": { "search": true, "discover": true },
  "search": {
    "request": {
      "method": "GET",
      "url": "https://api.example.com/books?q={QUERY}&limit=25",
      "timeout": 15000
    },
    "response": {
      "type": "json",
      "resultsPath": "results",
      "mapping": {
        "title": "title",
        "author": "author_name",
        "cover": "cover_url",
        "description": "synopsis",
        "publishedYear": "year",
        "genres": "categories",
        "rating": "avg_rating"
      }
    }
  },
  "discover": {
    "sections": [
      {
        "id": "trending",
        "title": "📈 Trending Now",
        "request": {
          "method": "GET",
          "url": "https://api.example.com/trending?limit=20",
          "timeout": 10000
        }
      }
    ]
  }
}

Importing

Three ways to add a metadata provider to Cinder:

  • URL import — Host your JSON config at a public URL. In the app, go to Settings → Metadata Sources → Add → From URL.
  • JSON paste — Copy the JSON and paste it directly in the app's import dialog.

Download Sources

Download sources tell Cinder how to search for ebook files from external APIs. When you browse a book and tap download, Cinder queries all your enabled sources and shows the results.

Schema Overview

FieldTypeRequiredDescription
idstringUnique identifier (kebab-case)
namestringDisplay name shown in the app
versionstringSemver string (e.g. "1.0.0")
typestring"directDownload" or "debrid"
descriptionstringShort description
iconstringIonicons icon name
requestobjectHow to call the API
responseobjectHow to parse results
matchingobjectResult filtering config
authobjectAuthentication config
rateLimitobjectRate limiting config
paginationobjectMulti-page fetching
downloadResolveobjectTwo-step download resolution
debridLinkTemplatestringTemplate for constructing debrid links
trackersstring[]Tracker URLs for debrid links
legalobjectLegal metadata & disclaimer

Source Types

  • "directDownload" — Returns direct download URLs (HTTP links to epub/pdf/mobi files). Cinder downloads the file straight from the URL.
  • "debrid" — Returns magnet links, info hashes, or torrent URLs. Cinder routes these through your configured debrid service, which resolves them into direct download links.
ℹ️ Cinder is not a torrent client — it relies on debrid services to resolve magnet links into downloadable files.

Request Configuration

JSON
"request": {
  "method": "GET",
  "url": "https://api.example.com/search?q={TITLE}+{AUTHOR}",
  "headers": { "Content-Type": "application/json" },
  "timeout": 15000
}

For POST requests, add a body object — template variables inside strings are substituted automatically:

JSON
"request": {
  "method": "POST",
  "url": "https://api.example.com/v1/search",
  "headers": { "Content-Type": "application/json" },
  "body": {
    "query": "{TITLE}",
    "search_field": "title",
    "size": 50,
    "categories": [9001000, 9000000]
  }
}

Response Parsing

Tell Cinder how to extract results from the JSON response:

JSON
"response": {
  "type": "json",
  "resultsPath": "hits",
  "mapping": {
    "title": "name",
    "url": "downloadUrl",
    "size": "fileSize",
    "format": "extension",
    "seeders": "seeders"
  }
}

Download Result Fields

FieldTypeDescription
titlestringResult title (required)
urlstringDownload URL / magnet / identifier (required)
formatstringFile type (epub, pdf, mobi, etc.)
sizenumberFile size in bytes
seedersnumberSeeder count (torrent sources)
qualitystringQuality indicator
sourcestringSource name label
datestringUpload / publish date

Result Matching

Cinder can automatically filter results to find the best matches for the book you're looking for:

JSON
"matching": {
  "field": "title",
  "threshold": 0.6,
  "algorithm": "fuzzy"
}
  • "fuzzy" — Fuzzy string matching (recommended). Handles typos, word order differences.
  • "contains" — Result field must contain the search term.
  • "exact" — Exact case-insensitive match only.

Threshold ranges from 0.0 (match everything) to 1.0 (exact only). 0.5–0.6 is recommended for most APIs.

Template Variables

VariableReplaced With
{TITLE}Book title
{AUTHOR}Book author
{ISBN}ISBN (if available)
{YEAR}Publication year
{SERIES}Series name
{SERIES_POSITION}Position in series
{USER_API_KEY}User's configured API key
{PAGE}Current page number
{DOWNLOAD_ID}ID from search results (for two-step downloads)

All values are URL-encoded automatically. Unused variables are silently removed.

Two-Step Downloads

Some APIs don't return direct links — instead they return an identifier that you pass to a second endpoint to get the real download URL. Add a downloadResolve config:

JSON
"downloadResolve": {
  "request": {
    "method": "GET",
    "url": "https://api.example.com/get_links?id={DOWNLOAD_ID}",
    "timeout": 60000
  }
}

Flow: Search returns identifier in url field → user taps download → Cinder substitutes it into {DOWNLOAD_ID} → calls the resolve endpoint → gets the real download URL.

Debrid Link Templates

For debrid sources, debridLinkTemplate lets you construct the link sent to the debrid service from result fields. Use {field_name} placeholders:

JSON
"debridLinkTemplate": "magnet:?xt=urn:btih:{infoHash}&dn={title}",
"trackers": [
  "udp://tracker.opentrackr.org:1337/announce",
  "udp://open.stealth.si:80/announce"
]

If you don't set debridLinkTemplate on a debrid source, Cinder uses the raw url value as-is.

Pagination

For APIs that return paginated results:

JSON
"pagination": {
  "type": "page",
  "paramName": "page",
  "startAt": 1,
  "maxPages": 3,
  "hasMorePath": "meta.hasMore"
}

Use {PAGE} in your request URL. Cinder stops when maxPages is reached, no results are returned, or hasMorePath is false.

Built-in Sources

Cinder ships with two built-in download sources for public domain content:

  • Open Library Free Books — Free public domain ebooks from Open Library & Internet Archive.
  • Project Gutenberg — Over 70,000 free public domain ebooks.

These are always available and cannot be removed.

Example Configs

Direct Download (GET)

JSON
{
  "id": "my-ebook-source",
  "name": "My Ebook Source",
  "version": "1.0.0",
  "type": "directDownload",
  "description": "Search for ebooks with direct download links",
  "request": {
    "method": "GET",
    "url": "https://api.example.com/search?q={TITLE}+{AUTHOR}",
    "timeout": 15000
  },
  "response": {
    "type": "json",
    "resultsPath": "results",
    "mapping": {
      "title": "title",
      "url": "download_url",
      "format": "extension",
      "size": "filesize"
    }
  },
  "matching": {
    "field": "title",
    "threshold": 0.5,
    "algorithm": "fuzzy"
  }
}

Debrid Source (POST)

JSON
{
  "id": "torrent-ebooks",
  "name": "Torrent Ebooks",
  "version": "1.0.0",
  "type": "debrid",
  "description": "Torrent ebook search via debrid",
  "request": {
    "method": "POST",
    "url": "https://api.example.com/v1",
    "headers": { "Content-Type": "application/json" },
    "body": {
      "query": "{TITLE}",
      "search_field": "title",
      "size": 50,
      "categories": [9001000, 9000000]
    }
  },
  "response": {
    "type": "json",
    "resultsPath": "hits",
    "mapping": {
      "title": "title",
      "url": "magnetUrl",
      "size": "bytes",
      "seeders": "seeders"
    }
  },
  "matching": {
    "field": "title",
    "threshold": 0.6,
    "algorithm": "fuzzy"
  }
}

Two-Step Direct Download

JSON
{
  "id": "shelfmark-server",
  "name": "Shelfmark Server",
  "version": "1.0.0",
  "type": "directDownload",
  "request": {
    "method": "GET",
    "url": "https://api.cinderreader.com/api/resolve?q={TITLE}+{AUTHOR}",
    "timeout": 15000
  },
  "response": {
    "type": "json",
    "resultsPath": "results",
    "mapping": {
      "title": "title",
      "url": "mirrors.0.url",
      "format": "format",
      "size": "size"
    }
  },
  "downloadResolve": {
    "request": {
      "method": "GET",
      "url": "https://api.cinderreader.com/api/get_links?md5={DOWNLOAD_ID}",
      "timeout": 60000
    }
  }
}

Importing a Source

Same three methods as metadata providers:

  • URL import — Host your JSON at a public URL. Settings → Download Sources → Add → From URL.
  • JSON paste — Paste the JSON directly in the import dialog.

Debrid Services

Debrid services act as a middleman — they take magnet links from your download sources and convert them into direct download URLs. Cinder is not a torrent client and never downloads torrents directly. Instead, it passes magnet links to your debrid provider, which handles everything and returns a downloadable link.

Configure your debrid provider and API key in Settings → Debrid Service.

Setup

1

Get your API key

Sign up for a debrid service and generate an API key from their website.

2

Add it in Cinder

Go to Settings → Debrid Service, select your provider, and paste your API key.

3

That's it

Magnet results from your download sources will now be resolved via your debrid provider into direct download links.