# PosCloud External API Guide

## Base URL

| Environment | URL |
|---|---|
| Production | `https://api.bulutadisyon.com` |
| Development | `https://dev.api.bulutadisyon.com` |
| Beta | `https://beta.api.bulutadisyon.com` |

> Requests from any other domain will be rejected with a `404` error.

---

## Authentication

All endpoints (except where noted) require an **API Key** sent via the `Authorization` header.

```
Authorization: Bearer {YOUR_API_KEY}
```

The API key is company-specific and can be found in your company settings. If the key is missing or invalid, the API returns:

```json
{
    "data": false,
    "success": false,
    "errorCode": null,
    "message": "API key is not supplied."
}
```

---

## Response Format

All API endpoints return a standardized JSON envelope:

```json
{
    "data": "<response payload or false>",
    "success": true,
    "errorCode": null,
    "message": ""
}
```

| Field | Type | Description |
|---|---|---|
| `data` | mixed | The response payload. `false` on error. |
| `success` | boolean | `true` if the request succeeded. |
| `errorCode` | string \| null | Error code if applicable. |
| `message` | string | Human-readable status or error message. |

> **Note:** The `products` and `categories` endpoints return raw JSON arrays (not wrapped in the envelope).

---

## Payment Types

The following payment type IDs are used across the system:

| ID | Name | Description |
|---|---|---|
| `1` | Cash | Nakit |
| `2` | Credit Card | Kredi Karti |
| `3` | Ticket | Ticket / Yemek Ceki |
| `4` | Online | Online Odeme |
| `5` | Credit | Veresiye (customer credit) |
| `6` | Sabee | Sabee entegrasyonu |
| `10` | Treat | Ikram |
| `11` | Other | Diger |

---

## Endpoints

---

### 1. List Products

Retrieve products for the authenticated company.

```
GET /api/products
```

#### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | integer | No | Filter by product ID |
| `q` | string | No | Search by product name (LIKE) |
| `category_id` | integer | No | Filter by category ID |
| `limit` | integer | No | Limit number of results |

#### Example Request

```bash
curl -X GET "https://api.bulutadisyon.com/api/products?category_id=5&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

#### Example Response

```json
[
    {
        "id": 101,
        "name": "Americano",
        "description": "Double shot Americano",
        "category_id": 5,
        "order_number": 1,
        "price": 45.00,
        "status": 1,
        "image_id": 22
    }
]
```

#### Response Fields

| Field | Type | Description |
|---|---|---|
| `id` | integer | Product ID |
| `name` | string | Product name |
| `description` | string | Product description |
| `category_id` | integer | Category ID |
| `order_number` | integer | Display order |
| `price` | float | Unit price |
| `status` | integer | Active status |
| `image_id` | integer \| null | Photo ID (if uploaded) |

---

### 2. List Categories

Retrieve categories for the authenticated company.

```
GET /api/categories
```

#### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | integer | No | Filter by category ID |
| `q` | string | No | Search by category name (LIKE) |
| `app_enabled` | boolean | No | Filter by app visibility |
| `menu_enabled` | boolean | No | Filter by menu visibility |
| `limit` | integer | No | Limit number of results |

#### Example Request

```bash
curl -X GET "https://api.bulutadisyon.com/api/categories?app_enabled=1" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

#### Example Response

```json
[
    {
        "id": 5,
        "name": "Coffee",
        "app_enabled": 1,
        "menu_enabled": 1,
        "order_number": 2
    }
]
```

#### Response Fields

| Field | Type | Description |
|---|---|---|
| `id` | integer | Category ID |
| `name` | string | Category name |
| `app_enabled` | boolean | Visible in mobile app |
| `menu_enabled` | boolean | Visible in digital menu |
| `order_number` | integer | Display order |

---

### 3. List Receipts (Unprinted Sales)

Retrieve completed sales that have not yet been printed (slip not marked as printed).

```
GET /api/receipts
```

#### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `last_n_minutes` | integer | No | Look back window in minutes. Default: `1440` (24h). Max: `1440`. |

#### Example Request

