# Logzly Blog API v1 Documentation

Welcome to the Logzly Open API. This interface is designed for independent developers, automation scripts, and third-party integration tools to achieve lightweight, programmatic article synchronization and management.

## 1. Global Conventions

### 1.1 Base URL

All API requests are routed through the following base endpoint:
`https://logzly.com/api/v1`

### 1.2 Authentication

The API enforces authentication via an **API Key**. You must supply the `X-API-KEY` field in the **HTTP Header** of every single request.

* **Header Name**: `X-API-KEY`
* **Header Value**: Your unique Secret Key generated within your Profile Settings.

> **Warning:** Requests missing a valid `X-API-KEY` header will instantly be rejected with a `401 Unauthorized` response.

### 1.3 Data Formats

* Request Payload Format (POST/PUT): `application/json`
* Response Payload Format: `application/json`
* Timestamp Encoding: `yyyy-MM-dd HH:mm:ss` (or standard ISO-8601 strings)

---

## 2. Endpoints

### 2.1 Fetch Blog Posts

Retrieves a paginated list of all active posts and drafts belonging to the authenticated user (excludes posts residing in the trash/deleted repository).

* **HTTP Method**: `GET`
* **Path**: `/posts`
* **Query Parameters**:
  | Parameter | Type | Required | Default | Description |
  | --- | --- | --- | --- | --- |
  | `page` | Integer | No | `0` | Zero-based page index |
  | `size` | Integer | No | `20` | Maximum number of records per page |


* **Request Example**:
```bash
curl -X GET "https://logzly.com/api/v1/posts?page=0&size=2" \
     -H "X-API-KEY: your_secret_key_here"

```


* **Success Response (200 OK)**:
```json
{
  "username": "geek_coder",
  "currentPage": 0,
  "posts": [
    {
      "id": 12,
      "title": "My Automated Post via Python",
      "slug": "my-automated-post-via-python",
      "status": "PUBLISHED",
      "pinned": true,
      "createdAt": "2026-06-10T03:58:18"
    }
  ]
}

```



---

### 2.2 Create New Post

Spawns a new blog record, which can either be staged as a draft or published immediately. Creating a post successfully triggers an automated asynchronous full site compilation (SSG bake).

* **HTTP Method**: `POST`
* **Path**: `/posts`
* **JSON Body Parameters**:
  | Field | Type | Required | Description |
  | --- | --- | --- | --- |
  | `title` | String | **Yes** | The title of your post. Cannot be blank. |
  | `slug` | String | **Yes** | URL path identifier. Will be automatically sanitized to include only lowercase letters, digits, hyphens `-`, and underscores `_`. |
  | `content` | String | No | Body text of the article (Markdown syntax fully supported). |
  | `tagsInput` | String | No | Comma or space-separated hashtags. Maximum of 3 allowed. Must follow the same strict alphanumeric format constraints as the Slug. |
  | `action` | String | No | Pass `PUBLISHED` to go live immediately. Any other value or omission defaults to `DRAFT`. |


* **Request Example**:
```bash
curl -X POST "https://logzly.com/api/v1/posts" \
     -H "X-API-KEY: your_secret_key_here" \
     -H "Content-Type: application/json" \
     -d '{
           "title": "Publishing via OkHttp Client",
           "slug": "publishing-via-okhttp-client",
           "content": "This markdown content is compiled directly via an automated API pipeline.",
           "tagsInput": "api, automation, minimal",
           "action": "PUBLISHED"
         }'

```


* **Success Response (201 Created)**:
```json
{
  "message": "Post created successfully.",
  "id": 13
}

```


* **Error Responses**:
* `400 Bad Request`: Title/Slug is empty, or hashtags overflow the count limit / contain illegal characters.
* `409 Conflict`: The provided `slug` is already bound to another post under your account registry.



---

### 2.3 Update Post

Overrides and updates all asset fields of a specific post matching the provided ID. Successful mutations trigger an immediate static directory bake.

* **HTTP Method**: `PUT`
* **Path**: `/posts/{id}`
* **Path Parameters**:
* `id`: The unique database primary key of the post (Long type).


* **JSON Body Parameters**: Structurally identical to [Create New Post](https://www.google.com/search?q=%2322-create-new-post).
* **Request Example**:
```bash
curl -X PUT "https://logzly.com/api/v1/posts/13" \
     -H "X-API-KEY: your_secret_key_here" \
     -H "Content-Type: application/json" \
     -d '{
           "title": "Modified Title via Endpoint",
           "slug": "publishing-via-okhttp-client",
           "content": "Updated content logs.",
           "tagsInput": "java, update",
           "action": "DRAFT"
         }'

```


* **Success Response (200 OK)**:
```json
{
  "message": "Post updated successfully."
}

```


* **Error Responses**:
* `403 Forbidden`: Cross-user authorization violation detected (attempting to alter someone else's post asset).
* `404 Not Found`: No target record found matching the provided ID.
* `409 Conflict`: The updated `slug` introduces a naming collision with your other active posts.



---

### 2.4 Toggle Pin Status

Flips the sticky pin configuration of a specified post. Pinned articles are anchored cleanly to the top of your public blog timeline and discovery directories.

* **HTTP Method**: `POST`
* **Path**: `/posts/{id}/toggle-pin`
* **Path Parameters**:
* `id`: The unique database primary key of the post.


* **Request Example**:
```bash
curl -X POST "https://logzly.com/api/v1/posts/13/toggle-pin" \
     -H "X-API-KEY: your_secret_key_here" \
     -d ""

```


* **Success Response (200 OK)**:
```json
{
  "message": "Pin status toggled.",
  "isPinned": true
}

```



---

### 2.5 Soft Delete Post

Moves the specified post into the trash repository. Deleted articles automatically lose their pinned status and are cleanly unlinked from all public static indices.

* **HTTP Method**: `DELETE`
* **Path**: `/posts/{id}`
* **Path Parameters**:
* `id`: The unique database primary key of the post.


* **Request Example**:
```bash
curl -X DELETE "https://logzly.com/api/v1/posts/13" \
     -H "X-API-KEY: your_secret_key_here"

```


* **Success Response (200 OK)**:
```json
{
  "message": "Post moved to trash repository successfully."
}

```



---

## 3. Global Error Status Code Reference

When an operation fails, the endpoint returns a standard HTTP status code complemented by an explicit, clean JSON payload detailing the error state:

| Status Code | Reason for Trigger | Sample Error Payload |
| --- | --- | --- |
| **401 Unauthorized** | `X-API-KEY` is missing, blank, or cryptographically invalid | `{"error": "Invalid X-API-KEY."}` |
| **403 Forbidden** | Horizontal privilege escalation attempt blocked | `{"error": "Access denied."}` |
| **404 Not Found** | Target resource asset ID does not exist in the database | `{"error": "Post Not Found."}` |
| **400 Bad Request** | Server-side validation failure (e.g., missing title, malformed slug) | `{"error": "Title cannot be empty."}` |
| **409 Conflict** | Unique constraint violation (e.g., duplicate slug collision) | `{"error": "The slug is already used..."}` |
