Documentation

Extend and configure Ink & Echo.

Ink & Echo can import local media, connect to supported services, and accept provider JSON for metadata and source search. This page documents the JSON shape the app actually accepts today, with examples you can adapt for your own lawful sources.

Provider JSON

Metadata providers use kind: "metadata"
Source providers use kind: "search"
Import by paste, file, or remote JSON URL

Start

How Ink & Echo providers work

Metadata providers

Metadata providers enrich book records with title, author, cover, description, narrator, series, identifiers, genre fields, and discovery rows.

Source providers

Source providers return media candidates. Results can point to direct audio, direct ebooks, packages, magnet links, or info hashes that Ink & Echo routes through debrid.
Provider JSON is configuration, not executable code. Ink & Echo reads JSON, validates the shape, calls the configured HTTP endpoint when needed, and maps JSON response fields into the app model.

Workflow

Quick Start

  1. 1Choose the provider kindUse metadata when the API returns catalog information. Use search when it returns downloadable or streamable media candidates.
  2. 2Write a JSON configDefine provider identity, request URL, response resultsPath, and field mapping. Start with the examples below.
  3. 3Import it in SettingsOpen Metadata Providers or Sources, then paste JSON, load a .json file, or enter a remote JSON URL.
  4. 4Validate and addInk & Echo previews the provider before saving it. If validation fails, the error usually points to the missing request, response, label, or results array.

Import

Import Methods

Paste JSON

Paste the full provider definition. The app trims whitespace and validates before saving.

Local .json file

Import a JSON file from the device file picker. The file should contain one provider object.

Remote JSON URL

Provide an HTTP or HTTPS URL that returns a JSON provider definition. The URL is validated and the provider is only saved after you confirm.
JSONminimal-shape.json
Copy
{
  "kind": "metadata",
  "id": "my-provider",
  "name": "My Provider",
  "description": "Catalog metadata for my private library.",
  "trustLabel": "Private",
  "lawfulNote": "Only use this with catalogs you are allowed to access.",
  "capabilities": {
    "search": true
  },
  "search": {
    "request": {
      "method": "GET",
      "url": "https://api.example.com/books?q={QUERY}",
      "headers": {
        "Accept": "application/json"
      },
      "timeout": 15000
    },
    "response": {
      "type": "json",
      "resultsPath": "items",
      "mapping": {
        "title": "title",
        "author": "author"
      }
    }
  }
}

Metadata

Metadata Providers

Schema Overview

A metadata provider must include a display name and at least one data source: staticentries, remote search, remotediscover sections, or static results.

FieldDescription
kind*Declares this file as a metadata provider.
idStable identifier. If omitted, Ink & Echo generates one from the provider name.
name or label*Display name shown in Settings and result details.
versionOptional version string for your own tracking.
descriptionShort summary shown in the provider review step.
trustLabelExample: Built In, User Added, Private API, Self Hosted.
lawfulNoteRequired in practice for clarity. Tell users what the provider is allowed to access.
capabilitiesEnabled capability names. Truthy object keys become capabilities.
searchRemote JSON search definition.
discoverOptional discovery sections for the Discover screen.
entriesStatic metadata records bundled directly in the JSON.
rateLimitOptional requestsPerMinute and retryAfterMs hints.

Capabilities

Ink & Echo accepts capabilities as either an object or an array. Object keys with truthy values become capability names.

JSON
Copy
{
  "capabilities": {
    "search": true,
    "discover": true,
    "enrichment": true
  }
}

Discovery Configuration

Discovery sections appear as curated rows. Each section needs its own request and response parser.

JSON
Copy
"discover": {
  "sections": [
    {
      "id": "new-releases",
      "title": "New Releases",
      "icon": "sparkles",
      "request": {
        "method": "GET",
        "url": "https://api.example.com/books/new"
      },
      "response": {
        "type": "json",
        "resultsPath": "items",
        "mapping": {
          "title": "title",
          "author": "author",
          "cover": "coverUrl"
        }
      }
    }
  ]
}
In Ink & Echo, discovery sections do not inherit the search response parser. Includeresponse on every section.

Field Mapping

Mapping values use dot and bracket paths. For example, authors[0].namereads the first author name from an array.

