openapi: 3.0.0 info: title: 'People API — Islamic Network' description: "The ecosystem's canonical people registry: person slugs minted here are the identifiers referenced by events, quotes, stories and any future service. Each person carries a biography plus sync-time aggregations of what the ecosystem knows about them. Every response is wrapped in the `{code, status, data}` envelope shared across Islamic Network APIs; list endpoints add a `meta` sibling. Language keys are BCP 47 tags (`ar`, `en`, `ur`, …, and `ar-Latn` for transliteration); empty values are omitted entirely. Lists summarize, item endpoints expand." contact: url: 'https://islamic.network' email: hello@islamic.network version: 1.0.0 servers: - url: 'https://people.api.islamic.network/v1' description: Production paths: '/documentation/openapi/{format}': get: tags: - documentation summary: 'This specification, as OpenAPI 3' operationId: getOpenApiSpec parameters: - name: format in: path required: true schema: type: string enum: - yaml - json responses: '200': description: 'The OpenAPI document.' /people: get: tags: - people summary: 'The registry (summaries), slug order' description: 'Person slugs minted here are the ecosystem-wide identifiers referenced by events, quotes, stories and any future service.' operationId: listPeople parameters: - name: kind in: query description: 'Only people of one kind. The enum is additive; more values may be introduced.' schema: type: string enum: - prophet - wali - family-of-the-prophet - companion - scholar - ruler - other - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' responses: '200': description: 'Paginated person summaries.' content: application/json: schema: allOf: - { $ref: '#/components/schemas/Envelope' } - { properties: { meta: { $ref: '#/components/schemas/Meta' }, data: { type: array, items: { $ref: '#/components/schemas/PersonSummary' } } }, type: object } '/people/{slug}': get: tags: - people summary: 'Full person: biography + what the ecosystem knows' description: 'The aggregation blocks (events, quotes, stories) carry counts, a preview of up to 3 records and the URL of the full list; they are omitted when the ecosystem has nothing. Aggregations are assembled at sync time — one snapshot version, one coherent set of cross-links.' operationId: getPerson parameters: - $ref: '#/components/parameters/slug' responses: '200': description: 'The person, expanded.' content: application/json: schema: allOf: - { $ref: '#/components/schemas/Envelope' } - { properties: { data: { $ref: '#/components/schemas/PersonDetail' } }, type: object } '404': $ref: '#/components/responses/Error' '/people/{slug}/events': get: tags: - people summary: "The person's full event list" operationId: getPersonEvents parameters: - $ref: '#/components/parameters/slug' - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' responses: '200': description: 'The person and their events.' content: application/json: schema: allOf: - { $ref: '#/components/schemas/Envelope' } - { properties: { meta: { $ref: '#/components/schemas/Meta' }, data: { properties: { person: { $ref: '#/components/schemas/PersonSummary' }, events: { type: array, items: { $ref: '#/components/schemas/EventRef' } } }, type: object } }, type: object } '404': $ref: '#/components/responses/Error' '/people/{slug}/quotes': get: tags: - people summary: "The person's full quote list" operationId: getPersonQuotes parameters: - $ref: '#/components/parameters/slug' - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' responses: '200': description: 'The person and their quotes.' content: application/json: schema: allOf: - { $ref: '#/components/schemas/Envelope' } - { properties: { meta: { $ref: '#/components/schemas/Meta' }, data: { properties: { person: { $ref: '#/components/schemas/PersonSummary' }, quotes: { type: array, items: { $ref: '#/components/schemas/QuoteRef' } } }, type: object } }, type: object } '404': $ref: '#/components/responses/Error' /search: get: tags: - search summary: 'Diacritic-folded search across people' description: 'Matching is diacritic-folded (Ṣ→s, ā→a, ʿ/ʾ dropped, Arabic tashkīl stripped) across every language key of names, honorifics and biographies — `husayn`, `Ḥusayn` and `الْحُسَيْن` all match. Multiple query words AND together.' operationId: search parameters: - name: q in: query description: 'The query; at least 2 characters.' required: true schema: type: string example: husayn minLength: 2 - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' responses: '200': description: 'Matching people as summaries with the field paths that matched.' content: application/json: schema: allOf: - { $ref: '#/components/schemas/Envelope' } - { properties: { meta: { $ref: '#/components/schemas/Meta' }, data: { properties: { query: { type: string }, total: { type: integer }, results: { type: array, items: { $ref: '#/components/schemas/SearchResult' } } }, type: object } }, type: object } '400': $ref: '#/components/responses/Error' components: schemas: Name: description: 'Language-keyed text. Keys are BCP 47 tags; `ar-Latn` is the transliteration. Only non-empty languages appear.' type: object example: ar: 'الْحُسَيْنُ بْنُ عَلِيٍّ' ar-Latn: 'Ḥusayn ibn ʿAlī' en: 'Husayn ibn Ali' additionalProperties: type: string Prose: description: 'Language-keyed prose. `raw` is the authored Markdown; `html` is rendered from it (Arabic-script runs wrapped in ``).' type: object additionalProperties: required: - raw - html properties: raw: description: "OpenAPI document root and shared components. Endpoint annotations live on\nthe v1 controllers; the assembled spec is served by\nGET /v1/documentation/openapi/{yaml|json}." type: string html: type: string type: object Year: description: 'A year pair; either calendar may be absent (pre-Hijra years have only `gregorian`). Consumers compose lifespan displays ("4 – 61 AH") themselves.' properties: hijri: type: integer example: 61 gregorian: type: integer example: 680 type: object Kind: description: 'What kind of person this is. Additive; more values may be introduced.' type: string enum: - prophet - wali - family-of-the-prophet - companion - scholar - ruler - other Meta: description: 'Pagination block, sibling of `data` on list endpoints.' required: - total - page - limit - pages properties: total: type: integer page: type: integer limit: type: integer pages: type: integer type: object Envelope: required: - code - status properties: code: type: integer example: 200 status: type: string example: OK type: object PersonSummary: required: - slug - name - kind - url properties: slug: type: string example: husayn-ibn-ali name: $ref: '#/components/schemas/Name' honorific: $ref: '#/components/schemas/Name' kind: $ref: '#/components/schemas/Kind' url: type: string type: object PersonDetail: required: - id - slug - url - name - kind properties: id: type: string slug: type: string url: type: string name: $ref: '#/components/schemas/Name' honorific: $ref: '#/components/schemas/Name' kind: $ref: '#/components/schemas/Kind' birth: $ref: '#/components/schemas/Year' death: $ref: '#/components/schemas/Year' biography: $ref: '#/components/schemas/Prose' events: description: "Omitted when the ecosystem has none. The url is this API's /people/{slug}/events." properties: total: type: integer url: type: string preview: description: 'Up to 3 records.' type: array items: $ref: '#/components/schemas/EventRef' type: object quotes: description: "Omitted when the ecosystem has none. The url is this API's /people/{slug}/quotes." properties: total: type: integer url: type: string preview: type: array items: $ref: '#/components/schemas/QuoteRef' type: object stories: description: "Omitted when the ecosystem has none. The url is the stories API's ?person= filter." properties: total: type: integer url: type: string preview: type: array items: $ref: '#/components/schemas/StoryRef' type: object type: object EventRef: description: "An event involving the person — the events API's summary shape." required: - slug - title - date - url properties: slug: type: string title: $ref: '#/components/schemas/Name' date: required: - month - day properties: month: type: integer maximum: 12 minimum: 1 day: type: integer maximum: 30 minimum: 1 type: object year: $ref: '#/components/schemas/Year' url: description: 'The canonical event on the events API.' type: string type: object QuoteRef: description: 'A quote by the person.' required: - id - original - url properties: id: type: integer example: 91 original: required: - language - text properties: language: type: string example: en text: type: string type: object source: type: string url: description: 'The canonical quote on the quotes API.' type: string type: object StoryRef: description: 'A story about the person.' required: - slug - title - published - url properties: slug: type: string title: $ref: '#/components/schemas/Name' published: type: string format: date url: description: 'The canonical story on the stories API.' type: string type: object SearchResult: required: - type - matched_in - id - slug - name - kind - url properties: type: type: string enum: - person matched_in: description: 'Language-keyed field paths that matched, e.g. `name.ar-Latn`.' type: array items: type: string id: type: string slug: type: string name: $ref: '#/components/schemas/Name' honorific: $ref: '#/components/schemas/Name' kind: $ref: '#/components/schemas/Kind' url: type: string type: object responses: Error: description: 'Error — the envelope with a message string as `data`.' content: application/json: schema: required: - code - status - data properties: code: type: integer example: 404 status: type: string example: 'NOT FOUND' data: type: string example: 'Person not found.' type: object parameters: page: name: page in: query description: 'Page number (default 1).' schema: type: integer default: 1 minimum: 1 limit: name: limit in: query description: 'Records per page (default 50, max 200).' schema: type: integer default: 50 maximum: 200 minimum: 1 slug: name: slug in: path description: "The person's canonical, ecosystem-wide slug." required: true schema: type: string tags: - name: people description: 'The registry and per-person aggregations' - name: search description: 'Diacritic-folded search' - name: documentation description: 'This specification'