Files
Upload and manage files and attachments. Files are uploaded with multipart/form-data via POST /api/files/upload and can be linked to companies, contacts, deals, and notes.
Returns file records the user has access to. Filter by workspace using the where parameter: {"workspaceId": "<WORKSPACE_UUID>"}.
Files can also be filtered by companyId, contactId, dealId, or noteId to retrieve files attached to a specific record.
Note: Files are created by uploading via
POST /api/files/upload(multipart/form-data), not by a JSONPOST /api/files.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Comma-separated list of fields to return. Defaults to all fields.
Supports relation expansion with dot-notation: a path like company.name embeds the named field of the related record, and quotes.id embeds the id of each related quote. Use .* to embed full related objects, e.g. quotes.*. Related records are returned nested on the response — as an object for to-one relations (e.g. company) or an array for to-many relations (e.g. quotes). Archived related records are excluded.
Example: GET /api/deals/{dealId}?fields=id,name,quotes.id returns the deal with a quotes array of {"id": "<QUOTE_UUID>"} entries.
id,name,domainJSON-encoded filter object. All top-level conditions are combined with AND logic. Use $or for OR logic.
The available operators depend on the data type of the field you are filtering on.
String fields
Fields like name, domain, description, linkedin, source, externalId, location.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"name": "Linear"} |
$eq | Explicit exact match | {"name": {"$eq": "Linear"}} |
$not | Not equal | {"source": {"$not": "import"}} |
$in | Matches any value in array | {"domain": {"$in": ["linear.app", "granola.so"]}} |
$notIn | Matches none of the values | {"source": {"$notIn": ["import", "api"]}} |
$contains | Case-insensitive word-boundary substring match | {"name": {"$contains": "YC"}} |
$notContains | Does not contain | {"name": {"$notContains": "Test"}} |
$containsAny | Contains any of the given strings | {"name": {"$containsAny": ["YC", "Techstars"]}} |
$startsWith | Starts with prefix | {"domain": {"$startsWith": "app."}} |
$endsWith | Ends with suffix | {"email": {"$endsWith": "@zero.inc"}} |
$exists | Field is present and truthy | {"linkedin": {"$exists": true}} |
$notExists | Field is absent, null, or empty | {"linkedin": {"$notExists": true}} |
Number fields
Fields like value, confidence.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"value": 5000} |
$eq | Explicit exact match | {"value": {"$eq": 5000}} |
$not | Not equal | {"value": {"$not": 0}} |
$gt | Greater than | {"value": {"$gt": 10000}} |
$gte | Greater than or equal | {"value": {"$gte": 5000}} |
$lt | Less than | {"value": {"$lt": 10000}} |
$lte | Less than or equal | {"value": {"$lte": 50000}} |
$in | Matches any value in array | {"confidence": {"$in": [0.25, 0.5, 0.75]}} |
$notIn | Matches none of the values | {"confidence": {"$notIn": [0, 1]}} |
$exists | Field is present and truthy | {"value": {"$exists": true}} |
$notExists | Field is absent, null, or zero | {"value": {"$notExists": true}} |
Multiple operators can be combined on one field:
{"value": {"$gte": 5000, "$lt": 10000}}
Date fields
Fields like closeDate, startDate, endDate, createdAt, updatedAt.
Values can be ISO 8601 strings ("2026-01-01", "2026-01-01T00:00:00Z") or relative time macros.
Relative time macros: +Nd / -Nd (days), +Nw / -Nw (weeks), +Nm / -Nm (months), +Ny / -Ny (years), +Nh / -Nh (hours), +Ns / -Ns (seconds), now().
| Operator | Description | Example |
|---|---|---|
$gte | On or after | {"closeDate": {"$gte": "2026-01-01"}} |
$lte | On or before | {"closeDate": {"$lte": "2026-03-31"}} |
$gt | After | {"createdAt": {"$gt": "2026-01-01T00:00:00Z"}} |
$lt | Before | {"createdAt": {"$lt": "now()"}} |
$date | Exact date match (compares date portion only) | {"closeDate": {"$date": "2026-01-15"}} |
$exists | Field is present and truthy | {"closeDate": {"$exists": true}} |
$notExists | Field is absent or null | {"closeDate": {"$notExists": true}} |
Date range example:
{"closeDate": {"$gte": "2026-01-01", "$lte": "2026-03-31"}}
Relative date example (closing in next 30 days):
{"closeDate": {"$gte": "now()", "$lte": "+30d"}}
Array fields
Fields like listIds, ownerIds, contactIds.
| Operator | Description | Example |
|---|---|---|
$includes | Array contains the given value (use this — bare exact match is not supported) | {"listIds": {"$includes": "<LIST_UUID>"}} |
$notIncludes | Array does not contain the given value | {"ownerIds": {"$notIncludes": "<USER_UUID>"}} |
$overlaps | Array contains at least one of the given values | {"ownerIds": {"$overlaps": ["<UUID_1>", "<UUID_2>"]}} |
$notOverlaps | Array contains none of the given values | {"listIds": {"$notOverlaps": ["<UUID_1>", "<UUID_2>"]}} |
$all | Array contains all of the given values | {"listIds": {"$all": ["<UUID_1>", "<UUID_2>"]}} |
$length | Filter by array length (supports nested operators) | {"contactIds": {"$length": {"$gte": 2}}} |
$exists | Field is present and non-empty | {"ownerIds": {"$exists": true}} |
$notExists | Field is absent or empty | {"ownerIds": {"$notExists": true}} |
UUID / ID fields
Fields like id, workspaceId, companyId, stage.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"stage": "<PIPELINE_STAGE_UUID>"} |
$in | Matches any value in array | {"stage": {"$in": ["<UUID_1>", "<UUID_2>"]}} |
$notIn | Matches none of the values | {"stage": {"$notIn": ["<UUID_1>"]}} |
$not | Not equal | {"stage": {"$not": "<UUID>"}} |
Boolean fields
Fields like archived.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"archived": false} |
$not | Not equal | {"archived": {"$not": true}} |
Logical operators
These work across all data types.
$or — matches if any sub-condition is true:
{
"workspaceId": "<WORKSPACE_UUID>",
"$or": [
{"ownerIds": {"$overlaps": ["<USER_UUID_1>", "<USER_UUID_2>"]}},
{"closeDate": {"$gte": "2026-01-01"}}
]
}
$and — matches if all sub-conditions are true (useful when you need multiple conditions on the same field):
{
"$and": [
{"name": {"$contains": "Enterprise"}},
{"name": {"$notContains": "Test"}}
]
}
Dot-notation (relation filtering)
Use dot-syntax to filter records based on properties of related objects:
{"company.name": "Linear"}
{"company.domain": {"$in": ["linear.app", "granola.so"]}}
{"company.location.city": "San Francisco"}
{"companyProfile.categories": {"$overlaps": ["Sales", "Marketing"]}}
All operators available for the target field's data type can be used with dot-notation.
Custom property filtering
Custom properties are stored in the custom object on records, keyed by UUID. Use GET /api/columns to discover the available custom property IDs, types, and options for a workspace.
Filter on custom properties using custom.<COLUMN_ID> with operators appropriate for the column's type.
Note: For
selectandmultiselectcolumns, option values are UUIDs (thekeyfield from the column'soptionsarray). Use the UUID, not the human-readable name.
// Text custom property
{"custom.54e1ca7d-69c3-4b77-8266-8085b5834116": {"$contains": "enterprise"}}
// Select custom property (use the option key UUID)
{"custom.a1b2c3d4-e5f6-7890-abcd-ef1234567890": "3e839b5c-b311-4887-b2da-727d2d75cdd6"}
// Multi-select custom property (use option key UUIDs)
{"custom.b2c3d4e5-f6a7-8901-bcde-f12345678901": {"$overlaps": ["a1b1c1d1-e1f1-1111-aaaa-111111111111", "b2b2c2d2-e2f2-2222-bbbb-222222222222"]}}
// Currency/number custom property
{"custom.c3d4e5f6-a7b8-9012-cdef-123456789012": {"$gte": 100000}}
// Date custom property
{"custom.d4e5f6a7-b8c9-0123-defa-234567890123": {"$gte": "2026-01-01"}}
// Boolean custom property
{"custom.e5f6a7b8-c9d0-1234-efab-345678901234": true}
// Check if custom property has a value
{"custom.f6a7b8c9-d0e1-2345-fabc-456789012345": {"$exists": true}}
Complex example
All top-level keys are ANDed together:
{
"name": {"$contains": "Zero"},
"location.city": "Helsinki",
"location.country": {"$in": ["United Kingdom", "Germany", "Sweden"]},
"value": {"$gte": 10000},
"closeDate": {"$gte": "2026-01-01", "$lte": "2026-03-31"},
"ownerIds": {"$includes": "<USER_UUID>"},
"stage": {"$in": ["<UUID_1>", "<UUID_2>"]},
"lastActivity": {"$exists": true},
"companyProfile.categories": {"$overlaps": ["Sales", "Marketing"]},
"custom.54e1ca7d-69c3-4b77-8266-8085b5834116": {"$contains": "enterprise"}
}
{"stage":"<PIPELINE_STAGE_UUID>"}Maximum number of records to return
100Pagination offset
0JSON string for sort order
{"name":"asc"}Successful response
Authentication failed or token is invalid/expired
GET /api/files HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"contactId": "123e4567-e89b-12d3-a456-426614174000",
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"noteId": "123e4567-e89b-12d3-a456-426614174000",
"calendarEventId": "123e4567-e89b-12d3-a456-426614174000",
"placement": "text",
"url": "text",
"thumbnailUrl": "text",
"previewUrl": "text",
"name": "text",
"description": "text",
"size": 1,
"type": "text",
"metadata": {
"width": 1,
"height": 1,
"type": "text",
"format": "text",
"pages": 1,
"duration": 1
},
"assetId": "text",
"publicId": "text",
"googleCloudStorageId": "text",
"externalId": "text",
"importId": "123e4567-e89b-12d3-a456-426614174000",
"archived": true,
"archivedById": "123e4567-e89b-12d3-a456-426614174000",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
],
"total": 1
}Upload a file or image using multipart/form-data. Unlike the JSON CRUD endpoints, this endpoint does not accept a JSON body — send the binary in a file form part alongside text form fields.
The maximum file size is 32 MB; larger uploads are rejected with HTTP 413.
Upload types
The type form field selects how the file is stored and what is returned:
file— A general document or attachment (PDF, image, video, etc.). The binary is stored in cloud storage and thumbnails/previews are generated for supported content types. RequiresworkspaceId. Can be linked to a record viacompanyId,contactId,dealId, ornoteId. Returns the full file record (see theFileschema).image— An image asset, typically forlogos,avatars, or inlineattachments. Requiresfolder. Returns a lightweight object with the hosted image URL.
Thumbnails & previews
For type=file, thumbnailUrl and previewUrl are generated automatically for these content types: image/jpeg, image/png, image/gif, image/webp, application/pdf, video/mp4, video/mpeg, video/ogg, video/webm, video/quicktime. For other types they are null.
Downloading
For type=file, the stored url points to authenticated storage and is not directly accessible. Use GET /api/files/download?fileId=<FILE_ID> to obtain a short-lived signed URL, and GET /api/files/pages?fileId=<FILE_ID> to list per-page preview images for multi-page documents.
Example (curl)
curl -X POST https://api.zero.inc/api/files/upload \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-F "type=file" \
-F "workspaceId=<WORKSPACE_UUID>" \
-F "companyId=<COMPANY_UUID>" \
-F "file=@/path/to/proposal.pdf"All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
The binary file contents. Maximum 32 MB.
file stores a general document/attachment; image stores an image asset (logos, avatars, inline attachments).
Required when type=file. Optional when type=image.
Required when type=image. Storage folder for the image.
type=image only. When set to any non-empty value, the image is scaled down to a maximum of 128×128 px WebP (aspect ratio preserved; used for avatars and logos).
type=file only. Link the file to a company.
type=file only. Link the file to a contact.
type=file only. Link the file to a deal.
type=file only. Link the file to a note.
type=file only. Optional placement/context label for the file.
type=file only. Optional human-readable description.
Optional ID from an external system for integrations.
File uploaded successfully.
For type=file, data is the full file record. For type=image, data is a lightweight object with the hosted image URL.
Bad request — for example, workspaceId is missing for a type=file upload.
Authentication failed or token is invalid/expired
File too large — the file exceeds the 32 MB limit.
POST /api/files/upload HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: multipart/form-data
Accept: */*
Content-Length: 383
{
"file": "binary",
"type": "file",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"folder": "logos",
"transformation": "text",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"contactId": "123e4567-e89b-12d3-a456-426614174000",
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"noteId": "123e4567-e89b-12d3-a456-426614174000",
"placement": "text",
"description": "text",
"externalId": "text"
}{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"contactId": "123e4567-e89b-12d3-a456-426614174000",
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"noteId": "123e4567-e89b-12d3-a456-426614174000",
"calendarEventId": "123e4567-e89b-12d3-a456-426614174000",
"placement": "text",
"url": "text",
"thumbnailUrl": "text",
"previewUrl": "text",
"name": "text",
"description": "text",
"size": 1,
"type": "text",
"metadata": {
"width": 1,
"height": 1,
"type": "text",
"format": "text",
"pages": 1,
"duration": 1
},
"assetId": "text",
"publicId": "text",
"googleCloudStorageId": "text",
"externalId": "text",
"importId": "123e4567-e89b-12d3-a456-426614174000",
"archived": true,
"archivedById": "123e4567-e89b-12d3-a456-426614174000",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
}Returns a short-lived signed URL for downloading a type=file upload from storage. The URL expires 15 minutes after it is issued.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Signed download URL
Signed, time-limited download URL (valid for 15 minutes).
Authentication failed or token is invalid/expired
Returned when the fileId does not exist or is not accessible in the caller's workspace. The handler currently raises an unhandled error, surfaced as HTTP 500 with the message File not found (a future release may return 404 instead).
GET /api/files/download?fileId=123e4567-e89b-12d3-a456-426614174000 HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"url": "https://example.com"
}Returns per-page thumbnail and preview image URLs for a file, useful for multi-page documents such as PDFs.
If the file's content type does not support previews, data is false.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Page previews. data is false when the file type does not support previews.
falseAuthentication failed or token is invalid/expired
Returned when the fileId does not exist or is not accessible in the caller's workspace. The handler currently raises an unhandled error, surfaced as HTTP 500 with the message File not found (a future release may return 404 instead).
GET /api/files/pages?fileId=123e4567-e89b-12d3-a456-426614174000 HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": false
}Returns a single file record by ID.
If the file does not exist, the API returns HTTP 200 with an empty body ({}).
To obtain a downloadable URL for the file contents, use GET /api/files/download?fileId=<FILE_ID>.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Successful response
Authentication failed or token is invalid/expired
GET /api/files/{fileId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"contactId": "123e4567-e89b-12d3-a456-426614174000",
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"noteId": "123e4567-e89b-12d3-a456-426614174000",
"calendarEventId": "123e4567-e89b-12d3-a456-426614174000",
"placement": "text",
"url": "text",
"thumbnailUrl": "text",
"previewUrl": "text",
"name": "text",
"description": "text",
"size": 1,
"type": "text",
"metadata": {
"width": 1,
"height": 1,
"type": "text",
"format": "text",
"pages": 1,
"duration": 1
},
"assetId": "text",
"publicId": "text",
"googleCloudStorageId": "text",
"externalId": "text",
"importId": "123e4567-e89b-12d3-a456-426614174000",
"archived": true,
"archivedById": "123e4567-e89b-12d3-a456-426614174000",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
}Delete a file. Use archive=true for soft delete (recoverable), or omit for permanent deletion.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
If true, soft deletes (archives) the file instead of permanently deleting it.
falseFile deleted successfully
Hard delete (archive omitted or false): returns 1 on success.
Soft delete (archive=true): returns a single-element array containing the archived file object.
1Authentication failed or token is invalid/expired
DELETE /api/files/{fileId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": 1,
"sideEffects": []
}Update metadata on an existing file (e.g. name, description, or the record it is linked to). The file's binary contents cannot be changed — re-upload via POST /api/files/upload to replace contents.
Note: The response only returns the fields that were changed — not the full file object.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Editable file metadata. The file's binary contents cannot be changed via PATCH — re-upload to replace them.
File updated successfully. Only the fields that were changed are returned in data.
Partial file object containing only the updated fields plus updatedAt and updatedById.
Authentication failed or token is invalid/expired
PATCH /api/files/{fileId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 58
{
"description": "Signed proposal, v2",
"dealId": "deal-uuid"
}{
"data": {},
"sideEffects": []
}Last updated

