Contact Sync

Sync contacts from your database to Owlat using upsert patterns and bulk operations.

Sync contacts from your database to Owlat using upsert patterns and bulk operations.

Upsert a single contact

The API doesn't have a dedicated upsert endpoint, but you can build one with create + update:

import {
  Owlat,
  ConflictError,
} from '@owlat/sdk-js'

const owlat = new Owlat('lm_live_...')

async function upsertContact(email: string, data: { firstName?: string; lastName?: string }) {
  try {
    // Try creating first
    return await owlat.contacts.create({ email, ...data })
  } catch (error) {
    if (error instanceof ConflictError) {
      // Contact already exists — update instead
      return await owlat.contacts.update(email, data)
    }
    throw error
  }
}

await upsertContact('mira@acme.io', {
  firstName: 'Mira',
  lastName: 'Chen',
})

Bulk sync

Use Promise.allSettled to sync many contacts without one failure stopping the batch. Add concurrency control to stay within rate limits:

import { Owlat, RateLimitError } from '@owlat/sdk-js'

const owlat = new Owlat('lm_live_...')

interface UserRecord {
  email: string
  firstName: string
  lastName: string
}

async function syncBatch(users: UserRecord[], concurrency = 5) {
  const results: PromiseSettledResult<unknown>[] = []

  // Process in chunks to respect rate limits
  for (let i = 0; i < users.length; i += concurrency) {
    const chunk = users.slice(i, i + concurrency)
    const chunkResults = await Promise.allSettled(
      chunk.map((user) => upsertContact(user.email, {
        firstName: user.firstName,
        lastName: user.lastName,
      }))
    )
    results.push(...chunkResults)
  }

  const succeeded = results.filter((r) => r.status === 'fulfilled').length
  const failed = results.filter((r) => r.status === 'rejected').length
  console.log(`Synced: ${succeeded} succeeded, ${failed} failed`)

  return results
}
Rate limits

The API enforces per-key rate limits. If you're syncing thousands of contacts, keep concurrency low (3–5) and add a delay between chunks if you hit 429 responses. See Authentication for rate limit details.

Add synced contacts to a topic

After syncing, you can add contacts to a topic for targeted campaigns:

async function syncAndAddToTopic(users: UserRecord[], topicId: string) {
  // 1. Sync contacts
  await syncBatch(users)

  // 2. Add each to the topic
  const topicResults = await Promise.allSettled(
    users.map((user) =>
      owlat.topics.addContact({
        topicId,
        email: user.email,
      })
    )
  )

  const added = topicResults.filter((r) => r.status === 'fulfilled').length
  console.log(`Added ${added}/${users.length} contacts to topic ${topicId}`)
}

Full sync script

Putting it all together — a script that reads users from your database and syncs them to Owlat:

import { Owlat, ConflictError } from '@owlat/sdk-js'

const owlat = new Owlat(process.env.OWLAT_API_KEY!)

async function upsertContact(email: string, data: { firstName?: string; lastName?: string }) {
  try {
    return await owlat.contacts.create({ email, ...data })
  } catch (error) {
    if (error instanceof ConflictError) {
      return await owlat.contacts.update(email, data)
    }
    throw error
  }
}

async function main() {
  // Replace with your actual database query
  const users = await db.users.findMany({
    where: { emailVerified: true },
    select: { email: true, firstName: true, lastName: true },
  })

  console.log(`Syncing ${users.length} contacts...`)

  for (let i = 0; i < users.length; i += 5) {
    const chunk = users.slice(i, i + 5)
    await Promise.allSettled(
      chunk.map((u) => upsertContact(u.email, {
        firstName: u.firstName,
        lastName: u.lastName,
      }))
    )
    console.log(`Progress: ${Math.min(i + 5, users.length)}/${users.length}`)
  }
}

main()

Next steps