> ## Documentation Index
> Fetch the complete documentation index at: https://quickbutik.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart Tutorial

> Build your first Quickbutik integration in 15 minutes

This tutorial will walk you through building your first Quickbutik integration. You'll learn how to authenticate, fetch data, and handle responses.

<Note>
  **Prerequisites**: You'll need a Quickbutik store and an API key. If you don't have an API key yet, generate one in your store's Control Panel under **Settings → API**.
</Note>

## What we'll build

By the end of this tutorial, you'll have a simple integration that:

* ✅ Authenticates with the Quickbutik API
* ✅ Fetches your store's product count
* ✅ Retrieves your latest orders
* ✅ Handles errors gracefully

## Step 1: Set up authentication

First, let's test your API connection with a simple request to count your products:

<CodeGroup>
  ```bash cURL theme={null}
  # Replace 'your_api_key' with your actual API key
  curl https://api.quickbutik.com/v1/products/count \
    -u your_api_key:your_api_key \
    -H "Content-Type: application/json"
  ```

  ```javascript Node.js theme={null}
  const fetch = require('node-fetch'); // or use built-in fetch in Node 18+

  const apiKey = 'your_api_key';
  const credentials = Buffer.from(`${apiKey}:${apiKey}`).toString('base64');

  async function getProductCount() {
    try {
      const response = await fetch('https://api.quickbutik.com/v1/products/count', {
        headers: {
          'Authorization': `Basic ${credentials}`,
          'Content-Type': 'application/json'
        }
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const data = await response.json();
      console.log('Product count:', data.count);
      return data;
    } catch (error) {
      console.error('Error:', error.message);
    }
  }

  getProductCount();
  ```

  ```python Python theme={null}
  import requests
  import base64
  import json

  api_key = 'your_api_key'
  credentials = base64.b64encode(f'{api_key}:{api_key}'.encode()).decode()

  def get_product_count():
      try:
          response = requests.get(
              'https://api.quickbutik.com/v1/products/count',
              headers={
                  'Authorization': f'Basic {credentials}',
                  'Content-Type': 'application/json'
              }
          )
          response.raise_for_status()
          
          data = response.json()
          print(f"Product count: {data['count']}")
          return data
          
      except requests.exceptions.RequestException as error:
          print(f"Error: {error}")

  get_product_count()
  ```
</CodeGroup>

### Expected Response

```json theme={null}
{
  "count": "42"
}
```

<Accordion title="Troubleshooting authentication">
  **Common issues:**

  * **401 Unauthorized**: Check that your API key is correct
  * **Invalid base64**: Ensure you're encoding `api_key:api_key` format
  * **SSL errors**: Make sure you're using `https://` not `http://`

  **Test your base64 encoding:**

  ```bash theme={null}
  echo -n "your_api_key:your_api_key" | base64
  ```
</Accordion>

## Step 2: Fetch your latest orders

