/
Documentation

Error Codes

Complete reference of all API error responses and how to handle them.

HTTP Status Codes

CodeMeaningWhat to Do
200SuccessRequest completed. Parse the response body.
201CreatedResource created successfully. Response contains the new record.
400Bad RequestInvalid input. Check the error message for which field failed validation.
401UnauthorizedToken missing, invalid, or expired. User needs to reload the addon.
403ForbiddenToken valid but scope not granted. Show "permission needed" message.
404Not FoundRecord doesn't exist or belongs to another org.
429Rate LimitedToo many requests. Wait for Retry-After seconds, then retry.
500Server ErrorSomething broke on our end. Retry once, then report if persistent.

Error Response Format

Error Response
// All errors follow this format:
{
  "error": "Human-readable error message"
}

// 403 responses include scope info:
{
  "error": "Insufficient permissions. Required scope: customers.write",
  "requiredScope": "customers.write",
  "grantedScopes": ["customers.read", "invoices.read"]
}

// 429 responses include retry info:
{
  "error": "Rate limit exceeded",
  "limit": 1000,
  "window": "1 hour",
  "retryAfterSeconds": 1847
}

Handling Errors in Code

error-handling.tsx
import { customers, formatError, ui } from '@syncbooks/addon-sdk'

// Basic error handling
try {
  await customers.create({ name: '' })
} catch (error: any) {
  console.log(error.status)    // 400
  console.log(error.message)   // "name is required"
  console.log(error.response)  // Full error body
  
  ui.notify(formatError(error), 'error')
}

// Retry with exponential backoff
import { retry } from '@syncbooks/addon-sdk'

const result = await retry(
  () => customers.list({ limit: 100 }),
  { maxAttempts: 3, delayMs: 1000 }
)

// Global error handler
useEffect(() => {
  const handler = (event: PromiseRejectionEvent) => {
    const error = event.reason
    if (error?.status === 401) {
      setSessionExpired(true)
    } else if (error?.status === 429) {
      ui.notify('Please wait — rate limit reached', 'warning')
    }
  }
  window.addEventListener('unhandledrejection', handler)
  return () => window.removeEventListener('unhandledrejection', handler)
}, [])

Common Issues & Solutions

"Loading..." forever

  • Check that SyncBooksProvider wraps your app
  • Check browser console for JavaScript errors
  • Verify the addon is loaded inside SyncBooks (not standalone)

"No session token" error

  • The SyncBooksProvider hasn't received context yet
  • Wait for ready === true before making API calls
  • If developing locally, use configureClient() with a test token

403 on API calls that should work

  • The organization didn't grant that scope during installation
  • Check scopes array from useSyncBooks()
  • Ask the org admin to reinstall with the required scope

CORS errors

  • The addon API allows all origins by design (auth is via token)
  • If you see CORS errors, your backend URL might be wrong
  • Check that you're calling /api/addon/v1/ not /api/v1/

Webhook not receiving events

  • Verify your webhook URL is publicly accessible (not localhost)
  • Check that your subscription is active: events.subscriptions()
  • Subscriptions auto-disable after 3 failed deliveries — re-subscribe
  • Verify your server responds with 200 within 10 seconds