/
Documentation

How It Works

Understand the architecture, communication flow, and lifecycle of a SyncBooks add-on.

Architecture Overview

When a user opens your add-on in SyncBooks, here's what happens:

text
┌──────────────────────────────────────────────────────────┐
│  SyncBooks Web App (Next.js)                              │
│                                                           │
│  ┌──────────────────────────────────────────────────┐    │
│  │  Extension Host Component                         │    │
│  │  (manages the iframe lifecycle)                   │    │
│  │                                                   │    │
│  │  ┌───────────────────────────────────────────┐   │    │
│  │  │  YOUR ADD-ON (sandboxed iframe)            │   │    │
│  │  │                                            │   │    │
│  │  │  - Loaded from YOUR hosting (Vercel, etc.) │   │    │
│  │  │  - Runs YOUR code (React, Vue, anything)   │   │    │
│  │  │  - Communicates via postMessage bridge      │   │    │
│  │  │  - Makes API calls with session token       │   │    │
│  │  │                                            │   │    │
│  │  └───────────────────────────────────────────┘   │    │
│  │       ↕ window.postMessage                        │    │
│  └──────────────────────────────────────────────────┘    │
│                                                           │
└────────────────────────┬─────────────────────────────────┘
                         │  HTTPS API calls
                         ▼
┌──────────────────────────────────────────────────────────┐
│  SyncBooks Backend (Express.js)                           │
│                                                           │
│  /api/addon/v1/* endpoints                                │
│  ├─ addonSessionAuth middleware (validates JWT)           │
│  ├─ addonRateLimit middleware (1000 req/hour)             │
│  ├─ addonAuditLog middleware (logs every call)            │
│  └─ requireAddonScope middleware (enforces permissions)   │
│                                                           │
│  → MongoDB (organization-scoped data)                     │
└──────────────────────────────────────────────────────────┘

Communication Flow

The add-on and SyncBooks communicate through two channels:

1. postMessage Bridge (UI Communication)

For real-time UI interactions between your add-on and the SyncBooks host:

text
Your Addon                          SyncBooks Host
─────────                          ──────────────
    │                                     │
    │──── syncbooks:ready ───────────────→│  "I'm loaded"
    │                                     │
    │←─── syncbooks:context ─────────────│  "Here's your session"
    │     {orgId, userId, token, scopes}  │
    │                                     │
    │──── syncbooks:navigate ───────────→│  "Go to /invoices/123"
    │                                     │
    │──── syncbooks:notify ─────────────→│  "Show success toast"
    │                                     │
    │──── syncbooks:close ──────────────→│  "Close me"
    │                                     │
    │──── syncbooks:resize ─────────────→│  "Set height to 800px"
    │                                     │

2. REST API (Data Access)

For reading and writing data:

text
Your Addon                          SyncBooks API
─────────                          ─────────────
    │                                     │
    │── GET /api/addon/v1/customers ────→│
    │   Authorization: Bearer <token>     │
    │                                     │
    │←── 200 { data: [...], total: 42 } ─│
    │                                     │
    │── POST /api/addon/v1/invoices ───→│
    │   { customerId, lineItems, ... }    │
    │                                     │
    │←── 201 { data: { _id, ... } } ────│
    │                                     │

Session Lifecycle

  1. User clicks your add-on in SyncBooks (sidebar, action button, etc.)
  2. SyncBooks creates a session — a JWT containing org ID, user ID, app ID, and granted scopes
  3. Extension Host loads your iframe — passes the token via URL params
  4. Your SDK picks up the tokenSyncBooksProvider handles this automatically
  5. SDK sends "ready" message — SyncBooks responds with full context
  6. Your add-on renders — using the context and making API calls
  7. Token expires after 1 hour — user needs to reload to get a new session

Token handling is automatic

The SyncBooksProvider component handles all token extraction, context receiving, and API authorization automatically. You never need to manage tokens manually.

Permission System (Two Layers)

Layer 1: What the add-on CAN access

When you register your add-on, you declare which scopes you need (e.g., invoices.read, customers.write). The organization admin grants a subset of these during installation.

Layer 2: Who CAN use the add-on

The org admin decides which roles/users can see and use your add-on. This is configured after installation.

text
Developer declares:  ["invoices.read", "customers.read", "customers.write"]
                              ↓
Org admin grants:    ["invoices.read", "customers.read"]  ← they didn't grant write
                              ↓
API enforces:        GET /customers → ✅ allowed
                     POST /customers → ❌ 403 Forbidden (scope not granted)

Event System

Your add-on can subscribe to events that happen in SyncBooks:

text
1. Your addon calls: POST /events/subscribe
   { event: "invoice.paid", webhookUrl: "https://myapp.com/hook" }

2. Later, an invoice gets paid in SyncBooks

3. SyncBooks sends a POST to your webhookUrl:
   {
     event: "invoice.paid",
     data: { invoiceId: "...", amount: 5000 },
     timestamp: "2026-08-13T10:30:00Z"
   }
   Headers: X-SyncBooks-Signature: sha256=abc123...

4. Your server verifies the signature and processes the event

Rate Limiting

Each add-on is limited to 1,000 API requests per hour per organization. The response includes headers:

text
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1691920000  (Unix timestamp when limit resets)

If you exceed the limit, you'll receive a 429 Too Many Requests response with a Retry-After header.

Optimize your API usage

Use pagination (limit param), cache data client-side, and batch related reads together. Don't poll — use webhooks for real-time updates.