Operations & async write-back
Writes to PropSocket are asynchronous. A create or update doesn't change the source PMS inline — it returns an Operationimmediately, and PropSocket reconciles the change to the PMS in the background. You poll GET /v1/operations/{id} until the Operation reaches a terminal status.
1. Submit the write
Every write requires an Idempotency-Key header — a caller-generated, one-per-logical-write value. Reusing the same key with the same payload safely returns the same Operation; reusing it with adifferent payload is a 409 (see below).
curl -sS -X POST https://api.propsocket.io/v1/units \
-H "Authorization: Bearer ps_live_..." \
-H "Idempotency-Key: unit-create-maple-court-1042" \
-H "Content-Type: application/json" \
-d '{"unit_number":"1042","bedrooms":2,"bathrooms":2,"square_feet":1080,"max_occupants":4,"note":"South-facing, renovated 2025."}'2. Receive a pending Operation
The response is 202 Accepted with an Operation in a non-terminal state. Note resource.id and resource.x_id arenull — the record doesn't exist in the PMS yet.
{
"id": "opr_01HXABCDEFGHJKMNPQRSTVWXYZ",
"object": "operation",
"type": "unit.create",
"status": "queued",
"source": {
"state": null,
"applied_at": null
},
"reconcile": {
"state": "pending",
"attempts": 0,
"next_attempt_at": "2026-07-05T18:00:05Z",
"last_error": null
},
"resource": {
"type": "unit",
"id": null,
"x_id": null
},
"result": null,
"idempotency_key": "unit-create-maple-court-1042",
"created_at": "2026-07-05T18:00:00Z"
}3. Poll until terminal
Poll GET /v1/operations/{id} with backoff, stopping whenstatus becomes succeeded or failed. The poll itself always returns 200 — success and failure are carried in the Operation body, not as HTTP errors.
import time, requests
def wait_for(op_id, key):
while True:
op = requests.get(
f"https://api.propsocket.io/v1/operations/{op_id}",
headers={"Authorization": f"Bearer {key}"},
).json()
if op["status"] in ("succeeded", "failed"):
return op
time.sleep(min(2 ** op["reconcile"]["attempts"], 30)) # backoff, cap 30s4a. Terminal — succeeded
On success, resource.id and resource.x_id are now populated — this is how you learn the CDM id of the record you created, to read it back or reference it later.
{
"id": "opr_01HXABCDEFGHJKMNPQRSTVWXYZ",
"object": "operation",
"type": "unit.create",
"status": "succeeded",
"source": {
"state": "applied",
"applied_at": "2026-07-05T18:00:12Z"
},
"reconcile": {
"state": "reconciled",
"attempts": 1,
"next_attempt_at": null,
"last_error": null
},
"resource": {
"type": "unit",
"id": "unt_01HX1H6V9P4MFXCZW3RNS5EDGB",
"x_id": "entrata-unit-55120"
},
"result": {
"id": "unt_01HX1H6V9P4MFXCZW3RNS5EDGB"
},
"idempotency_key": "unit-create-maple-court-1042",
"created_at": "2026-07-05T18:00:00Z"
}4b. Terminal — failed
On failure, status is failed andreconcile.last_error explains why. Use it to distinguish a caller mistake (fix and resubmit with a new key) from a PropSocket-side issue (escalate to support with the Operationid).
{
"id": "opr_01HXABCDEFGHJKMNPQRSTVWXYZ",
"object": "operation",
"type": "unit.create",
"status": "failed",
"source": {
"state": "rejected",
"applied_at": null
},
"reconcile": {
"state": "failed",
"attempts": 3,
"next_attempt_at": null,
"last_error": "Entrata rejected unit_number '1042': a unit with this number already exists for property entrata-property-3391."
},
"resource": {
"type": "unit",
"id": null,
"x_id": null
},
"result": null,
"idempotency_key": "unit-create-maple-court-1042",
"created_at": "2026-07-05T18:00:00Z"
}Idempotency conflicts
Reusing an Idempotency-Key with a different payload returns409 as application/problem+json:
{
"type": "https://propsocket.io/problems/idempotency-key-conflict",
"title": "Idempotency-Key conflict",
"status": 409,
"detail": "Idempotency-Key 'unit-create-maple-court-1042' was already used with a different request payload.",
"request_id": "req_01JQ5ZK8Y0N9V3W7X2M4B6D8F"
}Reference
/v1/operations/{id}Retrieve an operation
Path & headers
id· path · required
Response · 200
The operation
| Field | Type | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| id* | string | ||||||||||||||||
| object* | string | ||||||||||||||||
| type* | string | ||||||||||||||||
| status* | string | ||||||||||||||||
| source | object | ||||||||||||||||
| |||||||||||||||||
| reconcile | object | ||||||||||||||||
| |||||||||||||||||
| resource | object | ||||||||||||||||
| |||||||||||||||||
| result | object | null | ||||||||||||||||
| idempotency_key | string | null | ||||||||||||||||
| created_at* | string (date-time) | ||||||||||||||||
Errors
401— Missing or invalid API key404— Resource not found429— Rate limit exceeded
Next
Back to the API reference overview, or see the writable entities: Units,Residents, Leases.