# Stories (القصص) — app contract

Rules and map: `App\Services\Stories`. A story is a photo or a short video with optional text and an optional link, posted by Kapitano or by a seller's shop; it is live for **24 hours** from `published_at`, then ends (the owner can republish it).

## Customers

`GET api/v2/stories` — no token needed, same answer for everyone (cached 60 s per `App-Language`).

```json
{
  "result": true,
  "lifetime_hours": 24,
  "data": [
    {
      "account": { "type": "kapitano|shop", "shop_id": null, "name": "...", "slug": null, "logo": "https://..." },
      "stories": [
        {
          "id": 12, "media_type": "image|video", "media_url": "https://...",
          "text": "...", "published_at": "ISO-8601", "expires_at": "ISO-8601",
          "link": { "type": "shop|category|brand|product|section", "id": 5, "name": "...", "slug": "...", "path": "/product/x", "url": "https://..." }
        }
      ]
    }
  ]
}
```

* `data` is in the order of the circles (set by staff in Admin → Stories → Order of circles); `stories` in the account's own order. Draw one circle per element, play its stories in order.
* `link` is `null` when the story points nowhere **or** its target is no longer on sale. App deep link = `kapitano://` + `path` without the first `/` (`kapitano://product/x`); `section` links open `/section/<slug>`.
* Seen state is kept on the phone (last seen story id per `account.shop_id ?? 0`).
* Photos play ~5 s; videos until they end.

## Seller app (token of a seller)

All under `api/v2/seller/stories`; the shop is the token's. Every story answer is `{result, message, data}` with `data` = the customer shape plus `status` (`live|expired|hidden`), `is_hidden`, `link_type`, `link_id`, `order_level`.

| Call | Body | Notes |
|---|---|---|
| `GET stories` | — | all the shop's stories, live first (their order), then the rest newest first |
| `POST stories` | `media_upload_id`, `text?`, `link_type?`, `link_id?` | upload the file first with `v2/seller/file/upload`; photo ≤ 5 MB (jpg/png/webp/gif), video ≤ 20 MB (mp4/webm); the file must be the seller's own |
| `POST stories/{id}` | same | edit; time window and place stay |
| `POST stories/{id}/republish` | — | live again for 24 h, last in order |
| `POST stories/{id}/hide` | `hidden` (default true) | |
| `DELETE stories/{id}` | — | |
| `POST stories/reorder` | `ids: [..]` | live stories, first first |
| `GET stories/link-options` | `type=product|category|brand|shop`, `q` | `{result, data:[{id,text}]}`; a seller may point at their own shop and products, any category and brand |

Validation failures answer 422 with `{result:false, message}`. A story of another shop answers 404.
