REST API Integration

This guide covers common integration patterns and best practices for working with the Otesse REST API.

Authentication Pattern

Every request must include authentication. We recommend creating a base client that handles this automatically:

class OtesseClient {
  private apiKey: string;
  private baseUrl: string;

  constructor(apiKey: string, baseUrl = 'https://api.otesse.com/v1') {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
    const url = `${this.baseUrl}${endpoint}`;

    const response = await fetch(url, {
      ...options,
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });

    // Handle rate limiting
    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') || '30');
      await this.sleep(retryAfter * 1000);
      return this.request<T>(endpoint, options);
    }

    if (!response.ok) {
      const error = await response.json();
      throw new OtesseError(error.error);
    }

    return response.json();
  }

  private sleep(ms: number) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // Convenience methods
  async getCustomers(params?: Record<string, string>) {
    const query = new URLSearchParams(params).toString();
    return this.request(`/customers?${query}`);
  }

  async createBooking(data: CreateBookingPayload) {
    return this.request('/bookings', {
      method: 'POST',
      body: JSON.stringify(data),
    });
  }
}

Pagination

All list endpoints return paginated results. Iterate through all pages:

async function getAllCustomers(client: OtesseClient) {
  const allCustomers = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await client.getCustomers({
      page: String(page),
      per_page: '100',
    });

    allCustomers.push(...response.data);

    hasMore = page < response.pagination.total_pages;
    page++;
  }

  return allCustomers;
}

Error Handling

Build error handling into your client:

class OtesseError extends Error {
  type: string;
  code: string;
  param?: string;

  constructor(error: { type: string; code: string; message: string; param?: string }) {
    super(error.message);
    this.type = error.type;
    this.code = error.code;
    this.param = error.param;
  }

  get isRetryable(): boolean {
    return ['rate_limit_exceeded', 'server_error'].includes(this.code);
  }
}

// Usage
try {
  const booking = await client.createBooking(data);
} catch (error) {
  if (error instanceof OtesseError) {
    if (error.code === 'missing_required_field') {
      console.error(`Missing field: ${error.param}`);
    } else if (error.isRetryable) {
      // Retry with backoff
    }
  }
}

Data Synchronization

For syncing Otesse data to your system:

Initial Sync

  1. Paginate through all records from each endpoint
  2. Store in your local database
  3. Record the latest updated_at timestamp

Incremental Sync

Option A: Polling — Periodically fetch records updated since the last sync:

GET /v1/customers?updated_after=2026-02-26T00:00:00Z

Option B: Webhooks (recommended) — Set up webhook subscriptions for real-time updates. This is more efficient and provides lower latency than polling.

Common Integration Scenarios

CRM Sync

Sync customer records between Otesse and your CRM:

  • Subscribe to customer.created and customer.updated webhooks
  • Map Otesse fields to your CRM fields
  • Handle conflicts with last-write-wins or manual merge

Accounting Integration

Export financial data to your accounting software:

  • Subscribe to invoice.paid and payment.refunded webhooks
  • Map invoices to your chart of accounts
  • Reconcile payments against Stripe settlements

Scheduling Integration

Sync bookings with external calendars:

  • Subscribe to booking.* webhooks
  • Create/update/delete calendar events based on booking status
  • Handle timezone conversion between Otesse zones and calendar time zones