Time to first API call: Less than 5 minutes
Prerequisites: Basic programming knowledge and a StateSet account

Overview

This quickstart guide will walk you through making your first StateSet API call, then progressively build up to a complete integration. By the end, you’ll be able to:
  • ✅ Authenticate with the API
  • ✅ Create and manage orders
  • ✅ Handle webhooks
  • ✅ Implement error handling
  • ✅ Use advanced features

Step 1: Get Your API Keys

1

Sign up for StateSet

Create your account at stateset.com/signup
2

Navigate to API Settings

Go to Dashboard → Settings → API Keys
3

Create Your First API Key

Click “Create API Key” and save it securely - you’ll only see it once!
Security First: Never commit API keys to version control. Use environment variables instead.

Step 2: Set Up Your Environment

# Install the StateSet SDK
npm install stateset-node dotenv

# Create .env file
echo "STATESET_API_KEY=sk_test_your_key_here" > .env
// index.js
require('dotenv').config();
const { StateSetClient } = require('stateset-node');

const stateset = new StateSetClient({
  apiKey: process.env.STATESET_API_KEY
});

console.log('StateSet client initialized!');

Step 3: Make Your First API Call

Let’s start with a simple request to list orders:
async function getOrders() {
  try {
    const orders = await stateset.orders.list({
      limit: 10,
      status: 'pending'
    });
    
    console.log(`Found ${orders.data.length} orders`);
    orders.data.forEach(order => {
      console.log(`- Order ${order.order_number}: ${order.status}`);
    });
  } catch (error) {
    console.error('Error fetching orders:', error.message);
  }
}

getOrders();

Step 4: Create Your First Resource

Now let’s create an order:
async function createOrder() {
  try {
    const order = await stateset.orders.create({
      customer: {
        email: 'customer@example.com',
        first_name: 'John',
        last_name: 'Doe'
      },
      items: [
        {
          sku: 'WIDGET-001',
          quantity: 2,
          price: 2999, // in cents
          name: 'Premium Widget'
        }
      ],
      shipping_address: {
        line1: '123 Main St',
        city: 'San Francisco',
        state: 'CA',
        postal_code: '94105',
        country: 'US'
      },
      payment: {
        method: 'card',
        payment_intent_id: 'pi_test_123'
      }
    });
    
    console.log('Order created successfully!');
    console.log(`Order ID: ${order.id}`);
    console.log(`Order Number: ${order.order_number}`);
    console.log(`Total: $${(order.totals.total / 100).toFixed(2)}`);
    
    return order;
  } catch (error) {
    console.error('Error creating order:', error.message);
    if (error.details) {
      console.error('Validation errors:', error.details);
    }
  }
}

createOrder();

Step 5: Handle Webhooks

Set up webhook handling for real-time events:
const express = require('express');
const crypto = require('crypto');
const app = express();

// Middleware to capture raw body for signature verification
app.use('/webhooks', express.raw({ type: 'application/json' }));

function verifyWebhookSignature(payload, signature, secret) {
  const elements = signature.split(' ');
  const timestamp = elements.find(e => e.startsWith('t=')).slice(2);
  const signatures = elements.filter(e => e.startsWith('v1='));
  
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  return signatures.some(sig => 
    sig.slice(3) === expectedSignature
  );
}

