---
title: "Reconcile Identities"
method: POST
path: "/identities/reconcile/"
tags: ["Identity"]
---

# Reconcile Identities

`POST /identities/reconcile/`

## Request

- Base URL: `https://api.zerosettle.io/v1`
- URL: `https://api.zerosettle.io/v1/identities/reconcile/`
- Auth: API key in header `X-ZeroSettle-Key`

Moves one id's purchases, credits, analytics and experiment rows onto another id, from your server. Call it when a guest signs up and you know the guest id and the account id are the same person.

Authenticated with your **secret key** (`zs_sk_*`), sent in `X-ZeroSettle-Key` or as `Authorization: Bearer`. Never call it from an app. A publishable key is refused with `401 secret_key_required`.

The merge is one way, and an id that has been merged away cannot be merged again. If both customers hold a live entitlement to the same non-consumable product (a subscription, including a trial or one cancelled but still running out its period, or a one-time unlock; consumables never conflict), the call is refused and nothing moves. Stripe and the stores are never changed. If `from` has no customer record at all (no purchase, checkout or Stripe customer), only its analytics and experiment rows move: the call succeeds with zero counts, sends no event and sets up no forwarding. A guest with a customer record but no purchases takes a normal merge, with zero counts, forwarding and an `identity.reconciled` event. Purchased credits move and add to the account's balance; plan allowances do not move.

**Idempotency.** Send an `Idempotency-Key`. A replay with the same key and the same ids within 24 hours returns the stored result. After 24 hours the same ids still return the original success. A key reused with different ids is refused with `422 idempotency_mismatch`, permanently: a key stays bound to the ids it first succeeded with. Refusals are not stored, so a retry after a refusal runs again.

A merge sends an `identity.reconciled` webhook, including a merge that moved nothing. An analytics-only move sends none.

**Limits.** 300 calls a minute and 50,000 a day per project, counted separately from link-token presentations. Over a limit the response is `429` with `Retry-After`.

## Headers

- `Idempotency-Key` string, required

## Request body

- object
  - `from` string, required: The id to merge away, usually the guest id. Ids are matched without regard to letter case.
  - `into` string, required: The id that keeps the purchases, usually the account id. If this id was merged away earlier, ZeroSettle follows it to the id that kept it.
  - `reason` string: Optional note stored on the reconciliation record.

## Response `200`

The ids were reconciled, or this is the replay of an earlier success.

- ReconcileResponse
  - `reconciliation_id` integer: The id of the reconciliation record.
  - `from` string: The id that was merged away.
  - `into` string: The id that kept the purchases.
  - `moved` object
    - `transactions` integer: Transactions moved. `0` when only analytics moved.
    - `entitlements` integer: Entitlements moved. `0` when only analytics moved.

## Other responses

- `400`: `idempotency_key_required` (the `Idempotency-Key` header is missing or longer than 255 characters) or `invalid_request` (the body is not JSON with string `from` and `into` of at most 255 characters, or `reason` is longer than 500).
- `401`: `secret_key_required`: no key, a publishable key, or a secret key that is not valid.
- `409`: `reconcile_refused`. Nothing moved. The specific reason is on the customer page in the dashboard, not in this response.
- `422`: `idempotency_mismatch`. The `Idempotency-Key` is already bound to different ids.
- `429`: `rate_limited`. Too many reconcile calls.

## Changes

- **2026-10-26** `8b59573fcc02`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/zerosettle/apis/zerosettle-iap-api/changes/identities/reconcile/post.md)

---

[API](https://skmtc.dev/zerosettle/apis/zerosettle-iap-api.md) - [All operations](https://skmtc.dev/zerosettle/apis/zerosettle-iap-api/llms.txt) - [OpenAPI document](https://skmtc.dev/zerosettle/apis/zerosettle-iap-api/revisions/4cd9e227d9b8?raw)
