Updates, deletes & concurrency
Organization entities (sectors, cost centers, job roles, groups, employees, products) support create, update, and delete. Writes use optimistic concurrency so two integrations editing the same record can never silently clobber each other.
The version token — the ETag header
Every single-record response — GET /{resource}/{id}, and the body returned by
create, update, and delete — carries the record's current version in a strong
ETag response header (e.g. ETag: "4"). (List responses cover many records and
carry no ETag; fetch the item by id to get its current version before updating.)
To update or delete a record, send that value back in an If-Match request header.
The server applies the change only if the version still matches; otherwise it returns
409 and changes nothing.
The version is exposed only through the ETag header — it is never included in
the response body.
# 1. Read the record; capture its ETag (use -i to see response headers)
curl -i "https://{host}/v1/sectors/sec_123" -H "Authorization: Bearer sk_…"
# → HTTP/2 200
# → ETag: "4"
# → { "data": { "id": "sec_123", "name": "Welding", … } }
# 2. Update, echoing the ETag back as If-Match
curl -X PUT "https://{host}/v1/sectors/sec_123" \
-H "Authorization: Bearer sk_…" \
-H 'If-Match: "4"' \
-H "Content-Type: application/json" \
-d '{ "name": "Welding & Cutting" }'
# → HTTP/2 200
# → ETag: "5" (the new version after your change)
If someone else updated sec_123 between your read and your write, its version is
now 5, your If-Match: "4" is stale, and you get a 409. Re-read to get the
fresh ETag, reapply your change on top of the fresh record, and retry.
You may instead send the version in the request body as "_version": 4. The
If-Match header is preferred (it's the standard HTTP mechanism and keeps the
body purely your data), but both are accepted. If the version is missing on an
update or delete, the request is rejected with 400.
Partial updates — no blanking
An update changes only the fields you send. Fields you omit are left exactly as they were — there is no "replace the whole record" semantics, so you cannot accidentally blank a field by leaving it out.
Read-only fields are silently ignored
Some fields appear in a read response but are not writable over this API. If you
send them on a create or update, the request still succeeds (200/201) but those
fields are silently ignored — they are not changed. These are:
statuson every entity — it is read-only here; manage activation in the console.allowWebAccessandaccessGroupIdson employees — web access requires console-side provisioning, so it cannot be granted through this API.
A common pitfall: read an entity (which returns status), edit the object, and PUT
it back. The status field rides along, the write returns 200, but status is
unchanged. Send only the fields you actually intend to change.
Delete
DELETE removes the record: it stops appearing in lists and GET-by-id then
returns 404. Delete also requires the current version via If-Match, and returns
200 with the deleted record in the body (not 204):
curl -X DELETE "https://{host}/v1/sectors/sec_123" \
-H "Authorization: Bearer sk_…" \
-H 'If-Match: "5"'
# → HTTP/2 200
# → { "data": { "id": "sec_123", "name": "Welding & Cutting", … } }
Delete is blocked while the entity is still referenced
You cannot delete an entity that other records still point at — that would orphan
those references. If, say, an employee still has a sectorId of the sector you're
deleting (or a group still lists a product in its rules), the delete is rejected
with 409 (type: urn:smartepi:error:resource_in_use) and nothing changes. Re-point or remove
the dependents first, then delete. Deleting a product cascades to its own sizes
and certificates, which is allowed.
Foreign keys
Any foreign key you set on a create or update must exist in your organization, or
the write is rejected with 422 (see Errors). This keeps writes from
ever referencing data that isn't yours.
When a change reaches the devices
A successful write is durable immediately — your next GET reflects it, and any
other API client sees it right away. The machine and desktop apps pick the change
up on their next sync (when a device reconnects or starts up), the same
mechanism that keeps them current with changes made in the web console. So a new
employee or an updated cost center created through this API will appear on the
devices — but not necessarily the instant you write it. A device that is offline,
or between sync cycles, sees the change once it next syncs. If you need a device to
reflect a change at a specific moment, account for this sync delay rather than
assuming the write is visible on the hardware immediately.