<!-- Generated from Btab's docs/api/VENDOR_INTEGRATION_GUIDE.md. The same guide as a web page: https://btab.app/developers/vendor-integration-guide -->

# BTAB Vendor Integration Guide

## Welcome to BTAB!

This guide shows you how to integrate BTAB's product catalog and order management into your own custom storefront.

---

## Overview

**What is BTAB?**
BTAB is a B2B2C platform that lets you sell products without holding inventory. You:
1. Select products from our catalog
2. Build your own storefront (any tech stack)
3. Set your own pricing
4. Sell to your customers
5. We handle fulfillment

**You Focus On:** Marketing, branding, customer experience
**We Handle:** Inventory, fulfillment, shipping

---

## Quick Start (5 Steps)

### 1. Create Your BTAB Account

Visit https://dashboard.btab.app/register

```
Email: you@yourstore.com
Password: [your secure password]
Company Name: Your Store Inc.
```

You'll receive an API key that looks like:
```
btab_live_abc123def456ghi789...
```

**⚠️ KEEP THIS SECRET!** Never expose it in browser code.

---

### 2. Select Products

1. Log into https://dashboard.btab.app
2. Go to "Products" tab
3. Browse the catalog
4. Click "Add to Store" on products you want to sell
5. Optionally set custom pricing (your retail price)

Example:
- **Wholesale price:** $49.99
- **Suggested retail:** $89.99
- **Your custom price:** $79.99 (your margin: $30)

---

### 3. Set Up Your Backend

**CRITICAL:** Your API key must ONLY be used from your backend server, NEVER in browser JavaScript.

**Why?** If you put it in browser code, anyone can:
- Steal your API key using browser devtools
- Make unauthorized requests
- Impersonate your store
- Create fake orders

**Correct Architecture:**
```
[Customer Browser]
    ↓ public requests
[Your Backend Server] ← API key stored here (env variable)
    ↓ authenticated requests
[BTAB API]
```

---

### 4. Integrate the API

#### Node.js Example

**Install dependencies:**
```bash
npm install express node-fetch dotenv
```

**Create `.env` file:**
```bash
BTAB_API_KEY=btab_live_abc123def456...
BTAB_API_URL=https://api.btab.app/api/v1
PORT=8000
```

**Create `server.js`:**
```javascript
require('dotenv').config();
const express = require('express');
const app = express();

app.use(express.json());

const BTAB_API_KEY = process.env.BTAB_API_KEY;
const BTAB_API_URL = process.env.BTAB_API_URL;

// Get products for your store
app.get('/api/products', async (req, res) => {
  try {
    const response = await fetch(`${BTAB_API_URL}/my-products`, {
      headers: {
        'Authorization': `Bearer ${BTAB_API_KEY}`
      }
    });

    if (!response.ok) {
      throw new Error('Failed to fetch products');
    }

    const data = await response.json();
    res.json(data);
  } catch (error) {
    console.error('Error fetching products:', error);
    res.status(500).json({ error: 'Failed to load products' });
  }
});

// Handle customer checkout
app.post('/api/checkout', async (req, res) => {
  try {
    const { customer, items, shipping, payment } = req.body;

    // Create order in BTAB
    const response = await fetch(`${BTAB_API_URL}/orders`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${BTAB_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        customer,
        items,
        shipping,
        payment
      })
    });

    if (!response.ok) {
      throw new Error('Order creation failed');
    }

    const order = await response.json();
    res.json({
      success: true,
      order_id: order.id,
      message: 'Order placed successfully!'
    });
  } catch (error) {
    console.error('Error creating order:', error);
    res.status(500).json({ error: 'Checkout failed' });
  }
});

app.listen(process.env.PORT, () => {
  console.log(`Server running on port ${process.env.PORT}`);
});
```

**Start your server:**
```bash
node server.js
```

---

### 5. Build Your Frontend

Your frontend can be anything:
- React, Vue, Angular
- Next.js, Nuxt
- Plain HTML/JS/CSS
- WordPress
- Mobile app

**Example Frontend (React):**

