Use this flow when you already have a customer record system (a CRM, a billing platform, your own database) and want Spotzee to mirror those users so you can market to them.Documentation Index
Fetch the complete documentation index at: https://docs.spotzee.com/llms.txt
Use this file to discover all available pages before exploring further.
What you’ll build
A repeatable job that:- Reads a batch of users from your source system.
- Sends them to Spotzee with
external_idset to your stable customer ID. - Uses an
Idempotency-Keyso timeouts and retries are safe. - 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
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 user.Spotzee uses external_id as the upsert key: re-syncing the same external id updates the existing user rather than creating a duplicate.Map your fields to Spotzee's schema
Spotzee users 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.| Your column | Spotzee field |
|---|---|
customers.id | external_id |
customers.email_address | email |
customers.phone_e164 | phone |
customers.locale | locale |
customers.first_name, last_name | data.first_name, data.last_name |
| everything else | data.<your_key> |
Pick the batch endpoint that fits
Two batch endpoints are available. Both accept up to 100 users 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.
| Endpoint | Behaviour | Use when |
|---|---|---|
POST /users/batch | Synchronous: the response reports per-item success or failure before returning | You want immediate confirmation of which items failed |
PATCH /users | Asynchronous: queued for processing, returns 204 No Content | Fire-and-forget bulk imports where eventual consistency is fine |
Send each batch with an idempotency key
Generate a unique key per batch. A deterministic key derived from the first and last The body is a bare JSON array, not wrapped in an object. Each item must include either
external_id works well, since a retry from a queue then uses the same key.external_id or anonymous_id (or both, to link them).Handle partial failures
The synchronous endpoint (
POST /users/batch) returns a per-item result array. Items that succeeded are committed; failed items are returned with their identifier and a 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.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 user at a time. The user 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. Spotzee returns
409 idempotency_key_mismatchif you do. See Idempotency for the exact semantics. - Don’t write back to your source from the response. Spotzee returns its own
idfor each user, 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