Units always belong to a project. All endpoints are scoped under /projects/{projectId}/units.
List units
GET /projects/{projectId}/units
Scope: projects:read. Returns all units of the project. Returns 404 NOT_FOUND if the project doesn’t exist or belongs to another organization.
Response 200
{
"data": [
{
"id": "unit_...",
"projectId": "proj_...",
"name": "A-101",
"type": "apartment",
"price": 250000,
"area": 110,
"status": "available",
"createdAt": "2025-09-01T12:00:00.000Z",
"updatedAt": "2026-04-29T12:00:00.000Z"
}
]
}
Create a unit
POST /projects/{projectId}/units
Scope: projects:write.
Request body
{
"name": "A-101",
"type": "apartment",
"price": 250000,
"area": 110,
"status": "available"
}
| Field | Required | Notes |
|---|
name | yes | Free text, ≤ 255 chars. |
price | yes | Integer, in the project’s currency. |
type | no | Free text, ≤ 100 chars. |
area | no | Integer (square meters). |
status | no | available (default), reserved, sold. |
Response 201
Returns the created unit with all its fields.
Webhook side effects
A unit.created event is dispatched.
Update a unit
PATCH /projects/{projectId}/units/{unitId}
Scope: projects:write. Partial update. All fields optional, must provide at least one.
Request body
Same fields as create. Pass null to clear nullable fields, omit to leave untouched.
Response 200
Returns the updated unit.
Webhook side effects
- Always:
unit.updated.
- If
status changed: unit.status_changed. The payload includes both the unit and its parent project, so receivers don’t need a second API call to know what was sold.
The unit.status_changed event is the canonical signal for “a unit just became available / reserved / sold”. Use it to update your website inventory, send Slack notifications, or trigger commission flows.