Skip to main content

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.

tip

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:

  • status on every entity — it is read-only here; manage activation in the console.
  • allowWebAccess and accessGroupIds on 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.