```javascript
import { useState, useEffect } from 'react';

function ProductList() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Call YOUR backend, not BTAB directly
    fetch('/api/products')
      .then(res => res.json())
      .then(data => {
        setProducts(data.products);
        setLoading(false);
      })
      .catch(error => {
        console.error('Error loading products:', error);
        setLoading(false);
      });
  }, []);

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

  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

function ProductCard({ product }) {
  return (
    <div className="product-card">
      <img src={product.image_url} alt={product.name} />
      <h3>{product.name}</h3>
      <p>{product.description}</p>
      <p className="price">
        ${(product.retail_price_cents / 100).toFixed(2)}
      </p>
      <button onClick={() => addToCart(product)}>
        Add to Cart
      </button>
    </div>
  );
}

async function checkout(cart, customer, shipping, payment) {
  const response = await fetch('/api/checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      customer,
      items: cart.items.map(item => ({
        product_id: item.product.id,
        quantity: item.quantity,
        price_cents: item.product.retail_price_cents
      })),
      shipping,
      payment
    })
  });

  return response.json();
}
```

---

## API Reference

### Base URL
```
https://api.btab.app/api/v1
```

### Authentication
All requests require API key in header:
```
Authorization: Bearer btab_live_abc123...
```

---

### Get Your Selected Products

**Endpoint:** `GET /my-products`

**Response:**
```json
{
  "products": [
    {
      "id": 1,
      "name": "Product Name",
      "description": "Product description...",
      "sku": "SKU-001",
      "wholesale_price_cents": 4999,
      "retail_price_cents": 8999,
      "custom_retail_price_cents": 7999,
      "image_url": "https://...",
      "in_stock": true,
      "stock_quantity": 50,
      "metadata": {
        "category": "electronics",
        "weight_oz": 16
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total_count": 25,
    "total_pages": 2
  }
}
```

---

### Get Full Product Catalog

**Endpoint:** `GET /products`

Shows ALL available products (not just yours). Useful for letting vendors browse and add new products via API.

**Query Parameters:**
- `page` - Page number (default: 1)
- `limit` - Items per page (default: 20, max: 100)
- `search` - Search term
- `in_stock` - true/false

---

### Add Product to Your Store

**Endpoint:** `POST /my-products`

**Request Body:**
```json
{
  "product_id": 1,
  "custom_retail_price_cents": 7999
}
```

**Response:**
```json
{
  "message": "Product added to your store",
  "vendor_product": {
    "id": 123,
    "product_id": 1,
    "custom_retail_price_cents": 7999
  }
}
```

---

### Remove Product from Your Store

**Endpoint:** `DELETE /my-products/:id`

**Response:**
```json
{
  "message": "Product removed from your store"
}
```

---

### Update Product Pricing

**Endpoint:** `PUT /api/v1/my-products/{productId}/pricing`

**Request Body:**
```json
{
  "custom_retail_price_cents": 8999
}
```

---

### Create Order

**Endpoint:** `POST /orders`

**Request Body:**
```json
{
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "phone": "+1234567890"
  },
  "items": [
    {
      "product_id": 1,
      "quantity": 2,
      "price_cents": 7999
    }
  ],
  "shipping": {
    "address": "123 Main St",
    "address2": "Apt 4B",
    "city": "San Francisco",
    "state": "CA",
    "zip": "94102",
    "country": "US"
  },
  "payment": {
    "method": "stripe",
    "transaction_id": "ch_abc123",
    "amount_cents": 15998
  }
}
```

**Response:**
```json
{
  "order": {
    "id": 456,
    "order_number": "ORD-2025-001",
    "status": "pending",
    "total_cents": 15998,
    "created_at": "2025-10-16T12:00:00Z"
  }
}
```

---

### Get Your Orders

**Endpoint:** `GET /orders`

**Query Parameters:**
- `page` - Page number
- `limit` - Items per page
- `status` - Filter by status (pending, processing, shipped, delivered, cancelled)

---

### Get Specific Order

**Endpoint:** `GET /orders/:id`

