---
title: "Update Village Onboarding"
method: PATCH
path: "/users/village-onboarding"
tags: ["users"]
---

# Update Village Onboarding

`PATCH /users/village-onboarding`

## Request

- Base URL: `https://api.projectindi.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.projectindi.com/users/village-onboarding`
- Auth: none declared

Persist how far a village_free worker got through the guided walkthrough.

Server-side rather than local storage because the villager is expected to
start on the website and finish in the app; a walkthrough that re-runs on
the second device is exactly the friction this audience won't forgive.

Stored as one namespaced key on the ``onboarding_setup_prefs`` JSONB column.
**Monotonicity lives in SQL, not here**: ``merge_village_onboarding_step``
takes ``GREATEST(stored, p_step)`` in the same statement as the write, so
the row lock serialises it. A Python read-then-compare would be a TOCTOU -
two devices (the expected web→app path) both read the old value, and the
lower write lands last, dropping the villager back into a walkthrough they
already skipped. The RPC refuses any ``p_user_id`` other than
``auth.uid()``, so a caller can only ever move their own progress.

Returns the whole merged column, so a client hydrating its prefs snapshot
from the response can't silently lose the other keys.

## Request body

- UpdateVillageOnboardingRequest
  - `step` integer, required

## Response `200`

Successful Response

- UpdateVillageOnboardingResponse
  - `success` boolean, required
  - `onboarding_setup_prefs` object, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/projectindi/apis/indi-agent-api-first-principles.md) - [All operations](https://skmtc.dev/projectindi/apis/indi-agent-api-first-principles/llms.txt) - [OpenAPI document](https://skmtc.dev/projectindi/apis/indi-agent-api-first-principles/revisions/25a7813e21fd?raw)
