Guide
Writing data
Writes run through the same validation as the PRESHos app: required fields, select options, field permissions, owner rules, and status workflows. They are made as the key's user and recorded in the workspace audit log.
POST /v1/objects/{object_type}/records takes { "fields": { ... } } keyed by field key. It returns 201 with the new record and an ETag header.
- Required fields from the field contract must be present.
- The owner defaults to the key's user. To assign someone else, set the owner field to a workspace user id; you need permission to do so.
- Status is set to the workflow's initial status. Change it afterwards with the status endpoint.
- Work items need
fields.work_item_type_id; setparent_work_item_idto create a child.
curl -X POST https://api.preshos.com/v1/objects/work-items/records \
-H "Authorization: Bearer $PRESHOS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8b4f1c1e-3d2a-4c7b-9f0e-2a6d5b7c9e10" \
-d '{
"fields": {
"work_item_type_id": "task",
"title": "Draft launch email",
"parent_work_item_id": "5120",
"due_date": "2026-10-15"
}
}'Field keys vary by workspace and type. Read them from GET /v1/objects/work-items?work_item_type_id=task before writing.
PATCH /v1/objects/{object_type}/records/{record_id} changes only the fields you send. If any field is unknown, read-only, or a status field, the whole request is rejected and nothing changes.
const API = 'https://api.preshos.com/v1';
const response = await fetch(`${API}/objects/companies/records/48213`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.PRESHOS_API_KEY}`,
'Content-Type': 'application/json',
'If-Match': '"9a1b2c3d4e5f60718293a4b5c6d7e8f9"',
},
body: JSON.stringify({
fields: { industry: 'Aerospace', custom_fields: { tier: 'enterprise' } },
}),
});Status is never changed with PATCH. Use POST /v1/objects/{object_type}/records/{record_id}/status with a target status_id:
- Find the workflow id in the object definition (
workflow.id, orwork_item_types[].workflow.idfor work items). - Fetch its statuses and allowed transitions with
GET /v1/workflows/{workflow_id}. - Send
{ "status_id": "<target status id>" }. Transitions the workflow does not allow, and approval-gated statuses, follow the same rules as the app and fail with422 invalid_write.
curl -X POST https://api.preshos.com/v1/objects/work-items/records/9312/status \
-H "Authorization: Bearer $PRESHOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status_id": "st-in-review" }'Every record carries a version, and single-record responses send it as an ETag header. To avoid overwriting someone else's change, send it back when you update:
- Read the record and keep its
version. - Send
If-Match: "<version>"with the PATCH (orexpected_versionin the body). - If the record changed in the meantime, the update fails with
412 stale_record. Read the record again, re-apply your change, and retry.
Without If-Match the last write wins.
Network failures can leave you unsure whether a write happened. Send an Idempotency-Key header (1–255 visible ASCII characters, such as a UUID) and retry with the same key and the same body:
- The first successful response is stored for 24 hours. Retries return it again with the header
Idempotent-Replayed: true, and the write is not repeated. - While the first request is still running, a retry gets
409 idempotency_key_in_use; wait briefly and retry. - Reusing a key with a different method, path, or body fails with
422 idempotency_key_reused. - Failed requests release the key, so a corrected retry with the same key can run.
- Idempotency keys are scoped to the API key that sends them.
Endpoints that accept Idempotency-Key: Apply a batch of changes atomically, Create a record, Update a record, Change a record's status, Link a record, Comment on a record.
DELETE /v1/objects/{object_type}/records/{record_id} permanently deletes a record. It needs the records:delete scope, delete permission on the record, and an object whose capabilities.delete is true. CRM association rows are removed with the record. There is no undo.
POST /v1/objects/{object_type}/records/batch applies 1–100 operations to one object type in a single transaction. Every operation is authorized before anything is written, and if any operation fails, none are applied.
| Action | Does | Needs |
|---|---|---|
create | Creates a record from fields. | records:write |
update | Updates record id with fields. | records:write |
status | Moves record id to status_id. | records:write |
delete | Deletes record id. | records:write and records:delete |
upsert | Updates the record matching match (for example { "domain": "acme.com" }), or creates it. | records:write |
link, unlink | Links or unlinks target_id through association_id. | records:write |
Each existing record may appear only once per batch. The response lists each operation's resulting id.
curl -X POST https://api.preshos.com/v1/objects/companies/records/batch \
-H "Authorization: Bearer $PRESHOS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"operations": [
{ "action": "upsert", "target": "companies", "match": { "domain": "acme.com" }, "fields": { "name": "Acme Corporation" } },
{ "action": "update", "id": "48213", "fields": { "industry": "Aerospace" } },
{ "action": "status", "id": "48214", "status_id": "customer" }
]
}'- Discover a record's relationships with
GET /v1/objects/{object_type}/records/{record_id}/relationships. Each has anid, the relatedobject_type, acardinality, andcan_link. - List linked records with
GET /v1/objects/{object_type}/records/{record_id}/relationships/{association_id}. This requires workspace-wide read on the related object type. - Link with
POST /v1/objects/{object_type}/records/{record_id}/relationships/{association_id}and{ "target_id": "..." }whencan_linkis true. Reference relationships replace the previous target; custom many-to-many relationships add a link. You need update permission on the record and read permission on the target. - Unlink with
DELETE /v1/objects/{object_type}/records/{record_id}/relationships/{association_id}/{target_id}. A reference is only cleared if it still points totarget_id; otherwise the call fails with412.
Read a record's comment thread with GET /v1/objects/{object_type}/records/{record_id}/comments and post with POST /v1/objects/{object_type}/records/{record_id}/comments. Comments are plain text and appear in PRESHos in real time, authored by the key's user. Comment threads are available for work-items, companies, contacts, deals, meetings, events.
curl -X POST https://api.preshos.com/v1/objects/companies/records/48213/comments \
-H "Authorization: Bearer $PRESHOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Kickoff is confirmed for Monday.", "mention_user_ids": [17] }'mention_user_ids must be active workspace members (see GET /v1/users). Mentioned users are notified when they can read the record.
Every write through the API is recorded in the workspace audit log with the API key, the acting user, the object, the record, and the field keys that changed. Field values are never written to the log.