**Response:**
```json
{
  "order": {
    "id": 456,
    "order_number": "ORD-2025-001",
    "status": "shipped",
    "customer": { ... },
    "items": [ ... ],
    "shipping": { ... },
    "tracking_number": "1Z999AA10123456784",
    "created_at": "2025-10-16T12:00:00Z",
    "updated_at": "2025-10-16T14:30:00Z"
  }
}
```

---

### Get the Buyer's Tax Invoice

**Endpoint:** `GET /orders/:id/invoice`

Returns the buyer's tax invoice for this order as a **PDF** (`Content-Type:
application/pdf`), not JSON.

BTAB is the merchant of record on every storefront, so BTAB issues the tax
invoice and the document carries BTAB's legal name and ABN — your store appears
on it as the channel the sale came through. The invoice is issued **when the
order ships**, is emailed to the buyer automatically at that moment, and carries
a permanent number in the form `INV-YYYYMM-NNNNNN`.

Before the order ships there is no invoice, and this endpoint returns `404` with
a message saying so. The number and issue date are also returned on
`GET /orders/:id` as `invoice_number` and `invoice_issued_at` — check those if
you want to know whether a document exists before requesting it.

```
GET /orders/456/invoice
Authorization: Bearer <your-api-key>

200 OK
Content-Type: application/pdf
Content-Disposition: inline; filename="INV-202609-000042.pdf"
```

---

## Error Handling

All errors follow this format:
```json
{
  "error": "Error type",
  "message": "Human-readable error message"
}
```

**Common HTTP Status Codes:**
- `200` - Success
- `400` - Bad Request (invalid data)
- `401` - Unauthorized (invalid API key)
- `404` - Not Found
- `500` - Server Error

**Example Error Handling:**
```javascript
try {
  const response = await fetch(`${BTAB_API_URL}/my-products`, {
    headers: { 'Authorization': `Bearer ${BTAB_API_KEY}` }
  });

  if (!response.ok) {
    const error = await response.json();
    console.error('API Error:', error.message);

    if (response.status === 401) {
      // Invalid API key
      console.error('Check your API key in .env file');
    }
  }

  const data = await response.json();
  // Use data...

} catch (error) {
  console.error('Network error:', error);
}
```

---

## Technology-Specific Examples

### Next.js (App Router)

**API Route: `app/api/products/route.ts`**
```typescript
import { NextRequest, NextResponse } from 'next/server';

const BTAB_API_KEY = process.env.BTAB_API_KEY!;
const BTAB_API_URL = process.env.BTAB_API_URL!;

export async function GET(request: NextRequest) {
  try {
    const response = await fetch(`${BTAB_API_URL}/my-products`, {
      headers: {
        'Authorization': `Bearer ${BTAB_API_KEY}`
      }
    });

    const data = await response.json();
    return NextResponse.json(data);
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch products' },
      { status: 500 }
    );
  }
}
```

---

### PHP (WordPress Plugin)

```php
<?php
// wp-content/plugins/btab-integration/btab-api.php

class BTAB_API {
    private $api_key;
    private $api_url = 'https://api.btab.app/api/v1';

    public function __construct() {
        $this->api_key = get_option('btab_api_key');
    }

