{"openapi":"3.0.0","paths":{"/api/v2/folders":{"get":{"operationId":"FolderListControllerV2_list","summary":"List folders","description":"Returns folders accessible to the authenticated workspace user. `fields[]` is not included in list responses — fetch a single folder for the full schema.","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Folder"}}}}},"400":{"description":"INVALID_REQUEST / INVALID_FIELD_TYPE / INVALID_FIELD_DEFINITION / INVALID_FIELD_OPTIONS / LABEL_RESERVED / IMMUTABLE_PROPERTY / DUPLICATE_PRESET / TOO_MANY_PRESETS / TOO_MANY_FIELDS.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"DUPLICATE_PRESET","message":"Duplicate preset value in field \"1\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"fieldId":"1","value":"Tech"}}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"402":{"description":"FOLDER_LIMIT_EXCEEDED — workspace plan paywall.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_LIMIT_EXCEEDED","message":"Workspace folder limit reached","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"limit":25}}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / FIELD_NOT_FOUND. Access-denied is intentionally hidden as 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_NOT_FOUND","message":"Folder 000000000000000000000000 not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"FOLDER","folderId":"000000000000000000000000"}}}}}},"409":{"description":"FOLDER_CONFLICT (optimistic concurrency) / FIELD_PROTECTED / FIELD_NAME_EXISTS_IN_FOLDER.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_CONFLICT","message":"Folder was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Folders"],"security":[{"BearerAuth":[]}]},"post":{"operationId":"FolderListControllerV2_create","summary":"Create a folder","description":"Creates a folder, optionally with an initial set of fields. Reserved labels, duplicate names and field-options compatibility are validated by service-schema.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFolder"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Folder"}}}},"400":{"description":"INVALID_REQUEST / INVALID_FIELD_TYPE / INVALID_FIELD_DEFINITION / INVALID_FIELD_OPTIONS / LABEL_RESERVED / IMMUTABLE_PROPERTY / DUPLICATE_PRESET / TOO_MANY_PRESETS / TOO_MANY_FIELDS.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"DUPLICATE_PRESET","message":"Duplicate preset value in field \"1\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"fieldId":"1","value":"Tech"}}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"402":{"description":"FOLDER_LIMIT_EXCEEDED — workspace plan paywall.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_LIMIT_EXCEEDED","message":"Workspace folder limit reached","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"limit":25}}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / FIELD_NOT_FOUND. Access-denied is intentionally hidden as 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_NOT_FOUND","message":"Folder 000000000000000000000000 not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"FOLDER","folderId":"000000000000000000000000"}}}}}},"409":{"description":"FOLDER_CONFLICT (optimistic concurrency) / FIELD_PROTECTED / FIELD_NAME_EXISTS_IN_FOLDER.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_CONFLICT","message":"Folder was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Folders"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}":{"get":{"operationId":"FolderControllerV2_get","summary":"Get a folder","description":"Returns the folder with the full `fields[]` schema.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Folder"}}}},"400":{"description":"INVALID_REQUEST / INVALID_FIELD_TYPE / INVALID_FIELD_DEFINITION / INVALID_FIELD_OPTIONS / LABEL_RESERVED / IMMUTABLE_PROPERTY / DUPLICATE_PRESET / TOO_MANY_PRESETS / TOO_MANY_FIELDS.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"DUPLICATE_PRESET","message":"Duplicate preset value in field \"1\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"fieldId":"1","value":"Tech"}}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"402":{"description":"FOLDER_LIMIT_EXCEEDED — workspace plan paywall.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_LIMIT_EXCEEDED","message":"Workspace folder limit reached","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"limit":25}}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / FIELD_NOT_FOUND. Access-denied is intentionally hidden as 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_NOT_FOUND","message":"Folder 000000000000000000000000 not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"FOLDER","folderId":"000000000000000000000000"}}}}}},"409":{"description":"FOLDER_CONFLICT (optimistic concurrency) / FIELD_PROTECTED / FIELD_NAME_EXISTS_IN_FOLDER.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_CONFLICT","message":"Folder was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Folders"],"security":[{"BearerAuth":[]}]},"patch":{"operationId":"FolderControllerV2_update","summary":"Update a folder","description":"Updates `name` and/or `description`. Pass `description: null` to clear.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFolder"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Folder"}}}},"400":{"description":"INVALID_REQUEST / INVALID_FIELD_TYPE / INVALID_FIELD_DEFINITION / INVALID_FIELD_OPTIONS / LABEL_RESERVED / IMMUTABLE_PROPERTY / DUPLICATE_PRESET / TOO_MANY_PRESETS / TOO_MANY_FIELDS.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"DUPLICATE_PRESET","message":"Duplicate preset value in field \"1\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"fieldId":"1","value":"Tech"}}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"402":{"description":"FOLDER_LIMIT_EXCEEDED — workspace plan paywall.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_LIMIT_EXCEEDED","message":"Workspace folder limit reached","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"limit":25}}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / FIELD_NOT_FOUND. Access-denied is intentionally hidden as 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_NOT_FOUND","message":"Folder 000000000000000000000000 not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"FOLDER","folderId":"000000000000000000000000"}}}}}},"409":{"description":"FOLDER_CONFLICT (optimistic concurrency) / FIELD_PROTECTED / FIELD_NAME_EXISTS_IN_FOLDER.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_CONFLICT","message":"Folder was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Folders"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/fields":{"post":{"operationId":"FieldListControllerV2_addField","summary":"Add a field to a folder","description":"Returns the full updated folder including all fields. Field type / options compatibility, reserved labels and duplicate names are validated by service-schema.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateField"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Folder"}}}},"400":{"description":"INVALID_REQUEST / INVALID_FIELD_TYPE / INVALID_FIELD_DEFINITION / INVALID_FIELD_OPTIONS / LABEL_RESERVED / IMMUTABLE_PROPERTY / DUPLICATE_PRESET / TOO_MANY_PRESETS / TOO_MANY_FIELDS.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"DUPLICATE_PRESET","message":"Duplicate preset value in field \"1\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"fieldId":"1","value":"Tech"}}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"402":{"description":"FOLDER_LIMIT_EXCEEDED — workspace plan paywall.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_LIMIT_EXCEEDED","message":"Workspace folder limit reached","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"limit":25}}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / FIELD_NOT_FOUND. Access-denied is intentionally hidden as 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_NOT_FOUND","message":"Folder 000000000000000000000000 not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"FOLDER","folderId":"000000000000000000000000"}}}}}},"409":{"description":"FOLDER_CONFLICT (optimistic concurrency) / FIELD_PROTECTED / FIELD_NAME_EXISTS_IN_FOLDER.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_CONFLICT","message":"Folder was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Folders"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/fields/{fieldId}":{"patch":{"operationId":"FieldControllerV2_update","summary":"Update a folder field","description":"Updates `name`, `description`, `required`, `presets` or `groupName`. Returns the full updated folder. Attempts to change `type` / `valueType` / `options` are rejected by service-schema with HTTP 400. Protected fields reject any update with HTTP 409 `FIELD_PROTECTED`: the built-in `name` field (every folder), and the role-bound fields of the Tasks folder (assignee, due date, completed, priority, creator, record link, description) — they drive reminders, calendar and notifications.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"fieldId","required":true,"in":"path","schema":{"example":"8","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateField"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Folder"}}}},"400":{"description":"INVALID_REQUEST / INVALID_FIELD_TYPE / INVALID_FIELD_DEFINITION / INVALID_FIELD_OPTIONS / LABEL_RESERVED / IMMUTABLE_PROPERTY / DUPLICATE_PRESET / TOO_MANY_PRESETS / TOO_MANY_FIELDS.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"DUPLICATE_PRESET","message":"Duplicate preset value in field \"1\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"fieldId":"1","value":"Tech"}}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"402":{"description":"FOLDER_LIMIT_EXCEEDED — workspace plan paywall.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_LIMIT_EXCEEDED","message":"Workspace folder limit reached","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"limit":25}}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / FIELD_NOT_FOUND. Access-denied is intentionally hidden as 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_NOT_FOUND","message":"Folder 000000000000000000000000 not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"FOLDER","folderId":"000000000000000000000000"}}}}}},"409":{"description":"FOLDER_CONFLICT (optimistic concurrency) / FIELD_PROTECTED / FIELD_NAME_EXISTS_IN_FOLDER.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_CONFLICT","message":"Folder was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Folders"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records":{"post":{"operationId":"RecordListControllerV2_create","summary":"Create a record","description":"Creates a record in the folder. `fields` is keyed by field id (default) or label (`?fieldKeys=label`). Read-only fields (formulas, lookups, statistics, autoIncrement) are rejected; required-field and uniqueness rules are enforced downstream.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRecord"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Record"}}}},"400":{"description":"INVALID_REQUEST / IMMUTABLE_PROPERTY — a field value has the wrong type, references an unknown field, uses an invalid record id, or targets a read-only field (formula / lookup / statistics / autoIncrement).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Field \"Amount\" expects a number","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / RECORD_NOT_FOUND. A record addressed under a folder it does not belong to is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record \"5f8a2b1c3d4e5f6a7b8c9d0f\" not found in folder \"5f8a2b1c3d4e5f6a7b8c9d0e\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"409":{"description":"RECORD_CONFLICT — the record was modified by another request (optimistic concurrency) on update / delete.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_CONFLICT","message":"Record was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Records"],"security":[{"BearerAuth":[]}]},"get":{"operationId":"RecordListControllerV2_listRecords","summary":"List updated records","description":"Lists records created or updated since a point in time, in ascending `updatedAt` order (oldest first). Designed for incremental polling and sync.\n\n### Polling\n\n1. Call without `since` (defaults to 24 hours ago) or with your timestamp checkpoint. The response carries `total` and, while more records remain, `nextSince`.\n2. While `nextSince` is present, call again with `since=<nextSince>` to read the rest of the window.\n3. Remember the `updatedAt` of the last record as the checkpoint for the next poll.\n\nThe `since` bound is inclusive (`updatedAt >= since`), so the checkpoint record comes back on the next poll — deduplicate by `id` + `updatedAt`. A record updated while you are paging moves to the end of the window and is picked up by the same sweep or the next poll; nothing is lost. Treat `nextSince` as opaque and pass it back unchanged; a malformed or tampered token returns `400 INVALID_REQUEST` — restart from a timestamp.\n\nNew records are included: a created record starts with `updatedAt` equal to `createdAt`. Records the caller cannot see are excluded and fields hidden by field-level permissions are omitted.\n\nFor arbitrary field conditions and custom sorting use `POST .../records/filter`; for full-text search use `GET .../records/search`.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}},{"name":"since","required":false,"in":"query","description":"Read position: ISO 8601 date-time, epoch-milliseconds, or the `nextSince` token from the previous response. Defaults to 24 hours ago.","schema":{"example":"2026-06-11T10:00:00Z","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size. When more records remain, the response carries `nextSince` — pass it back in `since` to continue.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordListResult"}}}},"400":{"description":"INVALID_REQUEST / IMMUTABLE_PROPERTY — a field value has the wrong type, references an unknown field, uses an invalid record id, or targets a read-only field (formula / lookup / statistics / autoIncrement).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Field \"Amount\" expects a number","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / RECORD_NOT_FOUND. A record addressed under a folder it does not belong to is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record \"5f8a2b1c3d4e5f6a7b8c9d0f\" not found in folder \"5f8a2b1c3d4e5f6a7b8c9d0e\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"409":{"description":"RECORD_CONFLICT — the record was modified by another request (optimistic concurrency) on update / delete.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_CONFLICT","message":"Record was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Records"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records/search":{"get":{"operationId":"RecordListControllerV2_searchRecords","summary":"Search records by text","description":"Full-text search within the folder (Elasticsearch, relevance-ranked). Returns up to `limit` records with friendly field values; fields hidden by field-level permissions are omitted. No pagination — raise `limit`, or use `POST .../records/filter` for structured queries with sorting and cursor pagination. Use `?fieldKeys=label` to key `fields` by label instead of id.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}},{"name":"text","required":true,"in":"query","description":"Full-text search query. Matched against record field values.","schema":{"example":"acme corp","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of records to return (relevance-ranked).","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordSearchResult"}}}},"400":{"description":"INVALID_REQUEST / IMMUTABLE_PROPERTY — a field value has the wrong type, references an unknown field, uses an invalid record id, or targets a read-only field (formula / lookup / statistics / autoIncrement).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Field \"Amount\" expects a number","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / RECORD_NOT_FOUND. A record addressed under a folder it does not belong to is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record \"5f8a2b1c3d4e5f6a7b8c9d0f\" not found in folder \"5f8a2b1c3d4e5f6a7b8c9d0e\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"409":{"description":"RECORD_CONFLICT — the record was modified by another request (optimistic concurrency) on update / delete.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_CONFLICT","message":"Record was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Records"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records/filter":{"post":{"operationId":"RecordListControllerV2_filterRecords","summary":"Filter records","description":"Queries records in the folder with a structured filter and returns up to `limit` matches plus the `total` match count.\n\n### Filter structure\n\nA filter is a JSON object of field conditions. Keys are field ids (or field labels with `?fieldKeys=label`); a value is either a bare value — shorthand for equality — or an object of operators:\n\n```json\n{ \"name\": \"Acme Corp\" }\n{ \"3\": { \"_gte\": 100, \"_lte\": 500 } }\n```\n\nSeveral keys in one object combine with AND. Explicit grouping uses `_and` / `_or`, each holding a non-empty array of nested filter objects. A group must be the only key of its object (put sibling conditions inside the group); groups nest up to 5 levels:\n\n```json\n{ \"_or\": [ { \"2\": \"Active\" }, { \"2\": \"Paused\" } ] }\n```\n\n### Operators\n\n| Operator | Meaning |\n|---|---|\n| `_eq` / `_ne` | equals / not equals (a bare value means `_eq`) |\n| `_in` / `_nin` | contains / does not contain — substring for a string value, exact set membership for an array value |\n| `_exists` / `_nexists` | field has a value / is empty (the operator value is ignored — pass `true`) |\n| `_lt`, `_lte`, `_gt`, `_gte` | ordered comparison (numbers, dates, date-times) |\n| `_any` | equals any of the listed values (text fields, array value) |\n\n### Support by field type\n\n| Field type | Operators and values |\n|---|---|\n| Text (single / multi line, dropdown, multi-select) | `_eq`, `_ne` exact match (case-insensitive); `_in` / `_nin` — substring (string) or set membership (array); `_any` (array); `_exists` / `_nexists` |\n| Number | `_eq`, `_ne`, `_lt`, `_lte`, `_gt`, `_gte`, `_exists`, `_nexists` with a numeric value (numeric strings accepted; a non-numeric string matches nothing) |\n| Checkbox | `_eq` / `_ne` with `true` / `false`; `_exists` means ticked, `_nexists` not ticked |\n| Date, Date & time | all comparison operators; value is an epoch-ms number, a calendar date `\"YYYY-MM-DD\"`, or a date expression (below). A string value targets the whole day / period: `_eq` matches within it, `_lt` before its start, `_gt` after its end, `_lte` / `_gte` include it |\n| User | `_eq` / `_ne` by email — the special value `\"me\"` is the calling user; `_in` / `_nin` email substring; `_exists` / `_nexists` |\n| Phone number | `_eq` matches the number ignoring formatting; `_in` matches a digit fragment; `_exists` / `_nexists` |\n| Website / domain | `_eq` matches the domain including subdomains (`acme.com` also matches `shop.acme.com`); `_in` substring over the stored value; `_exists` / `_nexists` |\n| Linked record | `_eq` / `_in` — linked to the given record id (any of the given ids for an array); `_ne` / `_nin` — not linked to it/them; `_exists` / `_nexists`. Values are record ids; an invalid id matches nothing |\n| Formula (`numberFormula`, `textFormula`) | by the computed value — number or text semantics as above |\n| Lookup | number- and date-valued lookups filter like Number / Date; a text-valued lookup is not filterable |\n| Statistics fields | count fields like Number, time fields like Date & time; `timeSinceUpdate` takes the elapsed amount in its own unit (days / hours), `fieldUpdateTime` a date-time |\n\n### Sorting\n\n`sort` is an array of up to 3 single-key objects `{ \"<fieldIdOrLabel>\": 1 | -1 }` (1 = ascending, -1 = descending), applied in array order:\n\n```json\n{ \"sort\": [ { \"3\": -1 }, { \"name\": 1 } ] }\n```\n\nKeys are field ids (or labels with `?fieldKeys=label`) plus the special fields below. When `sort` is omitted, records are sorted by `created` ascending. The response echoes the effective sort. Semantics: text sorts case-insensitively; empty values always go last; multi-value fields sort by their smallest (ascending) or largest (descending) element; a dropdown restricted to presets sorts by the preset order; ties are broken by an internal id, so the order is fully deterministic. Fields linking to multiple records are not sortable.\n\n### Pagination\n\nThe response carries `nextCursor` while more pages remain. Pass it back in `cursor` together with the SAME `filter`, `sort` and `timeZone` to fetch the next page; `limit` may change between pages. The last page has no `nextCursor`. The cursor is bound to `filter`, `sort` and `timeZone`: send it with a different one (or a malformed cursor) and the request fails with `400 INVALID_REQUEST` — restart from the first page without `cursor`. Cursors do not expire. `total` is returned only on the first page (request without `cursor`) — cache it client-side.\n\n### Date expressions\n\n`\"today\"`, `\"yesterday\"`, `\"tomorrow\"`, `\"now\"`, or `[last|this|next] [N] day|week|month|quarter|year` — e.g. `\"last 7 days\"`, `\"this month\"`, `\"next 2 weeks\"` (no direction defaults to `this`). Resolved in the `timeZone` query param for date-time fields and `created` / `updated`; date-only fields always resolve in UTC.\n\n### Special fields\n\nAvailable in every folder regardless of the schema:\n\n- `name` — the record name (text field);\n- `created`, `updated` — record timestamps (date-time operators and expressions);\n- `active_tasks_count` — number of active tasks (number operators; `_exists` means \"has active tasks\").\n\n### Behaviour\n\nAn unknown field, an unsupported operator, or a malformed filter/sort returns `400 INVALID_REQUEST`. Records the caller cannot see are excluded and fields hidden by field-level permissions are omitted. Omitting `filter` matches all records — combined with the default sort and the cursor this is the way to export a whole folder page by page (`limit` ≤ 1000 per page). For full-text search across all fields use `GET /v2/folders/{folderId}/records/search` — this endpoint filters by specific fields only.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size. Use `nextCursor` from the response to fetch subsequent pages.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"timeZone","required":false,"in":"query","description":"IANA time zone used to resolve date expressions (\"today\", \"last week\") and calendar dates (\"2026-06-10\") on date-time fields and on `created` / `updated`. Date-only fields always resolve in UTC.","schema":{"default":"UTC","example":"Europe/Kyiv","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterRecords"},"examples":{"equality":{"summary":"Bare values (AND)","value":{"filter":{"2":"Active","name":"Acme Corp"}}},"operators":{"summary":"Operators and a date expression","value":{"filter":{"3":{"_gte":100000},"updated":{"_gte":"last 30 days"}}}},"orGroup":{"summary":"OR group with a nested AND","value":{"filter":{"_or":[{"2":"Paused"},{"_and":[{"2":"Active"},{"4":{"_nexists":true}}]}]}}},"sorted":{"summary":"Filter with a two-field sort","value":{"filter":{"2":"Active"},"sort":[{"3":-1},{"name":1}]}},"nextPage":{"summary":"Next page (same filter & sort + cursor)","value":{"filter":{"2":"Active"},"sort":[{"3":-1},{"name":1}],"cursor":"<nextCursor from the previous page>"}},"byLabel":{"summary":"Labels as keys (?fieldKeys=label)","value":{"filter":{"Status":"Active","Manager":{"_eq":"me"}}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordFilterResult"}}}},"400":{"description":"INVALID_REQUEST / IMMUTABLE_PROPERTY — a field value has the wrong type, references an unknown field, uses an invalid record id, or targets a read-only field (formula / lookup / statistics / autoIncrement).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Field \"Amount\" expects a number","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / RECORD_NOT_FOUND. A record addressed under a folder it does not belong to is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record \"5f8a2b1c3d4e5f6a7b8c9d0f\" not found in folder \"5f8a2b1c3d4e5f6a7b8c9d0e\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"409":{"description":"RECORD_CONFLICT — the record was modified by another request (optimistic concurrency) on update / delete.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_CONFLICT","message":"Record was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Records"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records/{recordId}":{"get":{"operationId":"RecordControllerV2_get","summary":"Get a record","description":"Returns the record with friendly field values. Use `?fieldKeys=label` to key `fields` by field label instead of id.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"recordId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0f","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Record"}}}},"400":{"description":"INVALID_REQUEST / IMMUTABLE_PROPERTY — a field value has the wrong type, references an unknown field, uses an invalid record id, or targets a read-only field (formula / lookup / statistics / autoIncrement).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Field \"Amount\" expects a number","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / RECORD_NOT_FOUND. A record addressed under a folder it does not belong to is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record \"5f8a2b1c3d4e5f6a7b8c9d0f\" not found in folder \"5f8a2b1c3d4e5f6a7b8c9d0e\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"409":{"description":"RECORD_CONFLICT — the record was modified by another request (optimistic concurrency) on update / delete.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_CONFLICT","message":"Record was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Records"],"security":[{"BearerAuth":[]}]},"patch":{"operationId":"RecordControllerV2_update","summary":"Update a record","description":"Partially updates the record: only keys present in `fields` change, `null` clears a field, omitted keys are untouched. Read-only fields are rejected.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"recordId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0f","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRecord"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Record"}}}},"400":{"description":"INVALID_REQUEST / IMMUTABLE_PROPERTY — a field value has the wrong type, references an unknown field, uses an invalid record id, or targets a read-only field (formula / lookup / statistics / autoIncrement).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Field \"Amount\" expects a number","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / RECORD_NOT_FOUND. A record addressed under a folder it does not belong to is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record \"5f8a2b1c3d4e5f6a7b8c9d0f\" not found in folder \"5f8a2b1c3d4e5f6a7b8c9d0e\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"409":{"description":"RECORD_CONFLICT — the record was modified by another request (optimistic concurrency) on update / delete.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_CONFLICT","message":"Record was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Records"],"security":[{"BearerAuth":[]}]},"delete":{"operationId":"RecordControllerV2_remove","summary":"Delete a record","description":"Moves the record to trash (recoverable). Returns 204 No Content.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"recordId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0f","type":"string"}}],"responses":{"204":{"description":""},"400":{"description":"INVALID_REQUEST / IMMUTABLE_PROPERTY — a field value has the wrong type, references an unknown field, uses an invalid record id, or targets a read-only field (formula / lookup / statistics / autoIncrement).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Field \"Amount\" expects a number","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND / RECORD_NOT_FOUND. A record addressed under a folder it does not belong to is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record \"5f8a2b1c3d4e5f6a7b8c9d0f\" not found in folder \"5f8a2b1c3d4e5f6a7b8c9d0e\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"409":{"description":"RECORD_CONFLICT — the record was modified by another request (optimistic concurrency) on update / delete.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_CONFLICT","message":"Record was modified by another request; retry","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Records"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records/{recordId}/timeline":{"get":{"operationId":"RecordTimelineControllerV2_list","summary":"List record timeline","description":"Returns the record activity timeline — emails, calls, comments, chats, calendar events, files and field changes — newest first. Cursor-paginated.\nCATEGORIES (`types`, comma-separated; omit for all): `comment`, `email`, `campaign_email`, `call`, `chat`, `calendar`, `file`, `record_created`, `record_updated`. Each event has `id`, `recordId` (the record the event belongs to), `type`, `time` (epoch ms) + `timeISO`, `pinned`, `creator` ({name,email}), plus category-specific fields (e.g. email: `subject`/`snippet`; campaign_email: `campaignName`/`emailTo`; call/chat: the call/chat object; `record_created`: a `fields` value map; `record_updated`: a `changes` map of {old,new}, a side omitted when empty).\nTIME RANGE: `from` / `to` accept an ISO 8601 date-time or epoch-ms; both inclusive. A date without a time means midnight UTC at the start of that day, so to include all of 30 June pass `to=2025-06-30T23:59:59.999Z`.\nPAGINATION: while `nextCursor` is returned, pass it back as `cursor` (with the SAME filters) for the next page — a stable keyset position, no duplicates across pages. The last page has no `nextCursor`.\nUse `includeEmailStats=true` to add open/click stats to email events (extra cost), and the content endpoint to fetch full message bodies / transcripts.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"recordId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0f","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}},{"name":"from","required":false,"in":"query","description":"Start of the time range (inclusive): an ISO 8601 date-time or an epoch-milliseconds number. Omit for no lower bound.","schema":{"example":"2025-01-01T00:00:00Z","type":"string"}},{"name":"to","required":false,"in":"query","description":"End of the time range (inclusive): an ISO 8601 date-time or an epoch-milliseconds number. A date without a time, such as `2025-06-30`, means midnight UTC at the start of that day — to include the whole day pass `2025-06-30T23:59:59.999Z`. Omit for no upper bound.","schema":{"example":"2025-06-30T23:59:59.999Z","type":"string"}},{"name":"types","required":false,"in":"query","description":"Comma-separated event categories to include: comment, email, campaign_email, call, chat, calendar, file, record_created, record_updated. Omit to include all.","schema":{"example":"email,call","type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque pagination token from the previous response's `nextCursor`. Pass it back with the SAME filters to get the next page.","schema":{"maxLength":4096,"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of events per page (1–1000, default 100). The response carries `nextCursor` while more events remain; a page may hold fewer events than `limit`.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"includeEmailStats","required":false,"in":"query","description":"Include open/click tracking stats (`emailOpenStats` / `linkClickStats`) on email events, and aggregate `stats` on campaign events. Off by default: computing them adds response latency that grows with the number of email/campaign events on the page — enable it only when you need the counts.","schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"description":"The requested page of timeline events, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordTimelinePage"}}}},"400":{"description":"INVALID_REQUEST — malformed request input: `from` / `to` is not an ISO 8601 date-time or epoch-milliseconds value, `types` contains an unknown category, `limit` is out of range, or an id in the path is not a 24-character hex string.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"\"from\" must be an ISO 8601 date-time or an epoch-milliseconds number — got \"yesterday\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"RECORD_NOT_FOUND — the folder or record does not exist or is not visible to the caller. A record you cannot read, or one addressed under a folder it does not belong to, is hidden as RECORD_NOT_FOUND; an unknown `timelineId` on the content endpoint returns the same 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record timeline not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Timeline"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records/{recordId}/timeline/{timelineId}/content":{"get":{"operationId":"RecordTimelineControllerV2_getContent","summary":"Get timeline item content","description":"Full content of one timeline item, addressed by its `id` from the list. For an email or campaign-email item it returns the message bodies (from/to/cc, subject, full HTML body, attachments); for a messenger item, the chat transcript (sender, body, attachments, timestamps). `hasMore` (email only) signals more messages exist beyond the returned window. Other event types are already complete in the list and resolve to an empty `messages` array.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"recordId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0f","type":"string"}},{"name":"timelineId","required":true,"in":"path","description":"The timeline item's `id` from the list.","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d10","type":"string"}}],"responses":{"200":{"description":"The timeline item's full content — message bodies or a chat transcript; an empty `messages` array for other event types.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimelineMessages"}}}},"400":{"description":"INVALID_REQUEST — malformed request input: `from` / `to` is not an ISO 8601 date-time or epoch-milliseconds value, `types` contains an unknown category, `limit` is out of range, or an id in the path is not a 24-character hex string.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"\"from\" must be an ISO 8601 date-time or an epoch-milliseconds number — got \"yesterday\"","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"RECORD_NOT_FOUND — the folder or record does not exist or is not visible to the caller. A record you cannot read, or one addressed under a folder it does not belong to, is hidden as RECORD_NOT_FOUND; an unknown `timelineId` on the content endpoint returns the same 404.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record timeline not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Timeline"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records/{recordId}/timeline/comments":{"post":{"operationId":"RecordActivityControllerV2_createComment","summary":"Add a comment","description":"Adds a comment to the record timeline and returns it as the `comment` timeline event (same shape and `id` as in the timeline list).\nMENTIONS: to mention a workspace member, put their email right after an `@` at a word start in `text` (e.g. `@jane.doe@acme.com`). Recognized members are returned in `mentions` and receive a notification; any other `@…` token stays plain text. The mention token runs to the next whitespace, so the email must be followed by a space or the end of the text — punctuation attached directly to it (e.g. `@jane@acme.com,`) prevents the match. Matching is case-insensitive; each member is mentioned and notified at most once.\nTo list the record comments use `GET .../records/{recordId}/timeline?types=comment`.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"recordId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0f","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComment"}}}},"responses":{"201":{"description":"The created `comment` timeline event — same shape and `id` as items returned by the timeline list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentTimelineItem"}}}},"400":{"description":"INVALID_REQUEST — malformed body: `text` missing, empty after trimming, or longer than 65000 characters; for a call log also `time` not an ISO 8601 date-time or epoch-milliseconds value, or `duration` negative or not an integer. An id in the path that is not a 24-character hex string is rejected the same way.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"text should not be empty","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — the record is visible to the caller but they lack permission to add timeline items in this folder.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"No permission to add items to the record timeline","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"RECORD_NOT_FOUND — the folder or record does not exist or is not visible to the caller. A record you cannot read, or one addressed under a folder it does not belong to, is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Timeline"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/records/{recordId}/timeline/calls":{"post":{"operationId":"RecordActivityControllerV2_createCall","summary":"Log a call","description":"Logs a call on the record timeline and returns it as the `call` timeline event (same shape and `id` as in the timeline list). This endpoint records a call made or received outside NetHunt — calls made through telephony integrations appear on the timeline automatically.\n`time` is when the call happened (ISO 8601 or epoch-ms, as a string or a bare JSON number; defaults to the creation moment); `duration` is in whole milliseconds.\nMENTIONS: to mention a workspace member, put their email right after an `@` at a word start in `text` (e.g. `@jane.doe@acme.com`). Recognized members are returned in `mentions` and receive a notification; any other `@…` token stays plain text. The mention token runs to the next whitespace, so the email must be followed by a space or the end of the text — punctuation attached directly to it (e.g. `@jane@acme.com,`) prevents the match. Matching is case-insensitive; each member is mentioned and notified at most once.\nTo list the record calls use `GET .../records/{recordId}/timeline?types=call`.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"recordId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0f","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCallLog"}}}},"responses":{"201":{"description":"The created `call` timeline event — same shape and `id` as items returned by the timeline list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallTimelineItem"}}}},"400":{"description":"INVALID_REQUEST — malformed body: `text` missing, empty after trimming, or longer than 65000 characters; for a call log also `time` not an ISO 8601 date-time or epoch-milliseconds value, or `duration` negative or not an integer. An id in the path that is not a 24-character hex string is rejected the same way.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"text should not be empty","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — the record is visible to the caller but they lack permission to add timeline items in this folder.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"No permission to add items to the record timeline","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"RECORD_NOT_FOUND — the folder or record does not exist or is not visible to the caller. A record you cannot read, or one addressed under a folder it does not belong to, is hidden as RECORD_NOT_FOUND.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RECORD_NOT_FOUND","message":"Record not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"RECORD"}}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Timeline"],"security":[{"BearerAuth":[]}]}},"/api/v2/folders/{folderId}/timeline":{"get":{"operationId":"FolderTimelineControllerV2_list","summary":"List folder timeline","description":"Returns the activity timeline of every record in the folder as one feed — emails, calls, comments, chats, calendar events, files and field changes — newest first. Cursor-paginated. Built for backfilling the history of a whole folder without one request per record; for a backfill use `limit=1000` (the maximum; the default is 100).\nItems have the same shapes as on the record timeline; `recordId` is the record the event belongs to. Only events of records the caller can read are returned; events of deleted records are not.\nCATEGORIES (`types`, comma-separated; omit for all): `comment`, `email`, `campaign_email`, `call`, `chat`, `calendar`, `file`, `record_created`, `record_updated`.\nTIME RANGE: `from` / `to` accept an ISO 8601 date-time or epoch-ms; both inclusive. A date without a time means midnight UTC at the start of that day, so to include all of 30 June pass `to=2025-06-30T23:59:59.999Z`. Events are ordered and filtered by their own time (an email is placed at the email's date, even if it was linked to the record later), not by when they were added. Because of this, `from` does not detect new activity: an event added later with an earlier time (an older email linked to a record, an import) will not appear in a `from`-bounded request made after it. Use the feed to backfill history, not to poll for changes. Chats and calls move to the time of their latest activity, so one that gets new activity during a backfill can be missed: when the backfill ends, re-read the range from its start time to now, and store items by `id` (upsert) so re-reads never duplicate.\nPAGINATION: while `nextCursor` is returned, pass it back as `cursor` (with the SAME filters) for the next page — a stable position, no duplicates across pages. Pages are normally full, but a page can hold fewer events than `limit`, or none at all, and still return `nextCursor` — never infer the end from the number of events: keep paging until `nextCursor` is absent. Pass `nextCursor` back unchanged: a `cursor` that is not a valid token is rejected with 400 `INVALID_REQUEST` (it never silently restarts from the newest event).\nUse `includeEmailStats=true` to add open/click stats to email events (extra cost), and the record content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content` to fetch full message bodies / transcripts.","parameters":[{"name":"folderId","required":true,"in":"path","schema":{"example":"5f8a2b1c3d4e5f6a7b8c9d0e","type":"string"}},{"name":"fieldKeys","required":false,"in":"query","description":"Key `fields` by field id (default, stable) or by field label. Applies to both request and response.","schema":{"enum":["id","label"],"type":"string"}},{"name":"from","required":false,"in":"query","description":"Start of the time range (inclusive): an ISO 8601 date-time or an epoch-milliseconds number. Omit for no lower bound.","schema":{"example":"2025-01-01T00:00:00Z","type":"string"}},{"name":"to","required":false,"in":"query","description":"End of the time range (inclusive): an ISO 8601 date-time or an epoch-milliseconds number. A date without a time, such as `2025-06-30`, means midnight UTC at the start of that day — to include the whole day pass `2025-06-30T23:59:59.999Z`. Omit for no upper bound.","schema":{"example":"2025-06-30T23:59:59.999Z","type":"string"}},{"name":"types","required":false,"in":"query","description":"Comma-separated event categories to include: comment, email, campaign_email, call, chat, calendar, file, record_created, record_updated. Omit to include all.","schema":{"example":"email,call","type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque pagination token from the previous response's `nextCursor`. Pass it back unchanged with the SAME filters; an invalid token is rejected with 400.","schema":{"maxLength":4096,"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of events per page (1–1000, default 100). The response carries `nextCursor` while more events remain; a page may hold fewer events than `limit`.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"includeEmailStats","required":false,"in":"query","description":"Include open/click tracking stats (`emailOpenStats` / `linkClickStats`) on email events, and aggregate `stats` on campaign events. Off by default: computing them adds response latency that grows with the number of email/campaign events on the page — enable it only when you need the counts.","schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"description":"The requested page of folder timeline events, newest first. Usually full; can be shorter than `limit`, or empty, while `nextCursor` is present — keep paging until it is absent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderTimelinePage"}}}},"400":{"description":"INVALID_REQUEST — malformed request input: `from` / `to` is not an ISO 8601 date-time or epoch-milliseconds value, `types` contains an unknown category, `limit` is out of range, `cursor` is not a token returned as `nextCursor`, or the folder id is not a 24-character hex string.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"INVALID_REQUEST","message":"Unknown timeline type \"note\". Valid types: comment, email, campaign_email, call, chat, calendar, file, record_created, record_updated.","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"401":{"description":"UNAUTHORIZED — missing or invalid bearer token.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UNAUTHORIZED","message":"Missing or invalid bearer token","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"403":{"description":"FORBIDDEN — caller authenticated but lacks permission for the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FORBIDDEN","message":"Caller lacks permission for the resource","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"404":{"description":"FOLDER_NOT_FOUND — the folder does not exist or the caller cannot read it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"FOLDER_NOT_FOUND","message":"Folder not found","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"kind":"FOLDER"}}}}}},"429":{"description":"RATE_LIMIT_EXCEEDED.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"RATE_LIMIT_EXCEEDED","message":"Too many requests","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","details":{"retryAfterSeconds":30}}}}}},"502":{"description":"UPSTREAM_ERROR — downstream service returned an unmapped failure.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"UPSTREAM_ERROR","message":"Downstream service returned an unmapped failure","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}},"503":{"description":"SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"example":{"error":"SERVICE_UNAVAILABLE","message":"Service temporarily unavailable","traceId":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f"}}}}}},"tags":["Timeline"],"security":[{"BearerAuth":[]}]}}},"info":{"title":"NetHunt Public REST API","description":"Public REST API for NetHunt CRM","version":"0.0.1","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"BearerAuth":{"scheme":"bearer","bearerFormat":"nh_live_*","type":"http"}},"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","enum":["BAD_REQUEST","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","CONFLICT","VALIDATION_ERROR","INTERNAL_ERROR","SERVICE_UNAVAILABLE","PUBLIC_API_DISABLED","RATE_LIMIT_EXCEEDED","GLOBAL_RATE_LIMIT_EXCEEDED","CONCURRENCY_LIMIT_EXCEEDED","WORKSPACE_MISMATCH","INVALID_REQUEST","INVALID_FIELD_TYPE","INVALID_FIELD_DEFINITION","INVALID_FIELD_OPTIONS","LABEL_RESERVED","IMMUTABLE_PROPERTY","DUPLICATE_PRESET","TOO_MANY_PRESETS","TOO_MANY_FIELDS","FOLDER_LIMIT_EXCEEDED","FOLDER_NOT_FOUND","FIELD_NOT_FOUND","FOLDER_CONFLICT","FIELD_PROTECTED","FIELD_NAME_EXISTS_IN_FOLDER","RECORD_NOT_FOUND","RECORD_CONFLICT","UPSTREAM_ERROR"],"example":"DUPLICATE_PRESET","description":"Stable machine-readable error code. Use this for retry / branching logic, not `message`."},"message":{"type":"string","example":"Duplicate preset value in field \"1\"","description":"Human-readable explanation. May change between releases."},"traceId":{"type":"string","example":"b7a9e3c4-1d2e-4f5a-9c8b-0a1b2c3d4e5f","description":"Trace identifier; include in support requests."},"details":{"type":"object","additionalProperties":true,"example":{"fieldId":"1","value":"Tech"},"description":"Optional structured context. Shape depends on `error`; see endpoint-specific notes."}},"required":["error","message","traceId"]},"EmptyOptions":{"type":"object","properties":{}},"DigitsOptions":{"type":"object","properties":{"min":{"type":"number","example":0,"description":"Minimum fraction digits."},"max":{"type":"number","example":2,"description":"Maximum fraction digits."}}},"BasicNumberFormat":{"type":"object","properties":{"useGrouping":{"type":"boolean","example":true},"digits":{"$ref":"#/components/schemas/DigitsOptions"}}},"NumberOptions":{"type":"object","properties":{"format":{"$ref":"#/components/schemas/BasicNumberFormat"}}},"PercentOptions":{"type":"object","properties":{"format":{"$ref":"#/components/schemas/BasicNumberFormat"}}},"CurrencyFormat":{"type":"object","properties":{"useGrouping":{"type":"boolean","example":true},"digits":{"$ref":"#/components/schemas/DigitsOptions"},"currency":{"type":"string","example":"USD","description":"ISO 4217 currency code."}},"required":["currency"]},"CurrencyOptions":{"type":"object","properties":{"format":{"$ref":"#/components/schemas/CurrencyFormat"}},"required":["format"]},"AutoIncrementFormat":{"type":"object","properties":{"prefix":{"type":"string","example":"INV-","description":"Prefix prepended to the rendered number."},"suffix":{"type":"string","example":"-2026","description":"Suffix appended to the rendered number."}}},"AutoIncrementOptions":{"type":"object","properties":{"format":{"$ref":"#/components/schemas/AutoIncrementFormat"}}},"FreeNumberFormat":{"type":"object","properties":{"style":{"type":"string","enum":["decimal","percent","currency"]},"currency":{"type":"string","example":"USD"},"useGrouping":{"type":"boolean","example":true},"digits":{"$ref":"#/components/schemas/DigitsOptions"},"prefix":{"type":"string","example":"INV-"},"suffix":{"type":"string","example":"-2026"}}},"NumberFormulaOptions":{"type":"object","properties":{"formula":{"type":"string","example":"=field(\"Quantity\") * field(\"Price\")","description":"Tweakflow expression; must begin with `=`. Reference other fields by their label via `field(\"Label\")` (with optional default: `field(\"Label\", 0)`); curly-brace `{Label}` interpolation is NOT supported. Stdlib modules available: `_math`, `_strings`, `_data`, `_time`. Computed automatically on record save and read-only (cannot be set on a record). An invalid formula is accepted at field-create but silently produces an empty column (no compute), so the syntax must be correct."},"format":{"$ref":"#/components/schemas/FreeNumberFormat"}},"required":["formula"]},"TextFormulaOptions":{"type":"object","properties":{"formula":{"type":"string","example":"=field(\"First Name\") .. \" \" .. field(\"Last Name\")","description":"Tweakflow expression; must begin with `=`. Reference other fields by their label via `field(\"Label\")` (with optional default: `field(\"Label\", 0)`); curly-brace `{Label}` interpolation is NOT supported. String concatenation uses the `..` operator (there is no `CONCAT()` function). Stdlib modules available: `_math`, `_strings`, `_data`, `_time`. Computed automatically on record save and read-only (cannot be set on a record). An invalid formula is accepted at field-create but silently produces an empty column (no compute), so the syntax must be correct."}},"required":["formula"]},"FolderLinkOptions":{"type":"object","properties":{"linkedFolderId":{"type":"string","example":"62b0a7c92d9387028a36f2f3","description":"ObjectId of the linked folder. Immutable after create."}},"required":["linkedFolderId"]},"FieldSelectionAll":{"type":"object","properties":{"all":{"type":"number","enum":[true],"description":"Selects every link path. `fieldIds` must be omitted."}},"required":["all"]},"FieldSelectionExplicit":{"type":"object","properties":{"all":{"type":"number","enum":[false],"description":"Explicit selection — `fieldIds` is required and non-empty."},"fieldIds":{"type":"array","example":["1","3"],"items":{"type":"string"},"minItems":1,"description":"Field ids — at least one. To skip a link path entirely, omit the parent key instead of sending an empty list."}},"required":["all","fieldIds"]},"LookupRollup":{"type":"object","properties":{"type":{"type":"string","enum":["SUM","AVG","MIN","MAX","RECORD_COUNT"],"description":"Aggregation type. `SUM`/`AVG`/`MIN`/`MAX` aggregate a field of the related folder; `RECORD_COUNT` counts the linked records. For a date source field only `MIN` and `MAX` are supported."},"fieldId":{"type":"string","example":"5","description":"Field id in the related folder being aggregated — required for `SUM`/`AVG`/`MIN`/`MAX`, omitted for `RECORD_COUNT`. Must reference a number field (`number`, `percent`, `currency`, `autoIncrement`), a number-valued `lookup` or a `numberFormula` field; `MIN`/`MAX` also accept a `date` field or a date-valued `lookup`. Text formula and statistic fields cannot be aggregated. Chains of nested lookups (a lookup aggregating another lookup) support a maximum depth of 3 and must not form cycles."}},"required":["type"]},"LookupOptions":{"type":"object","properties":{"relatedFolderId":{"type":"string","example":"62b0a7c92d9387028a36f2f3","description":"ObjectId of the related folder. Immutable after create."},"directLinks":{"oneOf":[{"$ref":"#/components/schemas/FieldSelectionAll"},{"$ref":"#/components/schemas/FieldSelectionExplicit"}],"description":"Folder-link fields of THIS folder pointing at the related folder. At least one of `directLinks` / `backLinks` is required; omit the key (do not send an empty selection) when the path is not used. Immutable after create."},"backLinks":{"oneOf":[{"$ref":"#/components/schemas/FieldSelectionAll"},{"$ref":"#/components/schemas/FieldSelectionExplicit"}],"description":"Link paths from the related folder back to this one (folder-link fields of the related folder). At least one of `directLinks` / `backLinks` is required; omit the key (do not send an empty selection) when the path is not used. Immutable after create."},"filterStr":{"type":"string","example":"{\"2\":\"Active\",\"5\":{\"_gte\":100}}","description":"Optional JSON-encoded filter that restricts which linked records feed the rollup — only related records matching it are aggregated. The decoded value is a filter object in the SAME language as the `POST .../records/filter` request body: field conditions combinable with `_and` / `_or` groups, using the `_eq` / `_ne` / `_in` / `_nin` / `_lt` / `_lte` / `_gt` / `_gte` / `_exists` / `_nexists` operators (see that operation for the full reference). Keys are field ids of the RELATED folder (labels are never resolved here) and must reference value-carrying fields: text-like, number-like, checkbox, date / dateTime, user, formula fields (`numberFormula`, `textFormula`) or number/date-valued `lookup` fields, plus `name` (the built-in record name, filtered as text). NOT filterable here: linked-record fields (`folderLink`, single or multi) and conditions over records of another folder (`rel/<folderId>` keys) — a lookup filter selects related records by their OWN field values only — plus statistic fields, STRING-valued lookups and the system fields (`created`, `updated`, `active_tasks_count`). Omit to aggregate every linked record. The string is validated on save: invalid JSON, an unknown or unsupported operator (`_substr`, `_nsubstr`, `_primary`), a non-scalar comparison value or a malformed condition is rejected with `INVALID_FIELD_OPTIONS` instead of persisting a filter that could never be evaluated. Send an empty string to drop an existing filter.\n\nWhich operators each field type evaluates (a combination outside this list is accepted by the grammar check but leaves the lookup value empty):\n- text-like (text, `name`, dropdown, tag, email, phone, website, address, country, socials, `textFormula`): `_eq`, `_ne` (exact, case-insensitive); `_in` / `_nin` — \"contains\" / \"does not contain\", a substring match on ONE string value; `_any` — equals any of a LIST of exact values; `_exists` / `_nexists`. Ordered comparisons are not supported on text. (A list passed to `_in` / `_nin` is tolerated and behaves like `_any` / its negation.)\n- number-like (number, percent, currency, `autoIncrement`, `numberFormula`, NUMBER lookups): `_eq`, `_ne`, `_lt`, `_lte`, `_gt`, `_gte`, `_exists` / `_nexists`. Numbers have no substring match, so \"one of\" is `_any` with a list (`_in` / `_nin` also require a list).\n- checkbox: `_exists` = ticked, `_nexists` = unticked (the form the NetHunt UI itself uses). `_eq` / `_ne` with `true` / `false` also work — `false` means unticked, since an unticked checkbox stores no value.\n- date / dateTime / DATE lookups: the ordered comparisons and `_exists` / `_nexists`; the value must be epoch-ms or an absolute ISO string. Relative date expressions (`today`, `last 7 days`) are accepted by `POST .../records/filter` but NOT inside a lookup filter.\n- user: `_eq` / `_ne` by email address, `_in` / `_nin` — substring of the email (one string), `_any` — a list of email addresses, `_exists` / `_nexists`. The `me` keyword does not resolve here — a lookup is computed with no requesting user, so pass a real address."},"rollup":{"$ref":"#/components/schemas/LookupRollup"},"format":{"description":"Display format. Only meaningful when the lookup resolves to a NUMBER value.","allOf":[{"$ref":"#/components/schemas/FreeNumberFormat"}]}},"required":["relatedFolderId","rollup"]},"TimeSinceUpdateOptions":{"type":"object","properties":{"fieldId":{"type":"string","example":"5","description":"Field id whose updates are tracked."},"unit":{"type":"string","enum":["days","hours"],"description":"Time unit of the elapsed-since-update value."}},"required":["fieldId"]},"FieldUpdateTimeOptions":{"type":"object","properties":{"fieldId":{"type":"string","example":"5","description":"Field id whose last update time is tracked."}},"required":["fieldId"]},"LastInteractionTimeOptions":{"type":"object","properties":{"includeTypes":{"type":"array","example":["email","call"],"description":"Activity groups included in the last-interaction calculation.","items":{"type":"string","enum":["email","call","comment","file","calendarEvent"]}}},"required":["includeTypes"]},"EmailStatCountOptions":{"type":"object","properties":{"emailTypes":{"type":"array","example":["regular"],"description":"Email kinds counted (used by emailOpenCount / linkClickCount).","items":{"type":"string","enum":["regular","campaign","automation"]}}},"required":["emailTypes"]},"FieldError":{"type":"object","properties":{"code":{"type":"string","example":"UNSUPPORTED_LOOKUP_SOURCE","enum":["REFERENCED_FOLDER_NOT_FOUND","REFERENCED_FIELD_NOT_FOUND","INVALID_FOLDER_REFERENCE","INVALID_REFERENCED_FIELD_TYPE","UNSUPPORTED_LOOKUP_SOURCE","LOOKUP_CHAIN_TOO_DEEP","LOOKUP_CYCLE","FIELD_MISCONFIGURED"]},"message":{"type":"string","example":"Field \"7\" of folder \"62b0a7c92d9387028a36f2f3\" cannot be used as the filter source of a lookup","description":"Human-readable explanation of the specific problem."},"folderId":{"type":"string","example":"62b0a7c92d9387028a36f2f3","description":"Folder the offending reference points into."},"fieldId":{"type":"string","example":"7","description":"Field inside `folderId` the offending reference points at."},"fieldFor":{"type":"string","enum":["folderLink","rollup","filter"],"description":"Which part of this field's configuration is broken: the link field, the rollup source or a filter source (`options.filterStr`)."},"requiredFieldType":{"type":"string","example":"folderLink","description":"For `INVALID_REFERENCED_FIELD_TYPE` — the sub-type the referenced field must have."},"requiredFolderId":{"type":"string","example":"62b0a7c92d9387028a36f2f3","description":"For `INVALID_FOLDER_REFERENCE` — the folder the reference must point at."}},"required":["code","message"]},"Field":{"type":"object","properties":{"id":{"type":"string","example":"8"},"name":{"type":"string","example":"Industry"},"description":{"type":"string","example":"Business sector."},"type":{"type":"string","example":"dropdown","description":"Public field sub-type (e.g. singleLineText, dropdown, currency, lookup, timeSinceUpdate, ...)."},"valueType":{"type":"string","example":"STRING","enum":["STRING","NUMBER","BOOLEAN","DATE","TIME","PERSON","RECORD"]},"multiValue":{"type":"boolean","example":false},"groupName":{"type":"string","example":"Properties"},"required":{"type":"boolean","example":false},"role":{"type":"string","example":"dueDate","description":"Role binding of a built-in system-folder field; absent for regular fields. In the Tasks folder (folder `role: \"task\"`) the values are: `name`, `description`, `completed`, `dueDate`, `allDay`, `priority`, `assignee`, `creator`, `recordLink`. Role-bound Tasks-folder fields are protected — updates are rejected with `FIELD_PROTECTED`."},"presets":{"type":"array","description":"Preset values (dropdown / tag only).","items":{"oneOf":[{"type":"string"},{"type":"number"}]},"example":["Tech","Finance"]},"options":{"description":"Per-subtype options object — discriminated by `type`. See `oneOf` for the full schema of each variant (EmptyOptions / NumberOptions / CurrencyOptions / AutoIncrementOptions / NumberFormulaOptions / TextFormulaOptions / FolderLinkOptions / LookupOptions / TimeSinceUpdateOptions / FieldUpdateTimeOptions / LastInteractionTimeOptions / EmailStatCountOptions).","oneOf":[{"$ref":"#/components/schemas/EmptyOptions"},{"$ref":"#/components/schemas/NumberOptions"},{"$ref":"#/components/schemas/PercentOptions"},{"$ref":"#/components/schemas/CurrencyOptions"},{"$ref":"#/components/schemas/AutoIncrementOptions"},{"$ref":"#/components/schemas/NumberFormulaOptions"},{"$ref":"#/components/schemas/TextFormulaOptions"},{"$ref":"#/components/schemas/FolderLinkOptions"},{"$ref":"#/components/schemas/LookupOptions"},{"$ref":"#/components/schemas/TimeSinceUpdateOptions"},{"$ref":"#/components/schemas/FieldUpdateTimeOptions"},{"$ref":"#/components/schemas/LastInteractionTimeOptions"},{"$ref":"#/components/schemas/EmailStatCountOptions"}]},"errors":{"description":"Configuration problems of this `folderLink` / `lookup` field, absent when the field is healthy. Such a field IS saved — writes are not rejected — but it cannot produce values: a lookup whose rollup or filter source is missing or unsupported computes to an empty value on every record. Check this array after creating or updating a lookup.","type":"array","items":{"$ref":"#/components/schemas/FieldError"}}},"required":["id","name","type","valueType","multiValue","groupName","required","options"]},"Folder":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e"},"name":{"type":"string","example":"Contacts"},"description":{"type":"string","example":"Company contacts used by sales."},"role":{"type":"string","enum":["contact","company","deal","task"],"example":"task","description":"Present only on folders that hold a system role. `contact`, `company` and `deal` are assignable by users (enrichment settings); `task` marks the workspace’s Tasks folder — match by this value, not by folder name (names are user-editable). The Tasks folder schema is system-managed: it contains a `dateTime` due-date field (a type that cannot be created via the API) and a record-link field that may reference records of any folder (its options carry no `linkedFolderId`)."},"createdAt":{"type":"number","example":1742292000000,"description":"Epoch ms."},"createdISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `createdAt`."},"updatedAt":{"type":"number","example":1742394191000,"description":"Epoch ms."},"updatedISO":{"type":"string","example":"2025-03-19T14:23:11.000Z","description":"ISO-8601 form of `updatedAt`."},"fields":{"description":"Populated only on GET /folders/{folderId}.","type":"array","items":{"$ref":"#/components/schemas/Field"}}},"required":["id","name","createdAt","createdISO","updatedAt","updatedISO"]},"CreateField":{"type":"object","properties":{"name":{"type":"string","example":"Industry"},"description":{"type":"string","example":"Business sector."},"type":{"type":"string","example":"dropdown","enum":["singleLineText","multiLineText","dropdown","tag","email","date","number","percent","currency","user","phoneNumber","address","website","checkbox","numberFormula","textFormula","autoIncrement","country","folderLink","lookup","facebook","linkedin","timeSinceUpdate","fieldUpdateTime","firstEmailFrom","lastEmailFrom","firstEmailTime","lastEmailTime","firstIncomingEmailTime","firstOutgoingEmailTime","incomingEmailCount","lastIncomingEmailTime","lastOutgoingEmailOpenCount","lastEmailOpenTime","emailOpenCount","linkClickCount","lastOutgoingEmailTime","outgoingEmailCount","totalEmailCount","totalThreadCount","lastCalendarEventAddedTime","lastCalendarEventTime","lastCallLogAddedTime","lastCommentAddedTime","lastFileAddedTime","totalCalendarEventsCount","totalCallLogsCount","totalCommentsCount","totalFilesCount","lastInteractionTime"],"description":"Public field sub-type. Supported subset of service-schema enum (`twitter` and `skype` are excluded)."},"valueType":{"type":"string","example":"STRING","enum":["STRING","NUMBER","BOOLEAN","DATE","TIME","PERSON","RECORD"],"description":"Derived from `type` when omitted. When set it must match the value type of the field sub-type; `dropdown` additionally accepts `NUMBER` to create a numeric dropdown (numeric presets). `lookup` accepts any value — the effective type is derived from the rollup."},"multiValue":{"type":"boolean","example":false},"groupName":{"type":"string","example":"Properties"},"required":{"type":"boolean","example":false},"presets":{"type":"array","description":"Preset values for dropdown / tag fields.","items":{"oneOf":[{"type":"string"},{"type":"number"}]},"example":["Tech","Finance"]},"options":{"description":"Per-subtype options object — discriminated by `type`. Variants: EmptyOptions (text-like, email, user, dropdown, tag, date, checkbox, parameterless stats) | NumberOptions (number, percent) | CurrencyOptions | AutoIncrementOptions | NumberFormulaOptions | TextFormulaOptions | FolderLinkOptions | LookupOptions | TimeSinceUpdateOptions | FieldUpdateTimeOptions | LastInteractionTimeOptions | EmailStatCountOptions. See `oneOf` for the full schema of each variant.","oneOf":[{"$ref":"#/components/schemas/EmptyOptions"},{"$ref":"#/components/schemas/NumberOptions"},{"$ref":"#/components/schemas/PercentOptions"},{"$ref":"#/components/schemas/CurrencyOptions"},{"$ref":"#/components/schemas/AutoIncrementOptions"},{"$ref":"#/components/schemas/NumberFormulaOptions"},{"$ref":"#/components/schemas/TextFormulaOptions"},{"$ref":"#/components/schemas/FolderLinkOptions"},{"$ref":"#/components/schemas/LookupOptions"},{"$ref":"#/components/schemas/TimeSinceUpdateOptions"},{"$ref":"#/components/schemas/FieldUpdateTimeOptions"},{"$ref":"#/components/schemas/LastInteractionTimeOptions"},{"$ref":"#/components/schemas/EmailStatCountOptions"}]}},"required":["name","type"]},"CreateFolder":{"type":"object","properties":{"name":{"type":"string","example":"Projects"},"description":{"type":"string","example":"Internal projects."},"fields":{"description":"Initial set of fields. Omit for an empty folder.","type":"array","items":{"$ref":"#/components/schemas/CreateField"}}},"required":["name"]},"UpdateFolder":{"type":"object","properties":{"name":{"type":"string","example":"Contacts (renamed)"},"description":{"type":"object","example":"Updated text","nullable":true,"description":"`null` clears the description."}}},"UpdateField":{"type":"object","properties":{"name":{"type":"string","example":"Industry (v2)"},"description":{"type":"object","example":"Updated description","nullable":true,"description":"`null` clears the description."},"required":{"type":"boolean","example":true},"presets":{"type":"array","description":"Replacement preset list (dropdown / tag).","items":{"oneOf":[{"type":"string"},{"type":"number"}]},"example":["Tech","Finance","Retail"]},"groupName":{"type":"string","example":"New Group"},"options":{"description":"Partial replacement of the per-subtype options object — deep-partial of the discriminated FieldOptions union (same variants as CreateField; any key inside a variant may be omitted). Structural keys (e.g. a number format’s `style`, a currency’s `currency`, a lookup’s `relatedFolderId`) are immutable; changing one yields `IMMUTABLE_PROPERTY`.","oneOf":[{"$ref":"#/components/schemas/EmptyOptions"},{"$ref":"#/components/schemas/NumberOptions"},{"$ref":"#/components/schemas/PercentOptions"},{"$ref":"#/components/schemas/CurrencyOptions"},{"$ref":"#/components/schemas/AutoIncrementOptions"},{"$ref":"#/components/schemas/NumberFormulaOptions"},{"$ref":"#/components/schemas/TextFormulaOptions"},{"$ref":"#/components/schemas/FolderLinkOptions"},{"$ref":"#/components/schemas/LookupOptions"},{"$ref":"#/components/schemas/TimeSinceUpdateOptions"},{"$ref":"#/components/schemas/FieldUpdateTimeOptions"},{"$ref":"#/components/schemas/LastInteractionTimeOptions"},{"$ref":"#/components/schemas/EmailStatCountOptions"}]}}},"CreateRecord":{"type":"object","properties":{"fields":{"type":"object","additionalProperties":true,"example":{"3":150000,"4":"john@acme.com","6":1787616000000,"name":"Acme Corp"},"description":"Friendly field values to set on the new record, keyed by field id (or label when `?fieldKeys=label`). The `name` key sets the record display name. Read-only fields (formulas, lookups, statistics, auto-number) are rejected.\n\nValue shape per field type. Call `GET /folders/{folderId}` to learn each field's `type` and `valueType`.\n\n- **text / email / website / phoneNumber / address / single-line / multi-line** → string\n- **number / currency / percent** → number\n- **checkbox** → boolean\n- **date** (`valueType: \"DATE\"`) → number — epoch milliseconds, see *Dates* below\n- **dateTime**, and time-valued statistics (`valueType: \"TIME\"`) → number — epoch milliseconds, any time of day\n- **dropdown** → string; **tag / multi-select** → string[]\n- **user** — single: string (email); multi: string[] (emails)\n- **record link** — single: string (record id); multi: string[] (record ids)\n\n### Dates\n\nA `date` field holds a **calendar date with no time of day**, so its epoch-ms value must land exactly on UTC midnight — `2026-08-25` is `1787616000000`. A timestamp that carries a time of day is not a valid calendar date and the write is rejected, so derive the value instead of passing the current time: `Date.UTC(2026, 7, 25)` (the month index is 0-based), or floor a timestamp to its day with `Math.floor(ms / 86400000) * 86400000`. Sending `Date.now()` unfloored is the usual mistake.\n\n`dateTime` and time-valued statistics have no such constraint — any instant is accepted."}}},"Record":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e"},"folderId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f"},"createdAt":{"type":"number","example":1742292000000,"description":"Epoch ms."},"createdISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `createdAt`."},"updatedAt":{"type":"number","example":1742394191000,"description":"Epoch ms."},"updatedISO":{"type":"string","example":"2025-03-19T14:23:11.000Z","description":"ISO-8601 form of `updatedAt`."},"fields":{"type":"object","additionalProperties":true,"example":{"2":"Active","3":150000,"4":"john@acme.com","5":["enterprise","priority"],"6":1743465600000,"name":"Acme Corp"},"description":"Friendly field values keyed by field id (or label when `?fieldKeys=label`). Empty and unset fields are omitted.\n\nValue shape per field type. Call `GET /folders/{folderId}` to learn each field's `type` and `valueType`.\n\n- **text / email / website / phoneNumber / address / single-line / multi-line** → string\n- **number / currency / percent** → number\n- **checkbox** → boolean\n- **date** (`valueType: \"DATE\"`) → number — epoch milliseconds, see *Dates* below\n- **dateTime**, and time-valued statistics (`valueType: \"TIME\"`) → number — epoch milliseconds, any time of day\n- **dropdown** → string; **tag / multi-select** → string[]\n- **user** — single: string (email); multi: string[] (emails)\n- **record link** — single: string (record id); multi: string[] (record ids)\n\n### Dates\n\nA `date` field holds a **calendar date with no time of day**, so its epoch-ms value must land exactly on UTC midnight — `2026-08-25` is `1787616000000`. A timestamp that carries a time of day is not a valid calendar date and the write is rejected, so derive the value instead of passing the current time: `Date.UTC(2026, 7, 25)` (the month index is 0-based), or floor a timestamp to its day with `Math.floor(ms / 86400000) * 86400000`. Sending `Date.now()` unfloored is the usual mistake.\n\n`dateTime` and time-valued statistics have no such constraint — any instant is accepted."}},"required":["id","folderId","createdAt","createdISO","updatedAt","updatedISO","fields"]},"RecordListResult":{"type":"object","properties":{"records":{"description":"Records in ascending `updatedAt` order (oldest first).","type":"array","items":{"$ref":"#/components/schemas/Record"}},"total":{"type":"number","example":1432,"description":"Total number of records updated since the requested time. Returned only when `since` is a timestamp (or omitted) — cache it client-side; it may exceed the number of returned `records`."},"nextSince":{"type":"string","description":"Continuation token — pass it back in `since` to fetch the next page. Absent when the window has been fully read."}},"required":["records"]},"RecordSearchResult":{"type":"object","properties":{"records":{"type":"array","items":{"$ref":"#/components/schemas/Record"}}},"required":["records"]},"FilterRecords":{"type":"object","properties":{"filter":{"type":"object","additionalProperties":true,"example":{"_and":[{"2":"Active"},{"3":{"_gte":100000}},{"updated":{"_gte":"last 30 days"}}]},"description":"Filter expression: a JSON object of field conditions, combinable with `_and` / `_or` groups. Keys are field ids (or labels with `?fieldKeys=label`); a bare value means equality, an object holds operators (`_eq`, `_ne`, `_in`, `_nin`, `_exists`, `_nexists`, `_lt`, `_lte`, `_gt`, `_gte`, `_any`). See the operation description for the full reference. Omit to match all records (up to `limit`)."},"sort":{"type":"array","items":{"type":"object","additionalProperties":{"type":"integer","enum":[1,-1]},"maxProperties":1},"example":[{"3":-1},{"name":1}],"description":"Sort order: up to 3 single-key objects `{ \"<fieldIdOrLabel>\": 1 | -1 }` (1 = ascending, -1 = descending), applied in array order. Defaults to `[{\"created\": 1}]`. The effective sort is echoed in the response and the pagination cursor is bound to it."},"cursor":{"type":"string","maxLength":4096,"description":"Opaque pagination token from the previous page`s `nextCursor`. Send it with the SAME `filter`, `sort` and `timeZone`; a cursor sent with a different one, or a malformed cursor, returns `400 INVALID_REQUEST` — restart without `cursor`. When set, `total` is omitted from the response."}}},"RecordFilterResult":{"type":"object","properties":{"records":{"type":"array","items":{"$ref":"#/components/schemas/Record"}},"sort":{"type":"array","items":{"type":"object","additionalProperties":{"type":"integer","enum":[1,-1]},"maxProperties":1},"example":[{"created":1}],"description":"The sort this page was produced with — the request `sort` or the default `[{\"created\": 1}]`. `nextCursor` is only valid together with this sort."},"total":{"type":"number","example":1432,"description":"Total number of records matching the filter. Returned only on the first page (request without `cursor`) — cache it client-side; it may exceed the number of returned `records`."},"nextCursor":{"type":"string","description":"Pagination token for the next page — pass it back in `cursor` with the same `filter` and `sort`. Absent on the last page."}},"required":["records","sort"]},"UpdateRecord":{"type":"object","properties":{"fields":{"type":"object","additionalProperties":true,"example":{"2":"Inactive","3":null,"6":1787616000000},"description":"Friendly field values to set, keyed by field id (or label when `?fieldKeys=label`). `null` clears a field; omitted keys are unchanged. The `name` key sets the record display name. Read-only fields (formulas, lookups, statistics, auto-number) are rejected.\n\nValue shape per field type. Call `GET /folders/{folderId}` to learn each field's `type` and `valueType`.\n\n- **text / email / website / phoneNumber / address / single-line / multi-line** → string\n- **number / currency / percent** → number\n- **checkbox** → boolean\n- **date** (`valueType: \"DATE\"`) → number — epoch milliseconds, see *Dates* below\n- **dateTime**, and time-valued statistics (`valueType: \"TIME\"`) → number — epoch milliseconds, any time of day\n- **dropdown** → string; **tag / multi-select** → string[]\n- **user** — single: string (email); multi: string[] (emails)\n- **record link** — single: string (record id); multi: string[] (record ids)\n\n### Dates\n\nA `date` field holds a **calendar date with no time of day**, so its epoch-ms value must land exactly on UTC midnight — `2026-08-25` is `1787616000000`. A timestamp that carries a time of day is not a valid calendar date and the write is rejected, so derive the value instead of passing the current time: `Date.UTC(2026, 7, 25)` (the month index is 0-based), or floor a timestamp to its day with `Math.floor(ms / 86400000) * 86400000`. Sending `Date.now()` unfloored is the usual mistake.\n\n`dateTime` and time-valued statistics have no such constraint — any instant is accepted."}}},"TimelinePerson":{"type":"object","properties":{"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane@acme.com"}}},"CommentTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["comment"]},"text":{"type":"string"},"mentions":{"type":"array","items":{"$ref":"#/components/schemas/TimelinePerson"}},"participants":{"type":"array","items":{"$ref":"#/components/schemas/TimelinePerson"}}},"required":["id","recordId","time","timeISO","pinned","type"]},"TimelineTrackStats":{"type":"object","properties":{"tracked":{"type":"boolean","example":true,"description":"Whether tracking was set up."},"total":{"type":"number","example":3,"description":"Distinct event count (opens / clicks)."},"lastUpdateTime":{"type":"number","example":1742394191000,"description":"Epoch ms of the latest event."}},"required":["tracked","total"]},"EmailTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["email"]},"subject":{"type":"string"},"snippet":{"type":"string"},"withAttachments":{"type":"boolean"},"messageCount":{"type":"number"},"participants":{"type":"array","items":{"$ref":"#/components/schemas/TimelinePerson"}},"emailOpenStats":{"description":"Open tracking stats — only with `includeEmailStats`.","allOf":[{"$ref":"#/components/schemas/TimelineTrackStats"}]},"linkClickStats":{"description":"Link-click tracking stats — only with `includeEmailStats`.","allOf":[{"$ref":"#/components/schemas/TimelineTrackStats"}]}},"required":["id","recordId","time","timeISO","pinned","type"]},"TimelineCampaignStats":{"type":"object","properties":{"emailOpenStats":{"$ref":"#/components/schemas/TimelineTrackStats"},"linkClickStats":{"$ref":"#/components/schemas/TimelineTrackStats"},"replyStats":{"$ref":"#/components/schemas/TimelineTrackStats"},"bounceStats":{"$ref":"#/components/schemas/TimelineTrackStats"},"unsubscribeStats":{"$ref":"#/components/schemas/TimelineTrackStats"},"updatedAt":{"type":"number","example":1742394191000}}},"CampaignEmailTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["campaign_email"]},"campaignName":{"type":"string","example":"Spring promo"},"emailTo":{"type":"string","example":"lead@acme.com"},"replyMsgsCount":{"type":"number","description":"Number of reply messages received."},"participants":{"type":"array","items":{"$ref":"#/components/schemas/TimelinePerson"}},"stats":{"description":"Aggregate open/click/reply/bounce/unsubscribe stats — only with `includeEmailStats`.","allOf":[{"$ref":"#/components/schemas/TimelineCampaignStats"}]}},"required":["id","recordId","time","timeISO","pinned","type"]},"TimelineCall":{"type":"object","properties":{"direction":{"type":"string","example":"incoming","description":"incoming | outgoing."},"duration":{"type":"number","example":73000,"description":"Call length, ms."},"waitDuration":{"type":"number","example":4000,"description":"Wait-before-pickup, ms."},"externalNumber":{"type":"string","example":"+380441234567"},"internalNumber":{"type":"string"},"participants":{"description":"Participant emails.","example":["agent@acme.com"],"type":"array","items":{"type":"string"}},"status":{"type":"string","example":"pick-up"},"isNewCaller":{"type":"boolean"},"disposition":{"type":"string"},"description":{"type":"string"},"recordingUrl":{"type":"string"},"linkToExternalCallCard":{"type":"string"}}},"CallTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["call"]},"duration":{"type":"number","description":"Logged call: duration, ms."},"text":{"type":"string","description":"Logged call: note text."},"participants":{"description":"Logged call participants.","type":"array","items":{"$ref":"#/components/schemas/TimelinePerson"}},"mentions":{"type":"array","items":{"$ref":"#/components/schemas/TimelinePerson"}},"call":{"description":"Telephony-integration call details.","allOf":[{"$ref":"#/components/schemas/TimelineCall"}]}},"required":["id","recordId","time","timeISO","pinned","type"]},"TimelineChatLastMessage":{"type":"object","properties":{"body":{"type":"string"},"caption":{"type":"string"}}},"TimelineChatAssignee":{"type":"object","properties":{"email":{"type":"string","example":"agent@acme.com"}}},"TimelineChatMember":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"username":{"type":"string"},"profile_pic":{"type":"string"}}},"TimelineChat":{"type":"object","properties":{"provider":{"type":"string","example":"telegram-bot","description":"Messenger provider (internal event type)."},"chatType":{"type":"string","example":"private","description":"private | group."},"name":{"type":"string"},"photo":{"type":"string"},"status":{"type":"string","example":"NOT_REPLIED"},"totalMessageCount":{"type":"number","example":12},"lastMessageDate":{"type":"number","example":1742394191000},"lastMessage":{"$ref":"#/components/schemas/TimelineChatLastMessage"},"assignees":{"type":"array","items":{"$ref":"#/components/schemas/TimelineChatAssignee"}},"members":{"type":"array","items":{"$ref":"#/components/schemas/TimelineChatMember"}},"closedAt":{"type":"number","example":1742394191000}}},"ChatTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["chat"]},"chat":{"$ref":"#/components/schemas/TimelineChat"}},"required":["id","recordId","time","timeISO","pinned","type"]},"CalendarTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["calendar"]},"summary":{"type":"string"},"description":{"type":"string"},"timeZone":{"type":"string"},"dateOnly":{"type":"boolean"},"status":{"type":"string"},"htmlLink":{"type":"string"},"end":{"type":"number","example":1742295600000,"description":"Event end, epoch ms."},"participants":{"type":"array","items":{"$ref":"#/components/schemas/TimelinePerson"}}},"required":["id","recordId","time","timeISO","pinned","type"]},"TimelineFileEntry":{"type":"object","properties":{"name":{"type":"string","example":"proposal.pdf"},"url":{"type":"string"},"mimeType":{"type":"string","example":"application/pdf"},"iconUrl":{"type":"string"},"downloadUrl":{"type":"string"},"googleDriveFileId":{"type":"string"}}},"FileTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["file"]},"files":{"type":"array","items":{"$ref":"#/components/schemas/TimelineFileEntry"}}},"required":["id","recordId","time","timeISO","pinned","type"]},"RecordCreatedTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["record_created"]},"fields":{"type":"object","additionalProperties":true,"description":"Initial field values, friendly-encoded (same as a record), keyed by field id (or label).","example":{"3":150000,"name":"Acme Corp"}}},"required":["id","recordId","time","timeISO","pinned","type","fields"]},"RecordUpdatedTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["record_updated"]},"changes":{"type":"object","additionalProperties":{"type":"object","properties":{"old":{},"new":{}}},"description":"Per changed field: { old, new } friendly-encoded values (same as a record), keyed by field id (or label). `old` is omitted when the field was empty before the change; `new` is omitted when the field was cleared.","example":{"2":{"old":"Active","new":"Paused"},"3":{"new":"Kyiv"},"4":{"old":"Draft"}}}},"required":["id","recordId","time","timeISO","pinned","type","changes"]},"OtherTimelineItem":{"type":"object","properties":{"id":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0e","description":"Unique, stable id of the timeline item — present on every item of every type; the same item always has the same `id`, so use it to identify or de-duplicate items. For `email`, `campaign_email` and `chat` items it also addresses the content endpoint (full message bodies / transcript); other types are complete in the list."},"recordId":{"type":"string","example":"5f8a2b1c3d4e5f6a7b8c9d0f","description":"The record the event belongs to. Together with `id`, it addresses the content endpoint `GET /v2/folders/{folderId}/records/{recordId}/timeline/{id}/content`."},"time":{"type":"number","example":1742292000000,"description":"Epoch ms."},"timeISO":{"type":"string","example":"2025-03-18T10:00:00.000Z","description":"ISO-8601 form of `time`."},"pinned":{"type":"boolean","example":false},"origin":{"type":"string","example":"MANUALLY","description":"How the event originated — MANUALLY, AUTOMATION, API, IMPORT, UNDO, …"},"creator":{"description":"Who created the event (absent when the actor is not a resolvable workspace user — automation/import — or for some externally-sourced chats/calls).","allOf":[{"$ref":"#/components/schemas/TimelinePerson"}]},"type":{"type":"string","enum":["other"]}},"required":["id","recordId","time","timeISO","pinned","type"]},"RecordTimelinePage":{"type":"object","properties":{"events":{"type":"array","description":"Timeline events, newest-first. Each item is one of the per-category shapes, discriminated by `type`.","items":{"oneOf":[{"$ref":"#/components/schemas/CommentTimelineItem"},{"$ref":"#/components/schemas/EmailTimelineItem"},{"$ref":"#/components/schemas/CampaignEmailTimelineItem"},{"$ref":"#/components/schemas/CallTimelineItem"},{"$ref":"#/components/schemas/ChatTimelineItem"},{"$ref":"#/components/schemas/CalendarTimelineItem"},{"$ref":"#/components/schemas/FileTimelineItem"},{"$ref":"#/components/schemas/RecordCreatedTimelineItem"},{"$ref":"#/components/schemas/RecordUpdatedTimelineItem"},{"$ref":"#/components/schemas/OtherTimelineItem"}],"discriminator":{"propertyName":"type","mapping":{"comment":"#/components/schemas/CommentTimelineItem","email":"#/components/schemas/EmailTimelineItem","campaign_email":"#/components/schemas/CampaignEmailTimelineItem","call":"#/components/schemas/CallTimelineItem","chat":"#/components/schemas/ChatTimelineItem","calendar":"#/components/schemas/CalendarTimelineItem","file":"#/components/schemas/FileTimelineItem","record_created":"#/components/schemas/RecordCreatedTimelineItem","record_updated":"#/components/schemas/RecordUpdatedTimelineItem","other":"#/components/schemas/OtherTimelineItem"}}}},"nextCursor":{"type":"string","description":"Pagination token for the next page — pass it back in `cursor` with the same filters. Absent on the last page."}},"required":["events"]},"TimelineMessagePerson":{"type":"object","properties":{"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane@acme.com"}}},"TimelineMessageAttachment":{"type":"object","properties":{"name":{"type":"string","example":"proposal.pdf"},"mimeType":{"type":"string","example":"application/pdf"},"size":{"type":"number","description":"Size in bytes.","example":18432},"url":{"type":"string"}}},"TimelineChatMedia":{"type":"object","properties":{"fileName":{"type":"string","example":"photo.jpg"},"mimeType":{"type":"string","example":"image/jpeg"},"fileSize":{"type":"number","description":"Size in bytes.","example":206256},"duration":{"type":"number","description":"Duration in seconds (audio / video).","example":42}}},"TimelineMessage":{"type":"object","properties":{"globalMessageIds":{"description":"RFC 822 Message-ID(s): this message plus its thread references.","type":"array","items":{"type":"string"}},"from":{"type":"array","items":{"$ref":"#/components/schemas/TimelineMessagePerson"}},"to":{"type":"array","items":{"$ref":"#/components/schemas/TimelineMessagePerson"}},"cc":{"type":"array","items":{"$ref":"#/components/schemas/TimelineMessagePerson"}},"bcc":{"type":"array","items":{"$ref":"#/components/schemas/TimelineMessagePerson"}},"subject":{"type":"string"},"snippet":{"type":"string"},"htmlText":{"type":"string","description":"Full HTML body."},"references":{"type":"array","items":{"type":"string"}},"incoming":{"type":"boolean","description":"true = received, false = sent."},"time":{"type":"number","example":1742394191000,"description":"Epoch ms."},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/TimelineMessageAttachment"}},"headers":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Raw message headers, each { name, values }."},"sender":{"description":"Messenger message sender.","allOf":[{"$ref":"#/components/schemas/TimelineMessagePerson"}]},"type":{"type":"string","description":"Messenger message kind (lowercase).","example":"photo"},"body":{"type":"string","description":"Messenger text body."},"caption":{"type":"string","description":"Caption of a media message."},"fileUrl":{"type":"string","description":"Download URL of a media message."},"thumbnailUrl":{"type":"string","description":"Thumbnail URL of a media message."},"media":{"$ref":"#/components/schemas/TimelineChatMedia"}}},"TimelineMessages":{"type":"object","properties":{"messages":{"description":"Email/campaign message bodies, or the messenger transcript (different per-message shape).","type":"array","items":{"$ref":"#/components/schemas/TimelineMessage"}},"hasMore":{"type":"boolean","description":"Email path only: more messages exist beyond the returned window."}},"required":["messages"]},"CreateComment":{"type":"object","properties":{"text":{"type":"string","example":"Discussed the renewal — @jane.doe@acme.com please prepare the quote.","maxLength":65000,"description":"Plain-text comment body. To mention a workspace member, put their email right after an `@` at a word start (e.g. `@jane.doe@acme.com`): recognized members are returned in `mentions` and get notified; any other `@…` token stays plain text."}},"required":["text"]},"CreateCallLog":{"type":"object","properties":{"text":{"type":"string","example":"Intro call — agreed on a follow-up demo next week.","maxLength":65000,"description":"Plain-text call notes. To mention a workspace member, put their email right after an `@` at a word start (e.g. `@jane.doe@acme.com`): recognized members are returned in `mentions` and get notified; any other `@…` token stays plain text."},"time":{"oneOf":[{"type":"string","example":"2026-07-22T10:00:00Z"},{"type":"number","example":1742292000000}],"example":"2026-07-22T10:00:00Z","description":"When the call happened. Accepts an ISO 8601 date-time (e.g. `2026-07-22T10:00:00Z`) or an epoch-milliseconds timestamp — either as a string or a bare JSON number. Defaults to the moment the call log is created."},"duration":{"type":"integer","example":900000,"minimum":0,"description":"Call duration in whole milliseconds (a non-negative integer), e.g. `900000` for a 15-minute call."}},"required":["text"]},"FolderTimelinePage":{"type":"object","properties":{"events":{"type":"array","description":"Timeline events, newest-first. Each item is one of the per-category shapes, discriminated by `type`.","items":{"oneOf":[{"$ref":"#/components/schemas/CommentTimelineItem"},{"$ref":"#/components/schemas/EmailTimelineItem"},{"$ref":"#/components/schemas/CampaignEmailTimelineItem"},{"$ref":"#/components/schemas/CallTimelineItem"},{"$ref":"#/components/schemas/ChatTimelineItem"},{"$ref":"#/components/schemas/CalendarTimelineItem"},{"$ref":"#/components/schemas/FileTimelineItem"},{"$ref":"#/components/schemas/RecordCreatedTimelineItem"},{"$ref":"#/components/schemas/RecordUpdatedTimelineItem"},{"$ref":"#/components/schemas/OtherTimelineItem"}],"discriminator":{"propertyName":"type","mapping":{"comment":"#/components/schemas/CommentTimelineItem","email":"#/components/schemas/EmailTimelineItem","campaign_email":"#/components/schemas/CampaignEmailTimelineItem","call":"#/components/schemas/CallTimelineItem","chat":"#/components/schemas/ChatTimelineItem","calendar":"#/components/schemas/CalendarTimelineItem","file":"#/components/schemas/FileTimelineItem","record_created":"#/components/schemas/RecordCreatedTimelineItem","record_updated":"#/components/schemas/RecordUpdatedTimelineItem","other":"#/components/schemas/OtherTimelineItem"}}}},"nextCursor":{"type":"string","description":"Pagination token for the next page — pass it back in `cursor` with the same filters. Present while more events may remain, absent only on the last page. Pages are normally full, but one can hold fewer events than `limit`, or none at all, and still carry `nextCursor` — never infer the end from the number of events."}},"required":["events"]}}}}