app.post('/webhooks/stateset', async (req, res) => {
  const signature = req.headers['stateset-signature'];
  const secret = process.env.STATESET_WEBHOOK_SECRET;
  
  try {
    // Verify webhook signature
    if (!verifyWebhookSignature(req.body, signature, secret)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    const event = JSON.parse(req.body);
    
    // Handle different event types
    switch (event.type) {
      case 'order.created':
        console.log('New order:', event.data.object.id);
        await handleNewOrder(event.data.object);
        break;
        
      case 'order.shipped':
        console.log('Order shipped:', event.data.object.id);
        await notifyCustomerShipped(event.data.object);
        break;
        
      case 'return.created':
        console.log('Return initiated:', event.data.object.id);
        await processReturn(event.data.object);
        break;
        
      default:
        console.log('Unhandled event type:', event.type);
    }
    
    res.json({ received: true });
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(400).json({ error: 'Webhook processing failed' });
  }
});

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

Step 6: Implement Error Handling

Add robust error handling to your integration:
class StateSetAPIHandler {
  constructor(apiKey) {
    this.client = new StateSetClient({ apiKey });
    this.maxRetries = 3;
  }
  
  async executeWithRetry(operation, data) {
    let lastError;
    
    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        return await operation(data);
      } catch (error) {
        lastError = error;
        
        // Don't retry on validation errors
        if (error.code === 'VALIDATION_ERROR') {
          throw error;
        }
        
        // Retry with exponential backoff for rate limits
        if (error.code === 'RATE_LIMITED' && attempt < this.maxRetries) {
          const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
          console.log(`Rate limited. Retrying in ${delay}ms...`);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }
        
        // Retry on network errors
        if (error.code === 'NETWORK_ERROR' && attempt < this.maxRetries) {
          const delay = 1000 * attempt;
          console.log(`Network error. Retrying in ${delay}ms...`);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }
      }
    }
    
    throw lastError;
  }
  
  async createOrder(orderData) {
    return this.executeWithRetry(
      async (data) => await this.client.orders.create(data),
      orderData
    );
  }
  
  handleError(error) {
    const errorHandlers = {
      'VALIDATION_ERROR': () => {
        console.error('Validation failed:', error.details);
        // Show validation errors to user
      },
      'INSUFFICIENT_INVENTORY': () => {
        console.error('Not enough inventory:', error.details);
        // Update UI with available quantity
      },
      'PAYMENT_FAILED': () => {
        console.error('Payment failed:', error.message);
        // Prompt for different payment method
      },
      'RATE_LIMITED': () => {
        console.error('Rate limited. Please slow down requests.');
        // Implement request queuing
      },
      'UNAUTHORIZED': () => {
        console.error('Authentication failed. Check API key.');
        // Refresh authentication
      }
    };
    
    const handler = errorHandlers[error.code] || (() => {
      console.error('Unexpected error:', error.message);
    });
    
    handler();
  }
}

Step 7: Use Advanced Features

Pagination

Handle large datasets efficiently:
async function getAllOrders() {
  const allOrders = [];
  let cursor = null;
  
  do {
    const response = await stateset.orders.list({
      limit: 100,
      cursor: cursor
    });
    
    allOrders.push(...response.data);
    cursor = response.pagination.next_cursor;
    
  } while (cursor);
  
  console.log(`Total orders: ${allOrders.length}`);
  return allOrders;
}

Filtering and Searching

// Complex filtering
const orders = await stateset.orders.list({
  status_in: ['pending', 'processing'],
  created_after: '2024-01-01T00:00:00Z',
  total_amount_gte: 10000, // $100.00 in cents
  customer_email_like: '%@example.com',
  sort: 'created_at',
  order: 'desc'
});

// Full-text search
const searchResults = await stateset.orders.search({
  query: 'john doe widget',
  fields: ['customer.email', 'customer.name', 'items.name'],
  limit: 20
});

Batch Operations

// Batch update multiple orders
const updates = await stateset.orders.batchUpdate({
  filters: {
    status: 'pending',
    created_before: '2024-01-01T00:00:00Z'
  },
  updates: {
    status: 'cancelled',
    metadata: {
      cancelled_reason: 'expired'
    }
  }
});

console.log(`Updated ${updates.affected_count} orders`);

Idempotency

Ensure safe retries with idempotency keys:
const { v4: uuidv4 } = require('uuid');

async function createOrderSafely(orderData) {
  const idempotencyKey = `order-${uuidv4()}`;
  
  try {
    return await stateset.orders.create(orderData, {
      idempotencyKey: idempotencyKey
    });
  } catch (error) {
    if (error.code === 'DUPLICATE_REQUEST') {
      console.log('Order already created:', error.details.existing_id);
      return error.details.existing_resource;
    }
    throw error;
  }
}

Complete Example: Order Management System

Here’s a complete example that ties everything together:
const { StateSetClient } = require('stateset-node');
const express = require('express');
require('dotenv').config();

class OrderManagementSystem {
  constructor() {
    this.stateset = new StateSetClient({
      apiKey: process.env.STATESET_API_KEY
    });
    this.app = express();
    this.setupRoutes();
  }
  
  setupRoutes() {
    this.app.use(express.json());
    
    // Create order endpoint
    this.app.post('/api/orders', async (req, res) => {
      try {
        const order = await this.createOrder(req.body);
        res.json({ success: true, order });
      } catch (error) {
        this.handleApiError(error, res);
      }
    });
    
    // Get orders endpoint
    this.app.get('/api/orders', async (req, res) => {
      try {
        const orders = await this.getOrders(req.query);
        res.json({ success: true, orders });
      } catch (error) {
        this.handleApiError(error, res);
      }
    });
    
    // Update order status
    this.app.patch('/api/orders/:id/status', async (req, res) => {
      try {
        const order = await this.updateOrderStatus(
          req.params.id,
          req.body.status
        );
        res.json({ success: true, order });
      } catch (error) {
        this.handleApiError(error, res);
      }
    });
    
    // Webhook endpoint
    this.app.post('/webhooks/stateset', 
      express.raw({ type: 'application/json' }),
      async (req, res) => {
        await this.handleWebhook(req, res);
      }
    );
  }
  