```bash
curl -X GET "https://api.bulutadisyon.com/api/receipts?last_n_minutes=120" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

#### Example Response

```json
{
    "data": [
        {
            "id": 5501,
            "detail": {
                "grossPrice": 150.00,
                "netPrice": 135.00,
                "vat": 18.50,
                "serviceFee": 0,
                "discount": 15.00,
                "treat": 0,
                "cancel": 0,
                "numOrders": 3,
                "paymentType": 1
            },
            "orders": [
                {
                    "id": 12001,
                    "quantity": 2,
                    "totalPrice": 90.00,
                    "unitPrice": 45.00,
                    "name": "Americano",
                    "uom": "piece",
                    "price": 45.00,
                    "vat": 6.12,
                    "serviceFee": 0,
                    "vatPct": 8,
                    "paymentType": 1
                }
            ]
        }
    ],
    "success": true,
    "errorCode": null,
    "message": ""
}
```

#### Response Fields (Sale Detail)

| Field | Type | Description |
|---|---|---|
| `id` | integer | Sale ID |
| `detail.grossPrice` | float | Gross total |
| `detail.netPrice` | float | Net total (after discounts) |
| `detail.vat` | float | Total VAT amount |
| `detail.serviceFee` | float | Service fee |
| `detail.discount` | float | Discount amount |
| `detail.treat` | float | Complimentary amount |
| `detail.cancel` | float | Cancelled amount |
| `detail.numOrders` | integer | Number of order items |
| `detail.paymentType` | integer | Payment type ID (see table above) |

#### Response Fields (Order Item)

| Field | Type | Description |
|---|---|---|
| `id` | integer | Order ID |
| `quantity` | integer | Quantity ordered |
| `totalPrice` | float | Line total |
| `unitPrice` | float | Price per unit |
| `name` | string | Product name |
| `uom` | string | Unit of measure |
| `price` | float | Base price |
| `vat` | float | VAT amount |
| `serviceFee` | float | Service fee |
| `vatPct` | integer | VAT percentage |
| `paymentType` | integer | Payment type ID |

---

### 4. Mark Receipts as Printed

Mark one or more sales as printed (acknowledged by the external system).

```
POST /api/receipts
```

#### Request Body

| Parameter | Type | Required | Description |
|---|---|---|---|
| `receipt_ids` | array\<integer\> | Yes | Array of sale IDs to mark as printed |

#### Example Request

```bash
curl -X POST "https://api.bulutadisyon.com/api/receipts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"receipt_ids": [5501, 5502, 5503]}'
```

#### Example Response

```json
{
    "data": false,
    "success": true,
    "errorCode": null,
    "message": ""
}
```

> After calling this endpoint, the marked receipts will no longer appear in the `GET /api/receipts` response.

---

### 5. Menu Diff (Product Sync)

Get products that have been created, updated, or deleted since a given timestamp. Used for incremental menu synchronization.

```
GET /api/menu-diff
```

#### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `last_updated_at` | string | Yes | Reference timestamp. Format: `Ymdhi` (e.g., `202605131435` for 2026-05-13 14:35) |

#### Example Request

```bash
curl -X GET "https://api.bulutadisyon.com/api/menu-diff?last_updated_at=202605011200" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

#### Example Response

```json
{
    "data": {
        "created": [
            {
                "id": 120,
                "name": "Iced Latte",
                "company_id": 1,
                "gross_price": 55.00,
                "vat_pct": 8,
                "uom": "piece"
            }
        ],
        "updated": [
            {
                "id": 101,
                "name": "Americano",
                "company_id": 1,
                "gross_price": 50.00,
                "vat_pct": 8,
                "uom": "piece"
            }
        ],
        "deleted": [
            {
                "id": 99,
                "name": "Old Product",
                "company_id": 1,
                "gross_price": 30.00,
                "vat_pct": 8,
                "uom": "piece"
            }
        ]
    },
    "success": true,
    "errorCode": null,
    "message": ""
}
```

#### Response Fields (Product Diff Item)

| Field | Type | Description |
|---|---|---|
| `id` | integer | Product ID |
| `name` | string | Product name |
| `company_id` | integer | Company ID |
| `gross_price` | float | Gross unit price |
| `vat_pct` | integer | VAT percentage |
| `uom` | string | Unit of measure |

