> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getkato.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Sync client records

> Map external client data into your Kato workspace using schema discovery and UUID-based upsert.

Use this recipe when another system owns your client list and Kato needs a copy
for day-to-day work. Run the integration on a server or trusted job runner.

## Before you start

* Create a developer access key with `objects:read` and `records:write`.
* Store it as `KATO_API_KEY` in your runner's secret store.
* Have an object in Kato with fields for the values you want to sync.
* Keep a durable mapping from each external client ID to a Kato record UUID.

Follow the [API quickstart](/api-reference/quickstart) to create a key and verify
its workspace before writing data.

## Discover the destination schema

Use [List objects](/api-reference/objects/list) and `GET /v1/objects/{object}/fields`
to find your object and field identifiers. Match types and required fields, and
exclude fields marked `readOnly` from your writes.

The example below assumes an object with slug `clients` and a writable text field
with slug `name`. Replace them with identifiers from your workspace.

## Assign a stable ID

For each external client, generate a UUID once and save it in your mapping before
the first request. Set `KATO_RECORD_UUID` to that value. Reuse it on later runs;
generating a new UUID each time creates duplicate records.

## Upsert the record

```bash theme={null}
curl --fail-with-body --silent --show-error \
  -X PUT "https://api.getkato.io/v1/records/$KATO_RECORD_UUID" \
  -H "Authorization: Bearer $KATO_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{"object":"clients","values":{"name":"Northlane Studio"}}'
```

A new record returns `201`; an update returns `200`. Supplied values are merged,
so omitted fields retain their values. A `409` means the UUID conflicts with a
record in another workspace or object. Investigate the mapping before retrying.
Upsert does not restore an archived record automatically.

## Check the result

Retrieve the record with `GET /v1/records/{id}` and compare the response to the
source. Response `values` use field IDs, even when the request used slugs. Open
the record in Kato and confirm the fields appear as expected.

## Keep it reliable

* Respect `Retry-After` on `429` responses and use bounded backoff.
* Decide which system owns each field before allowing edits in both places.
* If Kato changes must flow back, [subscribe to webhooks](/api-reference/webhooks/setup),
  verify signatures, deduplicate event IDs, and avoid echoing changes indefinitely.
* When processing an event, fetch the current record before applying it to a
  mirror; events can be delivered out of order.

Read the [Objects and records reference](/api-reference/objects-and-records) for
archive, restore, pagination, and write behavior. Stable IDs prevent duplicate
record identities; they do not guarantee exactly-once downstream processing.


## Related topics

- [Recipes](/recipes/overview.md)
- [Loops](/integrations/loops.md)
- [Integrations](/integrations/overview.md)
- [Bring your data into Kato](/documentation/importing-data.md)
- [Objects and records API](/api-reference/objects-and-records.md)
