Upload Documents
The Upload API lets you programmatically add documents to your Workvivo organisation. You can upload files directly into document folders, assign them to the correct audience, and manage the full lifecycle from creation to deletion.
The upload flow depends on file size:
- Files up to 10 MB — upload the file directly in a single request.
- Files over 10 MB (up to 5 GB) — use the asset flow: create a signed upload URL, upload the file to cloud storage, then link the asset to a document folder.
This guide walks through both approaches and covers folder management, audience configuration, and asset lifecycle.
Documents uploaded via the API are attributed to a specific user in your organisation. You must provide a
user_idoruser_external_idwith every upload request.
A note on locations and folders: In the Workvivo UI, the destination for a document is called a "location." A location is typically a folder, but can also be the top-level root (no folder). In the API, the
location_idparameter refers to the ID of the destination folder. If omitted, the document is placed at the top level. The List Folders endpoint returns all available locations with their IDs.
Before you begin
Create a static API key under API Keys & JWT Settings in Workvivo administration. Send it as a Bearer token with the Workvivo-Id header on every API request.
Select only the permissions needed by your integration:
documents.writeto upload, create, and delete documents.documents.readto list documents and folders.assets.writeto create upload URLs and manage assets.assets.readto list and inspect assets.
You will also need:
- The location ID where the document should be placed. Use the List Folders endpoint to find it.
- The user ID of the employee the document should be attributed to.
- For large files, a client that can make HTTP PUT requests directly to a cloud storage URL.
1. List folders
Before uploading a document, retrieve the location ID for the target destination. Folders are identified by unique location IDs because folder names are not guaranteed to be unique.
curl -X GET "https://api.workvivo.com/v1/documents/folders" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json"
{
"data": [
{
"id": 40,
"label": "HR Policies",
"audience": {
"is_global": true,
"spaces": [],
"teams": []
},
"parent_id": null
},
{
"id": 41,
"label": "IT Documentation",
"audience": {
"is_global": true,
"spaces": [],
"teams": []
},
"parent_id": 40
}
],
"pagination": {
"skip": 0,
"take": 20,
"total": 2
}
}
A folder with a parent_id is nested inside another folder. Use the id value as the location_id parameter when uploading documents.
You can also create a new folder if the target destination does not exist:
curl -X POST "https://api.workvivo.com/v1/documents/folders" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"label": "Onboarding Materials",
"parent_id": 40
}'
{
"id": 65,
"label": "Onboarding Materials",
"audience": {
"is_global": true,
"spaces": [],
"teams": []
},
"parent_id": 40
}
2. Upload a document (files up to 10 MB)
For files 10 MB or smaller, upload the document directly using multipart form data. The file, location ID, and user attribution are sent in a single request.
curl -X POST "https://api.workvivo.com/v1/documents/upload" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json" \
-F "file=@/path/to/employee-handbook.pdf" \
-F "location_id=65" \
-F "label=Employee Handbook 2026" \
-F "user_id=1042"
{
"id": 202,
"label": "Employee Handbook 2026",
"original_filename": "employee-handbook.pdf",
"file_size": 4521984,
"mime_type": "application/pdf",
"location_id": 65,
"audience": {
"is_global": true,
"spaces": [],
"teams": []
},
"uploaded_at": "2026-07-28T11:41:00Z"
}
Note: No audience parameter is passed in this example. When omitted, the document defaults to global visibility.
Important: You cannot upload a file and reference a
document_idin the same request. Use one approach or the other.document_idis used for file uploads larger than 10 MB. See Section 3 for details.
If the file exceeds 10 MB, the API returns an error directing you to use the asset flow instead.
3. Upload a document (files over 10 MB)
For files larger than 10 MB (up to 5 GB), use the four-step asset flow. This uploads the file directly to cloud storage, bypassing Workvivo's application servers to avoid timeouts on large transfers.
Step 1: Create an upload URL
Request a signed upload URL by providing metadata about the file. This creates an asset record in Workvivo with a status of pending_upload and returns a time-limited URL for the actual upload.
curl -X POST "https://api.workvivo.com/v1/assets/upload-url" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"original_filename": "training-video.mp4",
"file_size": 709000000,
"mime_type": "video/mp4"
}'
{
"asset": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
"upload": {
"url": "https://s3.eu-west-1.amazonaws.com/workvivo-assets/...",
"method": "PUT",
"headers": {
"Content-Type": "video/mp4"
},
"expires_at": "2026-07-28T12:41:00Z"
}
}
Store the asset id from the response — you will need it in subsequent steps. The upload URL expires after a configured period (shown in expires_at). If the URL expires before you upload, create a new one.
Step 2: Upload the asset to cloud storage
Use the signed URL from Step 1 to upload the file directly to cloud storage. This request goes to the cloud provider, not to Workvivo's servers.
curl -X PUT "https://s3.eu-west-1.amazonaws.com/workvivo-assets/..." \
-H "Content-Type: video/mp4" \
--data-binary @/path/to/training-video.mp4
The cloud provider returns a 200 OK on success. No response body is returned.
Note: This request bypasses Workvivo entirely. The URL, method, and headers are exactly as specified in the Step 1 response. Do not add Workvivo authentication headers to this request.
Upload time depends on file size and network speed. For a 500 MB file, expect anywhere from a few seconds to several minutes.
Step 3: Confirm the upload
After the file has been uploaded to cloud storage, notify Workvivo so it can verify the file exists and update the asset status from pending_upload to ready.
curl -X POST "https://api.workvivo.com/v1/assets/{asset_id}/confirm" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json"
{
"asset": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
"status": "ready",
"message": "Asset uploaded successfully."
}
Workvivo checks with the cloud provider to confirm the file exists and matches the expected size. If the file is not found, the endpoint returns an error.
Step 4: Link the asset to a document folder
Once the asset is ready, create a document that references it. This moves the file from temporary asset storage into the correct document folder.
curl -X POST "https://api.workvivo.com/v1/documents/upload" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"document_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_id": 65,
"label": "Q3 Training Video",
"user_id": 1042
}'
{
"id": 203,
"label": "Q3 Training Video",
"original_filename": "training-video.mp4",
"file_size": 709000000,
"mime_type": "video/mp4",
"location_id": 65,
"audience": {
"is_global": true,
"spaces": [],
"teams": []
},
"uploaded_at": "2026-07-28T11:45:00Z"
}
Once linked to a document, the asset is removed from the assets list. The document now lives in the specified folder and is accessible to the configured audience.
4. Manage assets
Assets are temporary holding records for files that have been uploaded to cloud storage but not yet linked to a document. The assets endpoint only returns orphan assets — those not yet associated with any document.
List assets
curl -X GET "https://api.workvivo.com/v1/assets" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json"
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"original_filename": "training-video.mp4",
"file_size": 709000000,
"mime_type": "video/mp4",
"uploaded_at": "2026-07-28T11:41:00Z"
}
],
"pagination": {
"skip": 0,
"take": 20,
"total": 1
}
}
Get a single asset
curl -X GET "https://api.workvivo.com/v1/assets/{asset_id}" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json"
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"original_filename": "training-video.mp4",
"file_size": 709000000,
"mime_type": "video/mp4",
"uploaded_at": "2026-07-28T11:41:00Z"
}
Delete an asset
Remove an orphan asset that is no longer needed. This deletes both the asset record and the file from cloud storage.
curl -X DELETE "https://api.workvivo.com/v1/assets/{asset_id}" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json"
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
"status": "success"
}
Automatic cleanup: Assets with a
pending_uploadstatus that have not been uploaded before their URL expires are automatically removed by a scheduled cleanup process. You do not need to manually delete expired assets.
5. Delete a document
Remove a document from a folder. This permanently deletes the document and its associated file from storage.
curl -X DELETE "https://api.workvivo.com/v1/documents/{document_id}" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json"
{
"data": {
"id": 202
},
"status": "success"
}
Audience configuration
The audience parameter controls who can see a document. There are three audience modes: global, teams, and space. If you do not pass any audience parameters, the document defaults to global visibility.
Global (default)
To make a document visible to everyone in the organisation, simply omit the audience and space_id parameters from your request. Global is the default — you do not pass "global" as a value.
Teams
To restrict a document to specific teams, pass audience[type] as teams and list the team IDs. A document can belong to multiple teams.
curl -X POST "https://api.workvivo.com/v1/documents/upload" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json" \
-F "file=@/path/to/team-policy.pdf" \
-F "location_id=65" \
-F "label=Engineering Team Policy" \
-F "user_id=1042" \
-F "audience[type]=teams" \
-F "audience[teams][0]=13" \
-F "audience[teams][1]=27"
{
"audience": {
"is_global": false,
"spaces": [],
"teams": [
{
"id": 13,
"name": "Cork",
"permalink": "https://yourcompany.workvivo.com/directory/teams/13"
},
{
"id": 27,
"name": "Dubai",
"permalink": "https://yourcompany.workvivo.com/directory/teams/27"
}
]
}
}
Space
To assign a document to a space, pass the space_id parameter. A document can belong to only one space. Do not pass audience[type] when using space_id — the document's visibility is determined by the space membership.
curl -X POST "https://api.workvivo.com/v1/documents/upload" \
-H "Authorization: Bearer $WORKVIVO_TOKEN" \
-H "Workvivo-Id: $WORKVIVO_ORG_ID" \
-H "Accept: application/json" \
-F "file=@/path/to/space-doc.pdf" \
-F "location_id=67" \
-F "label=New Hires Welcome Pack" \
-F "user_id=1042" \
-F "space_id=83"
{
"audience": {
"is_global": false,
"spaces": [
{
"id": 83,
"name": "New Hires",
"permalink": "https://yourcompany.workvivo.com/spaces/83"
}
],
"teams": []
}
}
Important: You cannot combine
space_idwithaudience[type]. If you pass both, the API returns an error. When usingspace_id, thelocation_idmust refer to a folder within that space.
Limits
| Limit | Value |
|---|---|
| Maximum file size (direct upload) | 10 MB |
| Maximum file size (asset flow) | 5 GB |
| Upload URL expiration | 60 minutes |
| Pagination default page size | 10 items |
Production checklist
Before deploying your integration:
- Store the API key in a secrets manager and grant only the required permissions.
- Implement retry logic for the cloud storage upload step — large file transfers can be interrupted by network issues.
- Always confirm the upload after a successful cloud storage transfer (unless your environment uses automatic confirmation).
- Handle the upload URL expiration gracefully — if the URL expires, request a new one rather than retrying the expired URL.
- Map folder names to location IDs at the start of your migration, since folder names are not unique.
- When assigning documents to teams or spaces, verify the audience configuration before uploading — passing conflicting audience parameters (e.g., both
space_idandaudience[type]) will result in an error. - Attribute documents to the correct user with
user_idoruser_external_id. - Monitor the assets list for orphan assets that may indicate failed uploads in your pipeline.