> **Note:** This endpoint caches results for 6 hours per timestamp. Results include soft-deleted products in the `deleted` array.

---

### 6. Print Menu Orders

Send print commands for specific orders grouped by their tracking sections (kitchen, bar, etc.).

```
POST /api/menu/print-orders
```

#### Request Body

| Parameter | Type | Required | Description |
|---|---|---|---|
| `sale_id` | integer | Yes | The sale ID the orders belong to |
| `order_ids` | array\<integer\> | Yes | Array of order IDs to print (min 1 item) |
| `order_ids.*` | integer | Yes | Each order ID must be an integer |

#### Example Request

```bash
curl -X POST "https://api.bulutadisyon.com/api/menu/print-orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "sale_id": 5501,
      "order_ids": [12001, 12002, 12003]
  }'
```

#### Example Response (Success)

```json
{
    "data": true,
    "success": true,
    "errorCode": null,
    "message": "Print jobs queued successfully"
}
```

#### Example Response (Error)

```json
{
    "data": false,
    "success": false,
    "errorCode": null,
    "message": "Order IDs do not match the sale"
}
```

---

### 7. Get Customer Balance

Retrieve the current account balance for a specific customer.

```
GET /api/customer-balance
```

#### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `customer_id` | integer | Yes | Customer ID |

#### Example Request

```bash
curl -X GET "https://api.bulutadisyon.com/api/customer-balance?customer_id=45" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

#### Example Response

```json
{
    "data": {
        "customer_id": 45,
        "customer_name": "Ahmet Yilmaz",
        "balance": 350.00
    },
    "success": true,
    "errorCode": null,
    "message": ""
}
```

#### Response Fields

| Field | Type | Description |
|---|---|---|
| `customer_id` | integer | Customer ID |
| `customer_name` | string | Customer full name |
| `balance` | float | Current account balance. Positive = customer owes money. Negative = customer has credit. |

---

### 8. List Customer Transactions

Retrieve transaction history for a specific customer with optional date filters.

```
GET /api/customer-transactions
```

#### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `customer_id` | integer | Yes | Customer ID |
| `date_from` | string | No | Start date filter (format: `DD.MM.YYYY`) |
| `date_to` | string | No | End date filter (format: `DD.MM.YYYY`) |
| `limit` | integer | No | Max results to return. Default: `50`. Max: `100`. |

#### Example Request

```bash
curl -X GET "https://api.bulutadisyon.com/api/customer-transactions?customer_id=45&date_from=01.05.2026&date_to=13.05.2026&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

#### Example Response

```json
{
    "data": [
        {
            "id": 1201,
            "amount": 150.00,
            "discount": 0,
            "payment_type": 1,
            "paid_at": "2026-05-13 14:30:00",
            "description": "Payment received",
            "action_type": null,
            "created_at": "2026-05-13 14:30:15"
        },
        {
            "id": 1198,
            "amount": -50.00,
            "discount": 0,
            "payment_type": 2,
            "paid_at": "2026-05-12 09:15:00",
            "description": "Refund",
            "action_type": null,
            "created_at": "2026-05-12 09:15:22"
        }
    ],
    "success": true,
    "errorCode": null,
    "message": ""
}
```

#### Response Fields

| Field | Type | Description |
|---|---|---|
| `id` | integer | Transaction ID |
| `amount` | float | Transaction amount. Positive = collection (money in). Negative = payment (money out). |
| `discount` | float | Discount amount applied |
| `payment_type` | integer | Payment type ID (see Payment Types table) |
| `paid_at` | string \| null | Payment date (ISO 8601) |
| `description` | string \| null | Transaction description |
| `action_type` | string \| null | Related action type (e.g., `sale`, `stock_delivery`) |
| `created_at` | string \| null | Record creation timestamp |

---

### 9. Create Customer Transaction

Create a new financial transaction (payment or collection) for a customer.

```
POST /api/customer-transactions
```

#### Request Body

