{
    "openapi": "3.0.0",
    "info": {
        "title": "Stories API — Islamic Network",
        "description": "The marifa.org stories corpus: teaching stories from the tradition, with artwork, shared-taxonomy tags and people from the shared registry. 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; empty values are omitted entirely. Lists summarize (no body), the item endpoint expands.",
        "contact": {
            "url": "https://islamic.network",
            "email": "hello@islamic.network"
        },
        "version": "1.0.0"
    },
    "servers": [
        {
            "url": "https://stories.api.islamic.network/v1",
            "description": "Production"
        }
    ],
    "paths": {
        "/assets/{id}": {
            "get": {
                "tags": [
                    "assets"
                ],
                "summary": "Story artwork, streamed from CMS storage",
                "operationId": "getAsset",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "The asset uuid, as carried in `image.id`.",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The image bytes, aggressively cacheable (ids are immutable)."
                    },
                    "404": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        },
        "/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."
                    }
                }
            }
        },
        "/search": {
            "get": {
                "tags": [
                    "search"
                ],
                "summary": "Diacritic-folded search across stories",
                "description": "Matching is diacritic-folded (Ṣ→s, ā→a, ʿ/ʾ dropped, Arabic tashkīl stripped) across every language key of titles, descriptions, bodies, sources and tags — `rumi`, `Rūmī` 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": "forgotten",
                            "minLength": 2
                        }
                    },
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/limit"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Matching stories as references 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"
                    }
                }
            }
        },
        "/stories": {
            "get": {
                "tags": [
                    "stories"
                ],
                "summary": "All stories (summaries, no body), newest first",
                "operationId": "listStories",
                "parameters": [
                    {
                        "name": "tag",
                        "in": "query",
                        "description": "Only stories carrying this topic tag.",
                        "schema": {
                            "type": "string",
                            "example": "remembrance"
                        }
                    },
                    {
                        "name": "person",
                        "in": "query",
                        "description": "Only stories about this person (registry slug).",
                        "schema": {
                            "type": "string",
                            "example": "jalal-ad-din-muhammad-rumi"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/limit"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Paginated story summaries.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "allOf": [
                                        {
                                            "$ref": "#/components/schemas/Envelope"
                                        },
                                        {
                                            "properties": {
                                                "meta": {
                                                    "$ref": "#/components/schemas/Meta"
                                                },
                                                "data": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/StorySummary"
                                                    }
                                                }
                                            },
                                            "type": "object"
                                        }
                                    ]
                                }
                            }
                        }
                    }
                }
            }
        },
        "/stories/{slug}": {
            "get": {
                "tags": [
                    "stories"
                ],
                "summary": "Full story with its body",
                "operationId": "getStory",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/slug"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The story, expanded.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "allOf": [
                                        {
                                            "$ref": "#/components/schemas/Envelope"
                                        },
                                        {
                                            "properties": {
                                                "data": {
                                                    "$ref": "#/components/schemas/StoryDetail"
                                                }
                                            },
                                            "type": "object"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        },
        "/tags": {
            "get": {
                "tags": [
                    "tags"
                ],
                "summary": "The tag index, with counts",
                "description": "The topic slugs used by stories, with their shared-taxonomy names — the same tags collection serves the quotes API, so a topic can be browsed across domains.",
                "operationId": "listTags",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/limit"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Paginated tags, slug order.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "allOf": [
                                        {
                                            "$ref": "#/components/schemas/Envelope"
                                        },
                                        {
                                            "properties": {
                                                "meta": {
                                                    "$ref": "#/components/schemas/Meta"
                                                },
                                                "data": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/Tag"
                                                    }
                                                }
                                            },
                                            "type": "object"
                                        }
                                    ]
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "Name": {
                "description": "Language-keyed text. Keys are BCP 47 tags; `ar-Latn` is the transliteration. Only non-empty languages appear (`{ \"en\": … }` today; translations appear additively).",
                "type": "object",
                "example": {
                    "en": "One Thing Which Must Never Be Forgotten"
                },
                "additionalProperties": {
                    "type": "string"
                }
            },
            "Prose": {
                "description": "Language-keyed prose. `raw` is the authored Markdown; `html` is rendered from it (Arabic-script runs wrapped in `<span lang=\"ar\">`).",
                "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"
                }
            },
            "Image": {
                "description": "The story's artwork, a CMS asset. The url is this API's gateway-cached /assets passthrough; size variants can be added later via query params without breaking.",
                "required": [
                    "id",
                    "url"
                ],
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "url": {
                        "type": "string"
                    }
                },
                "type": "object"
            },
            "PersonRef": {
                "description": "A person the story is about. The slug is the ecosystem-wide identifier owned by the People API; `url` points into that registry. People are never tags.",
                "required": [
                    "slug",
                    "name",
                    "url"
                ],
                "properties": {
                    "slug": {
                        "type": "string",
                        "example": "jalal-ad-din-muhammad-rumi"
                    },
                    "name": {
                        "$ref": "#/components/schemas/Name"
                    },
                    "url": {
                        "type": "string"
                    }
                },
                "type": "object"
            },
            "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"
            },
            "StorySummary": {
                "required": [
                    "slug",
                    "url",
                    "published",
                    "title"
                ],
                "properties": {
                    "slug": {
                        "description": "From the marifa.org post URL (permanent).",
                        "type": "string"
                    },
                    "url": {
                        "type": "string"
                    },
                    "published": {
                        "type": "string",
                        "format": "date",
                        "example": "2025-05-21"
                    },
                    "title": {
                        "$ref": "#/components/schemas/Name"
                    },
                    "description": {
                        "$ref": "#/components/schemas/Name"
                    },
                    "image": {
                        "$ref": "#/components/schemas/Image"
                    },
                    "tags": {
                        "description": "Shared-taxonomy topic slugs.",
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "source": {
                        "description": "The citation exactly as authored.",
                        "type": "string",
                        "example": "Discourses of Rumi ق"
                    },
                    "people": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/PersonRef"
                        }
                    }
                },
                "type": "object"
            },
            "StoryDetail": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/StorySummary"
                    },
                    {
                        "properties": {
                            "body": {
                                "$ref": "#/components/schemas/Prose"
                            }
                        },
                        "type": "object"
                    }
                ]
            },
            "Tag": {
                "required": [
                    "slug",
                    "name",
                    "stories",
                    "url"
                ],
                "properties": {
                    "slug": {
                        "type": "string",
                        "example": "remembrance"
                    },
                    "name": {
                        "type": "string",
                        "example": "Remembrance"
                    },
                    "stories": {
                        "description": "How many stories carry the tag.",
                        "type": "integer"
                    },
                    "url": {
                        "description": "This API's filtered story list.",
                        "type": "string"
                    }
                },
                "type": "object"
            },
            "SearchResult": {
                "required": [
                    "type",
                    "matched_in",
                    "slug",
                    "title",
                    "published",
                    "url"
                ],
                "properties": {
                    "type": {
                        "type": "string",
                        "enum": [
                            "story"
                        ]
                    },
                    "matched_in": {
                        "description": "Language-keyed field paths that matched, e.g. `body.en`.",
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "slug": {
                        "type": "string"
                    },
                    "title": {
                        "$ref": "#/components/schemas/Name"
                    },
                    "published": {
                        "type": "string",
                        "format": "date"
                    },
                    "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": "Story 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 record's canonical URL key.",
                "required": true,
                "schema": {
                    "type": "string"
                }
            }
        }
    },
    "tags": [
        {
            "name": "stories",
            "description": "The stories themselves"
        },
        {
            "name": "tags",
            "description": "The shared topic taxonomy"
        },
        {
            "name": "assets",
            "description": "Story artwork passthrough"
        },
        {
            "name": "search",
            "description": "Diacritic-folded search"
        },
        {
            "name": "documentation",
            "description": "This specification"
        }
    ]
}