---
name: turastories
version: 1.0.0
description: Ephemeral story content API. Post frame-based stories that expire after 24 hours. Track views. Add stories to any app.
homepage: https://www.turastories.com
metadata: {"category":"social","api_base":"https://www.turastories.com/api/v1"}
---

# TuraStories

**Ephemeral story content via API.**

Add 24-hour tap-through stories to any app. Users post frames (image, text, or mixed), viewers tap through in order, and content expires automatically. Part of Tura Cloud. Authentication uses TuraLogin API keys (Bearer token).

## Skill Files

| File | URL |
|------|-----|
| **SKILL.md** (this file) | `https://www.turastories.com/SKILL.md` |
| **AGENTS.md** | `https://www.turastories.com/AGENTS.md` |
| **API.md** | `https://www.turastories.com/API.md` |
| **QUICKSTART.md** | `https://www.turastories.com/QUICKSTART.md` |
| **skill.json** (metadata) | `https://www.turastories.com/skill.json` |

**Base URL:** `https://www.turastories.com/api/v1`

**Authentication:** `Authorization: Bearer <API_KEY>` — Use TuraLogin API keys from https://www.turalogin.com/dashboard/keys

---

## Overview

TuraStories is an ephemeral content API:

✅ **Stories** — Create stories with one or more frames; auto-expire after 24 hours  
✅ **Frames** — Text, image, or mixed frame types with optional overlays  
✅ **Views** — Track who viewed each frame and when  
✅ **Custom expiry** — Override the default TTL (up to 7 days)  

You handle:
- Frontend story renderer (tap navigation, progress bars)
- Media uploads (use TuraStore for images/video)
- User identity (use TuraLogin)
- Push notifications on new stories (use TuraText)

---

## Quick Start

### 1. Get Your API Key

TuraStories uses **TuraLogin API keys**. Get one at https://www.turalogin.com/dashboard/keys

- Test: `tl_test_xxxxxxxxxxxxxxxx`
- Production: `tl_live_xxxxxxxxxxxxxxxx`

### 2. Create a Story

```bash
curl -X POST https://www.turastories.com/api/v1/stories/create \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_123",
    "frames": [
      { "type": "text", "content": "Hello world!" },
      { "type": "image", "imageUrl": "https://cdn.example.com/photo.jpg" }
    ]
  }'
```

**Response:**
```json
{
  "id": "story_abc123xyz",
  "userId": "user_123",
  "frames": [
    { "id": "frame_1", "type": "text", "content": "Hello world!", "order": 0 },
    { "id": "frame_2", "type": "image", "imageUrl": "https://cdn.example.com/photo.jpg", "order": 1 }
  ],
  "expiresAt": "2026-02-23T10:00:00Z",
  "viewCount": 0,
  "createdAt": "2026-02-22T10:00:00Z"
}
```

### 3. Get Stories for a User

```bash
curl "https://www.turastories.com/api/v1/stories?userId=user_123" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

---

## Full API Reference

### POST /api/v1/stories/create

Create a new story.

**Request Body:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `userId` | string | Yes | Author's user ID |
| `frames` | array | Yes | Ordered array of frame objects |
| `expiresIn` | number | No | Seconds until expiry (default: 86400 = 24h, max: 604800 = 7 days) |

**Frame object:**

| Field | Type | Description |
|-------|------|-------------|
| `type` | string | `text`, `image`, `video` |
| `content` | string | Text content (for `text` type) |
| `imageUrl` | string | Image URL (for `image` type) |
| `videoUrl` | string | Video URL (for `video` type) |
| `caption` | string | Optional caption overlay |
| `duration` | number | Display duration in ms (default: 5000) |

---

### GET /api/v1/stories

Get active (non-expired) stories.

**Query Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | Filter by author |
| `limit` | number | Max results (default: 20, max: 50) |
| `cursor` | string | Pagination cursor |

---

### GET /api/v1/stories/:id

Get a single story with all frames.

---

### DELETE /api/v1/stories/:id

Delete a story before it expires.

---

### POST /api/v1/stories/:id/views

Record a view event for a frame.

**Request Body:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `viewerId` | string | Yes | Viewer's user ID |
| `frameId` | string | Yes | Frame that was viewed |

---

### GET /api/v1/stories/:id/views

Get view counts per frame.

**Success Response (200):**
```json
{
  "storyId": "story_abc123",
  "totalViews": 47,
  "frames": [
    { "frameId": "frame_1", "views": 47 },
    { "frameId": "frame_2", "views": 31 }
  ]
}
```

---

## Framework Examples

### Next.js (App Router)

```typescript
// app/api/stories/route.ts
export async function POST(request: Request) {
  const body = await request.json();

  const res = await fetch('https://www.turastories.com/api/v1/stories/create', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.TURASTORIES_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });

  return Response.json(await res.json(), { status: res.status });
}

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);

  const res = await fetch(
    `https://www.turastories.com/api/v1/stories?${searchParams.toString()}`,
    { headers: { 'Authorization': `Bearer ${process.env.TURASTORIES_API_KEY}` } }
  );

  return Response.json(await res.json());
}
```

---

## Error Codes

| Status | Error | Description |
|--------|-------|-------------|
| 400 | `userId is required` | Missing user ID |
| 400 | `frames is required` | No frames provided |
| 400 | `frames must have at least one item` | Empty frames array |
| 400 | `Invalid frame type` | Must be text, image, or video |
| 401 | `Unauthorized` | Missing or invalid API key |
| 404 | `Story not found` | Invalid or expired story ID |
| 410 | `Story expired` | Story TTL has passed |
| 429 | `Too many requests` | Rate limit exceeded |

---

## Support & Resources

- 🌐 Website: https://www.turastories.com
- 🔑 API Keys: https://www.turalogin.com/dashboard/keys