| Parameter | Type | Required | Description |
|---|---|---|---|
| `customer_id` | integer | Yes | Customer ID |
| `amount` | float | Yes | Transaction amount (always positive) |
| `discount` | float | No | Discount amount (default: `0`) |
| `direction` | string | Yes | `collection` (money IN from customer) or `payment` (money OUT to customer) |
| `payment_type` | integer | Yes | `1` (cash), `2` (credit-card), `3` (ticket), `4` (online) |
| `paid_at` | string | No | Payment datetime. Format: `DD.MM.YYYY HH:mm`. Default: now. |
| `description` | string | No | Description text (max 500 chars) |

#### Example Request (Collection)

```bash
curl -X POST "https://api.bulutadisyon.com/api/customer-transactions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "customer_id": 45,
      "amount": 150.00,
      "direction": "collection",
      "payment_type": 1,
      "paid_at": "13.05.2026 14:30",
      "description": "Payment received from customer"
  }'
```

#### Example Request (Payment / Refund)

```bash
curl -X POST "https://api.bulutadisyon.com/api/customer-transactions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "customer_id": 45,
      "amount": 50.00,
      "direction": "payment",
      "payment_type": 2,
      "description": "Refund to customer"
  }'
```

#### Example Response

```json
{
    "data": {
        "transaction_id": 1205,
        "customer_id": 45,
        "amount": 150.00,
        "direction": "collection",
        "payment_type": 1,
        "paid_at": "2026-05-13 14:30:00",
        "description": "Payment received from customer",
        "balance_after": 500.00
    },
    "success": true,
    "errorCode": null,
    "message": "Transaction created successfully."
}
```

#### Response Fields

| Field | Type | Description |
|---|---|---|
| `transaction_id` | integer | Newly created transaction ID |
| `customer_id` | integer | Customer ID |
| `amount` | float | Signed amount (positive = collection, negative = payment) |
| `direction` | string | `collection` or `payment` |
| `payment_type` | integer | Payment type ID |
| `paid_at` | string | Payment datetime (ISO 8601) |
| `description` | string | Description text |
| `balance_after` | float | Customer's updated balance after this transaction |

---

## Error Handling

### Authentication Errors

| Scenario | `success` | `message` |
|---|---|---|
| No Authorization header | `false` | `API key is not supplied.` |
| Invalid API key | `false` | `Bad API key` |

### HTTP Status Codes

| Code | Meaning |
|---|---|
| `200` | Successful response |
| `404` | Wrong API domain or route not found |
| `422` | Validation error (Laravel default format) |
| `429` | Rate limit exceeded |
| `500` | Server error |

### Validation Error Format (422)

```json
{
    "message": "The given data was invalid.",
    "errors": {
        "last_updated_at": ["The last updated at field is required."]
    }
}
```

---

## Rate Limiting

API requests are rate-limited. The throttle middleware is configured per environment. Exceeding the limit will return a `429 Too Many Requests` response.

---

## Workflow: Receipt Printing Integration

A typical integration flow for external receipt printing:

```
1. GET  /api/receipts           → Fetch unprinted sales
2. (External system processes and prints receipts)
3. POST /api/receipts           → Mark sales as printed
4. Repeat at desired interval
```

## Workflow: Menu Sync Integration

A typical integration flow for keeping an external menu in sync:

```
1. GET /api/menu-diff?last_updated_at=<last_sync_timestamp>
2. Apply created/updated/deleted changes to external system
3. Store current timestamp as new last_sync_timestamp
4. Repeat at desired interval (results cached for 6 hours)
```

---

## Quick Reference

| Method | Endpoint | Auth | Description |
|---|---|---|---|
| `GET` | `/api/products` | Bearer | List/search products |
| `GET` | `/api/categories` | Bearer | List/search categories |
| `GET` | `/api/receipts` | Bearer | List unprinted receipts |
| `POST` | `/api/receipts` | Bearer | Mark receipts as printed |
| `GET` | `/api/menu-diff` | Bearer | Incremental product sync |
| `POST` | `/api/menu/print-orders` | Bearer | Print kitchen/bar orders |
| `GET` | `/api/customer-balance` | Bearer | Get customer account balance |
| `GET` | `/api/customer-transactions` | Bearer | List customer transactions |
| `POST` | `/api/customer-transactions` | Bearer | Create customer transaction |
