# CMS W3 Post Types API Documentation

## Overview
The W3 Post Types API provides access to custom post types (W3CPT) like gallery, portfolio, testimonials, and more. Each post type supports taxonomy filtering, search, and pagination.

## Authentication
All requests require the following headers:

| Header | Required | Description |
|--------|----------|-------------|
| `X-Company-Hash` | Yes | Unique company identifier |
| `Accept` | Yes | `application/json` |

**Missing or invalid company hash returns `422 Unprocessable Entity`**

---

## Endpoints

### 1. List Available Post Types
```
GET /api/w3/post-types
```

Returns all available custom post types for the authenticated company.

**Response:**
```json
{
  "success": true,
  "message": "Post types fetched successfully",
  "data": {
    "post_types": [
      {
        "name": "gallery",
        "label": "Gallery",
        "description": "Photo galleries and albums",
        "count": 15
      },
      {
        "name": "portfolio",
        "label": "Portfolio",
        "description": "Portfolio projects and case studies",
        "count": 23
      },
      {
        "name": "testimonials",
        "label": "Testimonials",
        "description": "Customer testimonials and reviews",
        "count": 42
      },
      {
        "name": "team",
        "label": "Team Members",
        "description": "Team member profiles",
        "count": 8
      },
      {
        "name": "events",
        "label": "Events",
        "description": "Upcoming and past events",
        "count": 12
      }
    ]
  }
}
```

---

### 2. List Posts by Type
```
GET /api/w3/{postType}
```

Returns posts for a specific post type with filtering and pagination support.

**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `postType` | string | Yes | Post type slug (e.g., `gallery`, `portfolio`) |

**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number |
| `per_page` | integer | 10 | Items per page (max 50) |
| `category` | string | - | Filter by category slug |
| `tag` | string | - | Filter by tag slug |
| `author` | integer | - | Filter by author ID |
| `search` | string | - | Search in title and content |
| `status` | string | `published` | Filter by status |
| `order` | string | `desc` | Sort order (`asc`, `desc`) |
| `orderby` | string | `created_at` | Sort field (`created_at`, `title`, `updated_at`) |

**Response:**
```json
{
  "success": true,
  "message": "Posts fetched successfully",
  "data": {
    "posts": [
      {
        "id": 15,
        "title": "Summer Vacation Gallery",
        "slug": "summer-vacation-2025",
        "post_type": "gallery",
        "excerpt": "Beautiful moments from our summer vacation...",
        "featured_image": "https://cdn.example.com/gallery-summer.jpg",
        "author": {
          "id": 3,
          "name": "John Doe"
        },
        "taxonomies": {
          "categories": [
            {"id": 5, "name": "Travel", "slug": "travel"}
          ],
          "tags": [
            {"id": 12, "name": "Summer", "slug": "summer"},
            {"id": 15, "name": "Beach", "slug": "beach"}
          ]
        },
        "meta": {
          "views": 1234,
          "likes": 56
        },
        "created_at": "2025-07-15T10:30:00Z",
        "updated_at": "2026-01-10T14:20:00Z"
      },
      {
        "id": 14,
        "title": "Winter Wonderland",
        "slug": "winter-wonderland-2024",
        "post_type": "gallery",
        "excerpt": "Snowy landscapes and winter activities...",
        "featured_image": "https://cdn.example.com/gallery-winter.jpg",
        "author": {
          "id": 3,
          "name": "John Doe"
        },
        "taxonomies": {
          "categories": [
            {"id": 6, "name": "Nature", "slug": "nature"}
          ],
          "tags": [
            {"id": 20, "name": "Winter", "slug": "winter"}
          ]
        },
        "meta": {
          "views": 890,
          "likes": 42
        },
        "created_at": "2024-12-20T09:15:00Z",
        "updated_at": "2025-12-22T11:30:00Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page": 10,
      "total": 15,
      "total_pages": 2,
      "has_more": true
    },
    "filters": {
      "category": null,
      "tag": null,
      "author": null,
      "search": null
    }
  }
}
```

---

### 3. Get Single Post by Slug
```
GET /api/w3/{postType}/{slug}
```

Returns a single post with full content.

**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `postType` | string | Yes | Post type slug |
| `slug` | string | Yes | Post slug |

**Response:**
```json
{
  "success": true,
  "message": "Post fetched successfully",
  "data": {
    "post": {
      "id": 15,
      "title": "Summer Vacation Gallery",
      "slug": "summer-vacation-2025",
      "post_type": "gallery",
      "content": [
        {
          "type": "heading",
          "level": 1,
          "text": "Summer Vacation 2025"
        },
        {
          "type": "paragraph",
          "text": "Here are some amazing photos from our summer vacation..."
        },
        {
          "type": "gallery",
          "images": [
            {
              "src": "https://cdn.example.com/img1.jpg",
              "alt": "Beach sunset",
              "caption": "Beautiful sunset at the beach"
            },
            {
              "src": "https://cdn.example.com/img2.jpg",
              "alt": "Mountain view",
              "caption": "Mountain hiking trail"
            }
          ]
        }
      ],
      "excerpt": "Beautiful moments from our summer vacation...",
      "featured_image": "https://cdn.example.com/gallery-summer.jpg",
      "author": {
        "id": 3,
        "name": "John Doe",
        "avatar": "https://cdn.example.com/avatar-john.jpg",
        "bio": "Travel photographer and blogger"
      },
      "taxonomies": {
        "categories": [
          {
            "id": 5,
            "name": "Travel",
            "slug": "travel",
            "description": "Travel photos and stories"
          }
        ],
        "tags": [
          {"id": 12, "name": "Summer", "slug": "summer"},
          {"id": 15, "name": "Beach", "slug": "beach"},
          {"id": 18, "name": "Adventure", "slug": "adventure"}
        ]
      },
      "meta": {
        "views": 1234,
        "likes": 56,
        "seo_title": "Summer Vacation Gallery 2025",
        "seo_description": "Explore our summer vacation photos",
        "custom_fields": {
          "location": "California Coast",
          "duration": "7 days"
        }
      },
      "status": "published",
      "created_at": "2025-07-15T10:30:00Z",
      "updated_at": "2026-01-10T14:20:00Z",
      "published_at": "2025-07-15T12:00:00Z"
    },
    "related": [
      {
        "id": 14,
        "title": "Winter Wonderland",
        "slug": "winter-wonderland-2024",
        "featured_image": "https://cdn.example.com/gallery-winter.jpg"
      }
    ]
  }
}
```