  async createOrder(orderData) {
    // Validate order data
    this.validateOrderData(orderData);
    
    // Check inventory
    await this.checkInventory(orderData.items);
    
    // Create order with idempotency
    const idempotencyKey = `order-${Date.now()}-${orderData.customer.email}`;
    
    const order = await this.stateset.orders.create(orderData, {
      idempotencyKey
    });
    
    // Send confirmation email
    await this.sendOrderConfirmation(order);
    
    return order;
  }
  
  async getOrders(filters) {
    const orders = await this.stateset.orders.list({
      limit: filters.limit || 20,
      status: filters.status,
      created_after: filters.from_date,
      created_before: filters.to_date,
      sort: filters.sort || 'created_at',
      order: filters.order || 'desc'
    });
    
    return orders;
  }
  
  async updateOrderStatus(orderId, newStatus) {
    // Validate status transition
    const order = await this.stateset.orders.get(orderId);
    this.validateStatusTransition(order.status, newStatus);
    
    // Update order
    const updated = await this.stateset.orders.update(orderId, {
      status: newStatus
    });
    
    // Trigger relevant workflows
    await this.triggerStatusWorkflow(updated);
    
    return updated;
  }
  
  async handleWebhook(req, res) {
    // Verify signature
    if (!this.verifyWebhook(req)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    const event = JSON.parse(req.body);
    
    try {
      switch (event.type) {
        case 'order.created':
          await this.onOrderCreated(event.data.object);
          break;
        case 'order.shipped':
          await this.onOrderShipped(event.data.object);
          break;
        case 'return.created':
          await this.onReturnCreated(event.data.object);
          break;
      }
      
      res.json({ received: true });
    } catch (error) {
      console.error('Webhook processing error:', error);
      res.status(500).json({ error: 'Processing failed' });
    }
  }
  
  validateOrderData(data) {
    const required = ['customer', 'items', 'shipping_address'];
    for (const field of required) {
      if (!data[field]) {
        throw new Error(`Missing required field: ${field}`);
      }
    }
    
    if (!data.items.length) {
      throw new Error('Order must contain at least one item');
    }
  }
  
  async checkInventory(items) {
    for (const item of items) {
      const inventory = await this.stateset.inventory.get(item.sku);
      if (inventory.available < item.quantity) {
        throw new Error(
          `Insufficient inventory for ${item.sku}. ` +
          `Available: ${inventory.available}`
        );
      }
    }
  }
  
  validateStatusTransition(currentStatus, newStatus) {
    const validTransitions = {
      'pending': ['processing', 'cancelled'],
      'processing': ['shipped', 'cancelled'],
      'shipped': ['delivered', 'returned'],
      'delivered': ['returned'],
      'cancelled': [],
      'returned': []
    };
    
    if (!validTransitions[currentStatus].includes(newStatus)) {
      throw new Error(
        `Invalid status transition from ${currentStatus} to ${newStatus}`
      );
    }
  }
  
  handleApiError(error, res) {
    const statusMap = {
      'VALIDATION_ERROR': 400,
      'UNAUTHORIZED': 401,
      'FORBIDDEN': 403,
      'NOT_FOUND': 404,
      'RATE_LIMITED': 429,
      'INTERNAL_ERROR': 500
    };
    
    const status = statusMap[error.code] || 500;
    
    res.status(status).json({
      success: false,
      error: {
        code: error.code,
        message: error.message,
        details: error.details
      }
    });
  }
  
  start(port = 3000) {
    this.app.listen(port, () => {
      console.log(`Order Management System running on port ${port}`);
    });
  }
}

// Start the system
const oms = new OrderManagementSystem();
oms.start();

Next Steps

Now that you have a working integration, explore these advanced features:

Resources

Documentation

Code Examples

Support

Troubleshooting


🎉 Congratulations! You’ve successfully integrated with the StateSet API. You’re now ready to build powerful commerce applications! For questions or feedback, reach out on Discord or email api-support@stateset.com.