FieldDescription
title*Book title.
author*Primary author. Arrays can use index paths like authors[0].name.
subtitleSubtitle.
narratorNarrator for audiobook metadata.
series, seriesIndexSeries name and position.
descriptionSynopsis or catalog description.
coverCover image URL or a value transformed by response.templates.cover.
languageLanguage label or code.
publisherPublisher name.
publishedYear, releaseDateYear or date fields.
isbn, asinExternal identifiers.
runtimeMinutesRuntime in minutes.
ratingRating value.
genres, categories, tagsGenre/category/tag values.
Template
Copy
"response": {
  "type": "json",
  "resultsPath": "books",
  "mapping": {
    "title": "volume.title",
    "author": "volume.authors[0].name",
    "publishedYear": "volume.year"
  },
  "templates": {
    "cover": "https://img.example.com/covers/{value}.jpg"
  }
}

Static Entries

Static entries are useful for private catalogs, test data, or small curated metadata sets.

JSONprivate-metadata.json
Copy
{
  "kind": "metadata",
  "id": "private-notes",
  "name": "Private Notes",
  "trustLabel": "Local",
  "lawfulNote": "Personal metadata maintained by the user.",
  "entries": [
    {
      "title": "Example Book",
      "author": "Example Author",
      "narrator": "Example Narrator",
      "series": "Example Series",
      "seriesIndex": "1",
      "description": "A private metadata record.",
      "cover": "https://example.com/cover.jpg",
      "genres": ["Fiction"]
    }
  ]
}

Complete Metadata Example

JSONmetadata-provider.json
Copy
{
  "kind": "metadata",
  "id": "example-audio-catalog",
  "name": "Example Audio Catalog",
  "version": "1.0.0",
  "description": "Searches a fictional audiobook catalog API.",
  "trustLabel": "User Added",
  "lawfulNote": "Use only with APIs and metadata you are allowed to access.",
  "capabilities": {
    "search": true,
    "discover": true
  },
  "search": {
    "request": {
      "method": "GET",
      "url": "https://api.example.com/audiobooks/search?q={QUERY}",
      "headers": {
        "Accept": "application/json"
      },
      "timeout": 15000
    },
    "response": {
      "type": "json",
      "resultsPath": "data.items",
      "mapping": {
        "title": "title",
        "author": "author.name",
        "narrator": "narrator.name",
        "cover": "images.large",
        "description": "description",
        "runtimeMinutes": "durationMinutes",
        "series": "series.name",
        "seriesIndex": "series.position",
        "genres": "genres"
      }
    }
  },
  "discover": {
    "sections": [
      {
        "id": "popular",
        "title": "Popular",
        "icon": "sparkles",
        "request": {
          "method": "GET",
          "url": "https://api.example.com/audiobooks/popular"
        },
        "response": {
          "type": "json",
          "resultsPath": "data.items",
          "mapping": {
            "title": "title",
            "author": "author.name",
            "cover": "images.large"
          }
        }
      }
    ]
  },
  "rateLimit": {
    "requestsPerMinute": 20,
    "retryAfterMs": 3000
  }
}

Sources

Source Providers

Schema Overview

Source providers return records that can become downloadable or playable library items. A source must include remote search, static results, or discovery sections.

FieldDescription
kind*Declares this file as a source provider.
idStable identifier. Recommended for updates and persistence.
name or label*Display name shown under Sources.
descriptionShort provider summary.
trustLabelHuman-readable trust/source label.
lawfulNoteExplains what the source is for and what the user is responsible for.
searchRemote request and response parser.
resultsStatic records, useful for samples or private fixed catalogs.
discoverOptional sections using the same request/response shape.
rateLimitOptional throttling hints.

Source Types

Direct audio

Map audioUrl to a direct audio file or supported package URL.

Direct ebook

Map ebookUrl to epub, pdf, txt, or md content.

Debrid-backed

Map magnet, magnetUrl, orinfoHash. Ink & Echo routes these through Real-Debrid or TorBox.

Request Configuration

Requests support GET and POST. Body templates can be strings, objects, or arrays.

JSON
Copy
"search": {
  "request": {
    "method": "POST",
    "url": "https://api.example.com/search",
    "headers": {
      "Accept": "application/json",
      "Content-Type": "application/json"
    },
    "body": {
      "query": "{TITLE} {AUTHOR}",
      "limit": 25
    },
    "timeout": 30000
  },
  "response": {
    "type": "json",
    "resultsPath": "hits",
    "mapping": {
      "title": "name",
      "author": "author",
      "audioUrl": "downloadUrl"
    }
  }
}

Response Parsing

resultsPath points to an array or object in the API response. If it resolves to an array, each object becomes a candidate result.

JSON
Copy
"response": {
  "type": "json",
  "resultsPath": "data.results",
  "mapping": {
    "title": "title",
    "author": "creator",
    "archiveUrl": "package.url",
    "fileType": "package.format",
    "sizeBytes": "package.bytes"
  }
}