    public function get_products() {
        $response = wp_remote_get($this->api_url . '/my-products', [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->api_key
            ]
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $body = wp_remote_retrieve_body($response);
        return json_decode($body, true);
    }

    public function create_order($order_data) {
        $response = wp_remote_post($this->api_url . '/orders', [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json'
            ],
            'body' => json_encode($order_data)
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $body = wp_remote_retrieve_body($response);
        return json_decode($body, true);
    }
}
```

---

### Python (Flask)

```python
# app.py
import os
import requests
from flask import Flask, jsonify, request

app = Flask(__name__)

BTAB_API_KEY = os.getenv('BTAB_API_KEY')
BTAB_API_URL = os.getenv('BTAB_API_URL', 'https://api.btab.app/api/v1')

@app.route('/api/products')
def get_products():
    try:
        response = requests.get(
            f'{BTAB_API_URL}/my-products',
            headers={'Authorization': f'Bearer {BTAB_API_KEY}'}
        )
        response.raise_for_status()
        return jsonify(response.json())
    except requests.exceptions.RequestException as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/checkout', methods=['POST'])
def checkout():
    try:
        order_data = request.json

        response = requests.post(
            f'{BTAB_API_URL}/orders',
            json=order_data,
            headers={
                'Authorization': f'Bearer {BTAB_API_KEY}',
                'Content-Type': 'application/json'
            }
        )
        response.raise_for_status()
        return jsonify(response.json())
    except requests.exceptions.RequestException as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(port=8000)
```

---

## Best Practices

### Security

1. **Never expose API key in:**
   - Browser JavaScript
   - Mobile app code
   - GitHub repository
   - Client-side config files

2. **Always store API key in:**
   - Environment variables (`.env` file)
   - Server-side config
   - Secure secret management (AWS Secrets, Vault, etc.)

3. **Use HTTPS everywhere** - Both your site and BTAB API use HTTPS

4. **Validate data** before sending to BTAB API:
   ```javascript
   // Validate customer email
   if (!isValidEmail(customer.email)) {
     return res.status(400).json({ error: 'Invalid email' });
   }

   // Validate pricing matches
   const product = await getProduct(item.product_id);
   if (item.price_cents !== product.retail_price_cents) {
     return res.status(400).json({ error: 'Price mismatch' });
   }
   ```

---

### Performance

1. **Cache product data** - Don't fetch on every page load:
   ```javascript
   // Refresh products every 5 minutes
   let productsCache = null;
   let lastFetch = 0;

   async function getProducts() {
     const now = Date.now();
     if (productsCache && (now - lastFetch) < 5 * 60 * 1000) {
       return productsCache;
     }

     productsCache = await fetchFromBTAB();
     lastFetch = now;
     return productsCache;
   }
   ```

2. **Handle rate limits** - Implement retry logic:
   ```javascript
   async function fetchWithRetry(url, options, retries = 3) {
     for (let i = 0; i < retries; i++) {
       const response = await fetch(url, options);
       if (response.status === 429) {
         // Rate limited, wait and retry
         await sleep(1000 * (i + 1));
         continue;
       }
       return response;
     }
     throw new Error('Max retries exceeded');
   }
   ```

---

### Testing

**Test in Development:**
Use the test API key from dashboard for local testing.

**Example Test:**
```javascript
// test-api.js
require('dotenv').config();

async function testAPI() {
  console.log('Testing BTAB API integration...\n');

  // Test 1: Get products
  console.log('1. Fetching products...');
  const response = await fetch(`${process.env.BTAB_API_URL}/my-products`, {
    headers: { 'Authorization': `Bearer ${process.env.BTAB_API_KEY}` }
  });

  if (!response.ok) {
    console.error('❌ Failed:', await response.text());
    return;
  }

  const data = await response.json();
  console.log(`✅ Success: Found ${data.products.length} products\n`);

  // Test 2: Create test order
  console.log('2. Creating test order...');
  // ... test order creation
}

testAPI();
```

---

## Troubleshooting

### "401 Unauthorized"
- Check API key is correct in `.env` file
- Verify key starts with `btab_live_` or `btab_test_`
- Make sure Authorization header is properly formatted

### "No products returned"
- Make sure you've added products in the dashboard
- Check you're calling `/my-products` not `/products`

### "CORS errors"
- You should NOT get CORS errors if using backend proxy correctly
- If you see CORS errors, you're calling BTAB API from browser (wrong!)

### "Rate limit exceeded"
- Implement caching (see Performance section)
- Add retry logic with backoff

---

## Support

**Dashboard:** https://dashboard.btab.app
**API Status:** https://api.btab.app/health
**Documentation:** https://btab.app/developers

---

## Example code

A working webhook consumer — signature check, retries, idempotent handling — ships with these docs: [examples/webhook-consumer.js](/docs/examples/webhook-consumer.js). It is the one example BTAB publishes.

---

**Happy Selling! 🚀**