Now let's retrieve your most recent orders:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.quickbutik.com/v1/orders?limit=5&include_details=true" \
    -u your_api_key:your_api_key \
    -H "Content-Type: application/json"
  ```

  ```javascript Node.js theme={null}
  async function getLatestOrders() {
    try {
      const response = await fetch('https://api.quickbutik.com/v1/orders?limit=5&include_details=true', {
        headers: {
          'Authorization': `Basic ${credentials}`,
          'Content-Type': 'application/json'
        }
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const orders = await response.json();
      console.log(`Found ${orders.length} recent orders`);
      
      orders.forEach(order => {
        console.log(`Order #${order.order_id}: ${order.total_amount} (${order.status})`);
      });
      
      return orders;
    } catch (error) {
      console.error('Error fetching orders:', error.message);
    }
  }

  getLatestOrders();
  ```

  ```python Python theme={null}
  def get_latest_orders():
      try:
          response = requests.get(
              'https://api.quickbutik.com/v1/orders',
              params={'limit': 5, 'include_details': True},
              headers={
                  'Authorization': f'Basic {credentials}',
                  'Content-Type': 'application/json'
              }
          )
          response.raise_for_status()
          
          orders = response.json()
          print(f"Found {len(orders)} recent orders")
          
          for order in orders:
              print(f"Order #{order['order_id']}: {order['total_amount']} ({order['status']})")
          
          return orders
          
      except requests.exceptions.RequestException as error:
          print(f"Error fetching orders: {error}")

  get_latest_orders()
  ```
</CodeGroup>

### Expected Response

```json theme={null}
[
  {
    "order_id": "12345",
    "date_created": "2025-01-29 11:35:39",
    "total_amount": "148.95",
    "status": "1"
  }
]
```

## Step 3: Update an order status

Let's mark an order as "done" (shipped):

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT https://api.quickbutik.com/v1/orders \
    -u your_api_key:your_api_key \
    -H "Content-Type: application/json" \
    -d '[{
      "order_id": "12345",
      "status": "done",
      "shipping_info": {
        "trackingnumber": "1Z999AA1234567890",
        "company": "UPS"
      },
      "email_confirmation": "true"
    }]'
  ```

  ```javascript Node.js theme={null}
  async function markOrderAsShipped(orderId, trackingNumber) {
    try {
      const response = await fetch('https://api.quickbutik.com/v1/orders', {
        method: 'PUT',
        headers: {
          'Authorization': `Basic ${credentials}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify([{
          order_id: orderId,
          status: 'done',
          shipping_info: {
            trackingnumber: trackingNumber,
            company: 'UPS'
          },
          email_confirmation: 'true'
        }])
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const result = await response.json();
      console.log('Order updated successfully:', result);
      return result;
    } catch (error) {
      console.error('Error updating order:', error.message);
    }
  }

  markOrderAsShipped('12345', '1Z999AA1234567890');
  ```

  ```python Python theme={null}
  def mark_order_as_shipped(order_id, tracking_number):
      try:
          response = requests.put(
              'https://api.quickbutik.com/v1/orders',
              headers={
                  'Authorization': f'Basic {credentials}',
                  'Content-Type': 'application/json'
              },
              json=[{
                  'order_id': order_id,
                  'status': 'done',
                  'shipping_info': {
                      'trackingnumber': tracking_number,
                      'company': 'UPS'
                  },
                  'email_confirmation': 'true'
              }]
          )
          response.raise_for_status()
          
          result = response.json()
          print('Order updated successfully:', result)
          return result
          
      except requests.exceptions.RequestException as error:
          print(f"Error updating order: {error}")

  mark_order_as_shipped('12345', '1Z999AA1234567890')
  ```
</CodeGroup>

## Step 4: Handle errors like a pro

Here's how to build robust error handling:

<CodeGroup>
  ```javascript Node.js theme={null}
  class QuickbutikAPI {
    constructor(apiKey) {
      this.apiKey = apiKey;
      this.credentials = Buffer.from(`${apiKey}:${apiKey}`).toString('base64');
      this.baseUrl = 'https://api.quickbutik.com/v1';
    }

    async request(endpoint, options = {}) {
      const url = `${this.baseUrl}${endpoint}`;
      
      try {
        const response = await fetch(url, {
          ...options,
          headers: {
            'Authorization': `Basic ${this.credentials}`,
            'Content-Type': 'application/json',
            ...options.headers
          }
        });

        // Handle different HTTP status codes
        if (!response.ok) {
          const errorData = await response.json().catch(() => ({}));
          
          switch (response.status) {
            case 401:
              throw new Error('Authentication failed. Check your API key.');
            case 404:
              throw new Error(`Resource not found: ${errorData.error || 'Unknown error'}`);
            case 400:
              throw new Error(`Bad request: ${errorData.error || 'Invalid data provided'}`);
            case 500:
              throw new Error('Server error. Please try again later.');
            default:
              throw new Error(`HTTP ${response.status}: ${errorData.error || response.statusText}`);
          }
        }

        return await response.json();
      } catch (error) {
        if (error.name === 'TypeError' && error.message.includes('fetch')) {
          throw new Error('Network error. Check your internet connection.');
        }
        throw error;
      }
    }

    async getOrders(params = {}) {
      const queryString = new URLSearchParams(params).toString();
      const endpoint = queryString ? `/orders?${queryString}` : '/orders';
      return this.request(endpoint);
    }

    async updateOrderStatus(updates) {
      return this.request('/orders', {
        method: 'PUT',
        body: JSON.stringify(Array.isArray(updates) ? updates : [updates])
      });
    }
  }

  // Usage
  const api = new QuickbutikAPI('your_api_key');

  async function example() {
    try {
      const orders = await api.getOrders({ limit: 5 });
      console.log('Orders fetched successfully:', orders.length);
    } catch (error) {
      console.error('Failed to fetch orders:', error.message);
    }
  }
  ```

  ```python Python theme={null}
  import requests
  import base64
  from typing import Dict, List, Optional

  class QuickbutikAPI:
      def __init__(self, api_key: str):
          self.api_key = api_key
          self.credentials = base64.b64encode(f'{api_key}:{api_key}'.encode()).decode()
          self.base_url = 'https://api.quickbutik.com/v1'
          
      def _request(self, endpoint: str, method: str = 'GET', data: Optional[Dict] = None):
          url = f'{self.base_url}{endpoint}'
          headers = {
              'Authorization': f'Basic {self.credentials}',
              'Content-Type': 'application/json'
          }
          
          try:
              response = requests.request(method, url, headers=headers, json=data)
              
              # Handle different HTTP status codes
              if response.status_code == 401:
                  raise Exception('Authentication failed. Check your API key.')
              elif response.status_code == 404:
                  error_data = response.json() if response.content else {}
                  raise Exception(f"Resource not found: {error_data.get('error', 'Unknown error')}")
              elif response.status_code == 400:
                  error_data = response.json() if response.content else {}
                  raise Exception(f"Bad request: {error_data.get('error', 'Invalid data provided')}")
              elif response.status_code == 500:
                  raise Exception('Server error. Please try again later.')
              
              response.raise_for_status()
              return response.json()
              
          except requests.exceptions.ConnectionError:
              raise Exception('Network error. Check your internet connection.')
          except requests.exceptions.Timeout:
              raise Exception('Request timed out. Please try again.')
          except requests.exceptions.RequestException as e:
              raise Exception(f'Request failed: {str(e)}')
      
      def get_orders(self, **params) -> List[Dict]:
          endpoint = '/orders'
          if params:
              query_string = '&'.join([f'{k}={v}' for k, v in params.items()])
              endpoint += f'?{query_string}'
          return self._request(endpoint)
      
      def update_order_status(self, updates) -> Dict:
          if not isinstance(updates, list):
              updates = [updates]
          return self._request('/orders', method='PUT', data=updates)

  # Usage
  api = QuickbutikAPI('your_api_key')

  try:
      orders = api.get_orders(limit=5)
      print(f'Orders fetched successfully: {len(orders)}')
  except Exception as error:
      print(f'Failed to fetch orders: {error}')
  ```
</CodeGroup>

## 🎉 Congratulations!

You've successfully built your first Quickbutik integration! You now know how to:

* ✅ Authenticate with the API
* ✅ Fetch orders and products
* ✅ Update order statuses
* ✅ Handle errors gracefully

## Next steps

<CardGroup cols={2}>
  <Card title="Set up webhooks" icon="webhook" href="/webhooks/introduction">
    Get real-time notifications when orders are created or updated
  </Card>

  <Card title="Order sync tutorial" icon="arrows-rotate" href="/api-v1/guides/order-sync-tutorial">
    Learn how to build a complete order synchronization system
  </Card>
</CardGroup>

## Need help?

<Card title="Get Support" icon="question">
  Stuck on something? We're here to help! Reach out to [support@quickbutik.com](mailto:support@quickbutik.com) with your questions.
</Card>
