Developers
Show batch ingestion and synchronization guides and endpoints, core concepts, and the content hash calculator.
Explore the docs

Work with data

Synchronization

Reconcile your records using version-2 content hashes and sync state.

On this page

At a glance#

  • Page compact ID/hash pairs for clients, disbursements, operations, or payments.
  • Compare resolved business state using version-2 hashes, not raw request JSON.
  • Never submit content_hash or treat a missing source record as permission to delete.

Use GET /api/sync-state/{table} to retrieve compact tenant state. Authenticate with your bearer API key. The only supported tables are clients, disbursements, operations, payments.

Read and page state

http
GET /api/sync-state/payments?limit=1000
Authorization: Bearer YOUR_API_KEY

The response has content_hash_version: 2, data containing {id, content_hash} pairs, and pagination with limit, has_more, and optional next_cursor. An empty tenant returns:

json
{"content_hash_version": 2, "data": [], "pagination": {"limit": 1000, "has_more": false}}

limit is an integer from 1 to 5000, default 1000. Results sort by ascending business ID. Follow the opaque next_cursor, URL-encoded; never construct one from the final ID. Stop when has_more is false. Invalid limits/cursors return 400.

Reconcile deliberately

  1. Gather the source business IDs and page the server state.
  2. Source ID absent on the server: plan a create.
  3. ID exists and verified v2 hashes match: skip the write.
  4. ID exists but business content differs: plan an update with a fresh event key.
  5. Review source-missing records separately; absence is not automatic authorization to delete them.
  6. Apply parent-before-child ordering, then inspect every result if using batches.

Clients that cannot reproduce canonicalization exactly should use ID-based reconciliation and let the API detect unchanged updates. Hash equality is not a guarantee of a transactionally frozen snapshot while other callers write.

Never send content_hash in an ingest payload. It is server-managed. Partial updates merge with the existing row before hashing; 200 unchanged performs no domain writes, timestamps, or generated child effects, though the request event is still recorded.

Version 2 canonicalization

The complete content hash specification includes the shared downloadable module and exact field/serialization rules. Use the local calculator to verify your implementation. If local hashing fails or context is missing, submit the normal update for that item rather than skipping it; the API still validates and detects unchanged content.

API 4.2.0 creditor repair: hash version stays 2, but disbursement hashes now always include the resolved pledge_creditor. Omitted updates preserve custom creditors. The operator must backfill affected stored hashes after deploying the corrected writer, then clients must refresh cached sync state. Rounding alone remains backfill-free; this repair is separate. Do not assume deployment/backfill completion merely because this guide is available.

The SHA-256 input is UTF-8 JSON with recursively sorted keys in an envelope containing data, the plural table name, and version: 2. A hash of arbitrary incoming JSON will not match.

  • Exclude step, content_hash, event_id, original_event_id, created_at, updated_at, change_date, and change_reason; omit null values.
  • Trim contract string fields. Normalize timestamps to UTC Z; preserve date-only values.
  • Round money to cents and represent it at persisted two-decimal scale. Do not apply that rule to ratios, counts, or arbitrary nested info values.
  • Recursively sort object keys and omit nested null object values. Omit empty top-level objects, so default info: {} equals omission. Preserve ordinary array order; sort purchase_clients by client_id.
  • Account for server defaults: company gender company, payment source client, disbursement is_pledged: true, coverage 0.9, fee 0.00, and default gross from individually rounded amount plus fee. Numeric coverage strings have trailing zeros removed for canonicalization.
  • Account for server-resolved values and existing rows when constructing the business state; a partial update is not a complete state hash. Version-2 examples are interoperability checks, not a substitute for the full field/default contract.

The trimmed string fields are client_id, name, email, phone, address, country, registration_number, contact_person, gender, disbursement_id, payment_id, operation_id, currency, kind, status, and source. Other strings, including arbitrary metadata, retain their whitespace. Timestamp normalization removes a zero millisecond part and trailing fractional zeros. Non-money integer numbers remain JSON numbers; non-integer numbers become plain decimal strings without insignificant trailing zeros, including inside info. This is hash formatting, not monetary rounding. Null array entries remain in place, unlike omitted null object properties.

Construct state from the documented business fields, not an entire read row: omit ignored legacy entity_type/entity_id and read-only/audit fields. For purchase disbursements include the effective purchase_clients links (an empty array when there are none); normalize each linked ID before sorting. Resolve loan payment client_id from its parent. Apply ingestion defaults and stored-value preservation first, including pledge_creditor: "CMK Capital" on create and the existing creditor when omitted on update. These resolved values belong in the hash input even when absent from the submitted event.

Download the version-2 language-neutral vectors. They include input/equivalent values, canonical JSON, and expected digests. They are not complete ingest requests. Decimal strings in equivalent values represent stored database values, not accepted request amount types. Match the parsed-number semantics before rounding; a decimal library cannot restore precision already lost in JSON parsing.

Version-1 vectors are historical and must not be used as the live contract. This rounding update preserves hashes of existing cent-valued records: hash version stays 2, with no backfill required for rounding alone. The hash version, documentation snapshot, and tenant setup version are different concepts.

Readiness and errors

  • 401: check bearer credentials.
  • 400: unsupported table, malformed cursor, or invalid limit.
  • 409 CONTENT_HASH_NOT_INITIALIZED: a page contains an invalid/uninitialized hash. Pause hash reconciliation and contact support. The current response text mentions reset/reingestion; do not reset or delete tenant data as a normal remedy.
  • 503 SCHEMA_NOT_READY: pause affected operations and contact support; retries do not perform tenant setup.
  • 500: unexpected read failure; use the correlation ID for support.

Retry hashes remain different

Business values 10.251 and 10.254 both round to 10.25, but changing the submitted value under a stored idempotency key still returns 409. Preserve original content and keys on retries; read the ingestion response guide.

Validate locally. Verify in sandbox.
The JSON validator checks structure and rounding. Credentials, existing records, and server-side state still require your test environment.

Search documentation

Start typing to find your next step.

    navigate Enter openLocal, private search