---

## Response Fields

### Post Object (List)

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Post identifier |
| `title` | string | Post title |
| `slug` | string | URL slug |
| `post_type` | string | Custom post type |
| `excerpt` | string | Short excerpt/summary |
| `featured_image` | string\|null | Featured image URL |
| `author` | object | Author information |
| `taxonomies` | object | Categories and tags |
| `meta` | object | Custom metadata |
| `created_at` | string | Creation timestamp (ISO 8601) |
| `updated_at` | string | Last update timestamp (ISO 8601) |

### Post Object (Single)

Includes all list fields plus:

| Field | Type | Description |
|-------|------|-------------|
| `content` | array | Full content blocks |
| `status` | string | Publication status |
| `published_at` | string | Publication timestamp |
| `related` | array | Related posts |

### Content Block Types

| Type | Description | Example |
|------|-------------|----------|
| `heading` | Heading text | `{"type": "heading", "level": 1, "text": "..."}` |
| `paragraph` | Paragraph text | `{"type": "paragraph", "text": "..."}` |
| `image` | Single image | `{"type": "image", "src": "...", "alt": "..."}` |
| `gallery` | Image gallery | `{"type": "gallery", "images": [...]}` |
| `video` | Video embed | `{"type": "video", "url": "..."}` |
| `html` | Custom HTML | `{"type": "html", "html": "..."}` |

---

## Error Responses

### 404 Not Found (Invalid Post Type)
```json
{
  "success": false,
  "message": "Post type 'invalid-type' not found"
}
```

### 404 Not Found (Post Not Found)
```json
{
  "success": false,
  "message": "Post not found"
}
```

### 422 Unprocessable Entity (Invalid Company Hash)
```json
{
  "success": false,
  "message": "Missing or invalid X-Company-Hash header."
}
```

---

## Behavior Notes

- **No Caching**: All post data fetched fresh from database
- **Company Scoping**: Posts filtered by authenticated company
- **Published Only**: Default filter shows only published posts
- **Taxonomy Support**: Full support for categories and tags
- **Search**: Searches in title and content fields
- **Related Posts**: Automatically includes related posts (single post endpoint)

---

## Example Usage

### JavaScript (Fetch API)
```javascript
const companyHash = 'abc123def456';

// Get gallery posts filtered by category
const response = await fetch('https://api.example.com/api/w3/gallery?category=travel&per_page=5', {
  headers: {
    'X-Company-Hash': companyHash,
    'Accept': 'application/json'
  }
});

const data = await response.json();
const posts = data.data.posts;

posts.forEach(post => {
  console.log(`${post.title} - ${post.excerpt}`);
});
```

### React Component
```jsx
import { useEffect, useState } from 'react';

function GalleryList({ companyHash, category }) {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const url = category 
      ? `https://api.example.com/api/w3/gallery?category=${category}`
      : 'https://api.example.com/api/w3/gallery';

    fetch(url, {
      headers: {
        'X-Company-Hash': companyHash,
        'Accept': 'application/json'
      }
    })
    .then(res => res.json())
    .then(data => {
      setPosts(data.data.posts);
      setLoading(false);
    });
  }, [companyHash, category]);

  if (loading) return <div>Loading galleries...</div>;

  return (
    <div className="gallery-grid">
      {posts.map(post => (
        <div key={post.id} className="gallery-item">
          <img src={post.featured_image} alt={post.title} />
          <h3>{post.title}</h3>
          <p>{post.excerpt}</p>
        </div>
      ))}
    </div>
  );
}
```

### cURL
```bash
# Get all galleries
curl -X GET "https://api.example.com/api/w3/gallery" \
  -H "X-Company-Hash: abc123def456" \
  -H "Accept: application/json"

# Get specific gallery by slug
curl -X GET "https://api.example.com/api/w3/gallery/summer-vacation-2025" \
  -H "X-Company-Hash: abc123def456" \
  -H "Accept: application/json"

# Search galleries
curl -X GET "https://api.example.com/api/w3/gallery?search=beach&tag=summer" \
  -H "X-Company-Hash: abc123def456" \
  -H "Accept: application/json"
```

### PHP
```php
$companyHash = 'abc123def456';

// Get posts by type
$response = Http::withHeaders([
    'X-Company-Hash' => $companyHash,
    'Accept' => 'application/json',
])->get('https://api.example.com/api/w3/gallery', [
    'category' => 'travel',
    'per_page' => 5
]);

$posts = $response->json()['data']['posts'];

foreach ($posts as $post) {
    echo "<h2>{$post['title']}</h2>";
    echo "<p>{$post['excerpt']}</p>";
}
```
