What you’ll build
A repeatable job that:- Reads a batch of contacts from your source system.
- Sends them to Spotzee with
external_idset to your stable customer ID. - Retains a stable batch identity and checks idempotency availability before retrying.
- Walks through every page of source data without re-importing what’s already in.
Prerequisites
You’ll need a
sk_ project key with write access. See Authentication for how to mint one.Walkthrough
1
Pick a stable identifier
Choose the field in your source system that uniquely identifies a customer and never changes. Usually the primary key from your customers table. Pass it to Spotzee as
external_id on every contact.Spotzee uses external_id as the upsert key: re-syncing the same external id updates the existing contact rather than creating a duplicate.2
Map your fields to Spotzee's schema
Spotzee contacts have a small set of first-class fields (
external_id, email, phone, timezone, locale) and a flexible data object for anything else, including first_name, last_name, and any custom attributes. Map your CRM columns to first-class fields where they line up; put everything else into data.3
Pick the batch endpoint that fits
Two batch endpoints are available. Both accept up to 100 contacts per call as a bare JSON array:
100 per call is the sweet spot for throughput; smaller batches limit the blast radius of a partial failure when using the synchronous form.
4
Send each batch with an idempotency key
Generate a unique key per batch. Include the batch version or a digest of its complete contents in that identity. The same first and last contact identifiers can occur in batches with different updates.The body is a bare JSON array, not wrapped in an object. Each item must include either
external_id or anonymous_id (or both, to link them).5
Handle partial failures
The synchronous endpoint (
POST /users/batch) returns results, succeeded, and failed. Each results entry contains either the saved user or an error. Items that succeeded are committed; failed items are returned with their identifier and an error.code from the error catalogue. Retry only the failed items in a fresh request.The async endpoint (PATCH /users) returns 204 immediately and does not surface per-item errors. Use the synchronous form when you need that visibility.6
Walk every page of source data
Use cursor pagination on your source system the same way you’d page Spotzee. See Pagination. Persist the cursor so a crashed job resumes where it stopped.
Pitfalls to avoid
- Don’t loop one contact at a time. The contact upsert endpoints are batch-only (
POST /users/batchsynchronous,PATCH /usersasync). Both accept up to 100 per call. Pack your batches. - Don’t reuse the same idempotency key for two different batches. Where enforcement is enabled, reuse with a different body returns
409 idempotency_key_mismatch. See Idempotency for the exact semantics. - Don’t write back to your source from the response. Spotzee returns its own
idfor each contact, but you should key offexternal_idon your side. That way a re-import doesn’t break.
Reference
- API surface: see Main API → Users
- Pagination: see Pagination
- Safe retries: see Idempotency
- Rate limits: see Rate limits