Field Mapping

At least one usable media field is needed for a source result to become actionable:audioUrl, ebookUrl,archiveUrl, magnet, orinfoHash.

FieldDescription
title*Result title shown in search.
authorAuthor used for filtering and metadata matching.
audioUrlDirect audio file or package URL.
ebookUrlDirect ebook URL. Supported file names include epub, pdf, txt, and md.
archiveUrlPackage URL such as a zip file that the import pipeline can scan.
magnet or magnetUrlMagnet link. Ink & Echo routes it through the selected debrid service.
infoHashTorrent info hash, also routed through debrid.
formataudiobook, ebook, or hybrid when the source provides it.
accessOptional direct or debrid hint. Ink & Echo also infers this from URLs.
fileTypeContainer or format label such as M4B, MP3, EPUB, PDF, ZIP.
sizeBytesByte size. Aliases like size, bytes, fileSize, and contentLength are supported.
seeders, leechersAvailability hints for debrid-backed results.

Example Configs

JSONdirect-audio-source.json
Copy
{
  "kind": "search",
  "id": "public-audio-example",
  "name": "Public Audio Example",
  "description": "Fictional public-domain audio API.",
  "trustLabel": "Public Catalog",
  "lawfulNote": "Only returns public-domain media from this fictional API.",
  "capabilities": {
    "search": true,
    "acquire": true
  },
  "search": {
    "request": {
      "method": "GET",
      "url": "https://api.example.com/public-audio?q={QUERY}",
      "headers": {
        "Accept": "application/json"
      },
      "timeout": 15000
    },
    "response": {
      "type": "json",
      "resultsPath": "items",
      "mapping": {
        "title": "title",
        "author": "author",
        "audioUrl": "audio.downloadUrl",
        "cover": "coverUrl",
        "fileType": "audio.format",
        "runtimeMinutes": "durationMinutes"
      }
    }
  }
}
JSONdebrid-source.json
Copy
{
  "kind": "search",
  "id": "private-index-example",
  "name": "Private Index Example",
  "description": "Fictional private index that returns magnet links.",
  "trustLabel": "Private",
  "lawfulNote": "User is responsible for the index and content they access.",
  "capabilities": {
    "search": true,
    "debrid": true
  },
  "search": {
    "request": {
      "method": "POST",
      "url": "https://api.example.com/index/search",
      "headers": {
        "Accept": "application/json",
        "Content-Type": "application/json"
      },
      "body": {
        "query": "{TITLE} {AUTHOR}"
      },
      "timeout": 30000
    },
    "response": {
      "type": "json",
      "resultsPath": "results",
      "mapping": {
        "title": "name",
        "author": "author",
        "magnet": "magnet",
        "infoHash": "hash",
        "sizeBytes": "size",
        "seeders": "seeders",
        "leechers": "leechers"
      }
    }
  },
  "rateLimit": {
    "requestsPerMinute": 30,
    "retryAfterMs": 2000
  }
}

Runtime

Template Variables

Ink & Echo currently substitutes three placeholders in request URLs, headers, and bodies.

FieldDescription
{QUERY}*The raw search query.
{TITLE}The selected title, falling back to query.
{AUTHOR}The selected author, or an empty string.
Do not document or depend on placeholders such as USER_API_KEY, PAGE, ISBN, ASIN, or NARRATOR unless the app runtime adds them later.

Services

Debrid Services

Real-Debrid

Real-Debrid uses device authorization in the app. Magnet and hash results can be resolved into direct files when the provider account supports the item.

TorBox

TorBox uses an API key and supports torrent, web download, and usenet library items. Saved provider links are refreshed before redownload.
Ink & Echo is not a torrent client. Debrid-backed source results are handed to the selected provider, then the resolved direct URL is submitted to the import pipeline.

Fixes

Troubleshooting

Provider will not import

Check that the JSON has a name or label, a valid kind, and at least one data source: entries, results, search, or discover.

No results appear

Confirm the API returns JSON, the resultsPath points to an array or object, and title/author mappings point to real fields.

Result cannot download

Source results need audioUrl, ebookUrl, archiveUrl, magnet, or infoHash. Metadata-only results enrich the library but do not provide media.

Provider responsibility

Only add providers you control or have permission to use. The provider JSON should include a lawfulNote that accurately describes the source.

Help

Support

Email support

Send app issues to support@inkandecho.app. Include your device model, iOS version, app version, provider kind, and the validation error if one appears.

Discord

Use Discord for provider examples, JSON review, setup notes, and roadmap feedback.