Overview
In this article, you’ll find tutorials on managing project documents via the PlanRadar API, including creating documents, retrieving files and folders (with filters), uploading and deleting document versions, moving documents between folders or to trash, restoring or permanently deleting items, and handling approval requests.
Find other tutorials in Open API Overview > API Tutorials.
Create a Document
To create a document and attach it to a project, use the API:
POST /api/v2/{customer_id}/projects/{project_id}/dms/nodes
In your curl request, include the data you want to add to your document. These include parent UUID, file name, data type, … etc.
A sample curl request:
curl -X 'POST' \
'https://planradar.com/api/v2/{customer_id}/projects/{project_id}/dms/nodes' \
-H 'accept: application/json' \
-H 'X-PlanRadar-API-Key: {string}
-H 'Content-Type: multipart/form-data' \
-F 'data[attributes][parent_id]=data[attributes][uuid]' \
-F 'data[attributes][file]=@Bill of quantities.pdf;type=application/pdf' \
-F 'data[attributes][type]=data[attributes][google_drive]' \
-F 'data[attributes][file_content_type]=data[attributes][pdf]' \
-F 'data[attributes][name]=data[attributes][Bill of quantities]'
The file will be created with the specified data and a response body will show the data of the newly created document.
A sample response body:
{
"data": {
"id": "xlyp",
"type": "node",
"attributes": {
"id": "xlyp",
"name": "Bill of quantities.pdf",
"uuid": "string",
"path": "2016",
"original-path": null,
"deleted-at": null,
"node-type": "file"
},
"relationships": {
"file": {
"data": {
"id": "abmy",
"type": "dms-attachment"
}
}
}
},
"included": [{}]
}
Retrieve All Documents
PlanRadar now includes a document management feature that allows users to administrate documents attached to their projects. The document management feature includes a number of functions; such as creating documents, editing documents, deleting documents, etc.
With PlanRadar, you can use APIs to retrieve all the documents attached to a certain project. This API uses different nodes to retrieve documents with different criteria.
Retrieving All the Files/Folders in a Project
First, to retrieve all documents related to a certain project, you do not need to use any nodes. Just use the simple API:
GET /api/v2/{customer_id}/projects/{project_id}/dms/nodes
The response returns all the files and folders attached to the project. Here’s a sample response body:
{
"data": [{
"id": "yjkg",
"type": "node",
"attributes": {
"id": "yjkg",
"name": "Instructional sheet on work safety.doc",
"uuid": "string",
"path": "1068",
"node-type": "file",
"is-deletable": true
}
}],
"included": [{}
]
}
Retrieving Files with Certain Criteria
To retrieve a certain file/s or a file/s with certain criteria, here are the nodes that allow you to do so:
First node: parent_id={folder_uuid} allows retrieving files and folders in a certain folder. An API that contains a node for retrieving files and folders in a certain folder would look like this:
/api/v2/{customer_id}/projects/{project_id}/dms/nodes?parent_id={folder_uuid}
The response body will return all the files and/or folders included in the folder for which you added the UUID in your node.
A sample is:
{
"data": [{
"id": "yjnm",
"type": "node",
"attributes": {
"id": "yjnm",
"name": "fixes",
"uuid": "string",
"path": "810.868",
"node-type": "folder",
"parent-uuid": "string"
}
},
{
"id": "wngg",
"type": "node",
"attributes": {
"id": "wngg",
"name": "Hotel_Hotep_16_01_2023.xlsx",
"uuid": "string",
"path": "810.812",
"node-type": "file",
"parent-uuid": "string"
}
},
{
"id": "nydn",
"type": "node",
"attributes": {
"id": "nydn",
"name": "updates.pdf",
"uuid": "string",
"path": "810.811",
"node-type": "file",
"parent-uuid": "string"
}
}
]
}
Second node: only_folders=true retrieves only folders. An API that contains a node for retrieving only folders would look like this:
/api/v2/{customer_id}/projects/{project_id}/dms/nodes?only_folders=true
The response body will return only folders attached to your project. It will not retrieve any files; only folders. A sample response body is below:
{
"data": [{
"id": "zzbp",
"type": "node",
"attributes": {
"id": "zzbp",
"name": "fixes",
"uuid": "a8c1dc1f-0e46-4d02-904f-d5fc8d268a2f",
"path": "810",
"original-path": null,
"deleted-at": null,
"node-type": "folder",
"trashed-at": null,
"bookmarked": false,
"parent-uuid": null,
"searchable-breadcrumb": null,
"is-deletable": true
},
"relationships": {
"file": {
"data": null
},
"author": {
"data": {
"id": "oll",
"type": "users"
}
}
}
}]
}
Third node: trashed=true retrieves deleted files and folders. Here’s a sample API containing a node for retrieving deleted files and folders:
/api/v2/{customer_id}/projects/{project_id}/dms/nodes?trashed=true
The response body will list all files and folders in the trash. An example response body:
{
"data": [{
"id": "gpoj",
"type": "node",
"attributes": {
"id": "gpoj",
"name": "sample txt.txt",
"uuid": "string",
"path": "805",
"original-path": "805",
"node-type": "folder",
"trashed-at": "2023-02-28T12:59:28.503Z",
"parent-uuid": null,
"is-deletable": true
}
},
{
"id": "olzx",
"type": "node",
"attributes": {
"id": "olzx",
"name": "user_logs.xlsx",
"uuid": "string",
"path": "802",
"original-path": "802",
"node-type": "file",
"trashed-at": "2023-02-28T12:59:36.198Z",
"parent-uuid": null,
"is-deletable": true
}
}
]
}
Fourth node: exclude_ticket_documents=true retrieves all files except for those attached to tickets. In the response body, only files and folders that are not attached to tickets.
Fifth node: bookmarked=true retrieves your favorite files and/or folders.
Adding Versions
To add a new version to an existing document, use the API:
POST /api/v2/{customer_id}/projects/{project_id}/dms/nodes/{file_uuid}/versions
In the CURL request, include the new version:
curl -X 'POST' \
'https://planradar.com/api/v2/{customer_id}/projects/{project_id}/dms/nodes/{file_uuid}/versions' \
-H 'accept: application/json' \
-H 'X-PlanRadar-API-Key: {string}' \
-H 'Content-Type: multipart/form-data' \
-F 'data[attributes][file]=@Bill of quantities.pdf;type=application/pdf'
In the response, all the new version data will be listed.
A sample response:
{
"data": {
"id": "knzl",
"type": "file-attachment-versions",
"attributes": {
"id": "knzl",
"uuid": "string",
"file-data": {
"id": "ymjxnx/poagan/nodes/2016/2/Bill_of_quantities.pdf",
"storage": "store",
"metadata": {
"filename": "Bill of quantities.pdf",
"size": 78624,
"mime_type": "application/pdf",
"is_360": false
}
}
}
}
} Deleting a Version
To delete a version that has been already uploaded, make sure that the version is not currently in use as versions that are in user cannot be deleted. Use the API:
DELETE /api/v2/{customer_id}/projects/{project_id}/dms/nodes/{file_uuid}/versions/{version_uuid}
In your request, add the file UUID as well as the version UUID.
A sample cURL request:
curl -X 'DELETE' \
'https://planradar.com/api/v2/{customer_id}/projects/{project_id}/dms/nodes/{file_uuid}/versions/{version_uuid}' \
-H 'accept: application/json' \
-H 'X-PlanRadar-API-Key: {string}'
The response will return the data of the version that got deleted as well as the user who deleted it.
A sample response:
{
"data": {
"id": "wpod",
"type": "file-attachment-versions",
"attributes": {
"id": "wpod",
"uuid": "string",
"file-data": {
"metadata": {
"filename": "Bill of quantities.pdf",
"screenshot_url": "string",
"has_valid_screenshot": true
}
},
"customer-id": "string",
"project-id": "string",
"revision": 1,
"node-id": "xlyp",
"is-deleted": true,
"created-at": "2023-03-06T08:07:33.423Z",
"updated-at": "2023-03-13T12:36:48.265Z",
"deleted-by-id": "oll"
}
}
}
Creating an Approval Request
To request approval for a single file, use the API:
/api/v2/{customer_id}/projects/{project_id}/approval_requests/validate_file/{file_uuid}
In the request, include the reviewer as well as the message to be attached to the request of approval.
{
"data": {
"id": "string",
"type": "approval-requests",
"attributes": {
"id": "string",
"uuid": "string",
"customer-id": "string",
"project-id": "string",
"author-id": "string",
"subject": "Approval request for 1 item",
"due-date": "2023-03-25T00:00:00.000Z",
"status": "pending",
"request-type": "file",
"created-at": "2023-03-19T09:27:29.008Z",
"updated-at": "2023-03-19T09:27:29.008Z"
}
}
}
For requesting approval for more than one file, use the API:
POST /api/v2/{customer_id}/projects/{project_id}/approval_requests/validate_nodes?search=&search_in_all_subfolders=true
In your request, include the nodes UUIDs.
attributes:
{nodes_to_validate: ["file_uuid", "file_uuid"]}
0: "fad9a38a-214c-4287-830a-7378877deffc"
1: "279cc52c-4f61-49eb-a0fb-e72c86b167a5"
filter: {}
In the response, the UUIDs of the files pending approval. Here’s a sample response:
{
"data": null,
"meta": {
"nodes_has_files_in_pending_request": ["{node_uuid}", "{node_uuid}", "{node_uuid}"],
"valid_files_count": 0
}
}
Show All Approval Requests
To show all approval requests in any of your projects, use the API:
GET /api/v2/{customer_id}/projects/{project_id}/approval_requests
In the response, all the data for the pending approval requests will be listed:
{
"data": [{
"id": "string",
"type": "approval-requests",
"attributes": {
"id": "string",
"uuid": "string",
"customer-id": "string",
"project-id": "string",
"author-id": "string",
"subject": "Approval request for 1 item",
"message": "Kindly approve",
"due-date": "2023-03-29T00:00:00.000Z",
"status": "pending",
"request-type": "file"
}
}, {
"id": "string",
"type": "approval-requests",
"attributes": {
"id": "string",
"uuid": "string",
"customer-id": "string",
"project-id": "string",
"author-id": "string",
"subject": "Approval request for 1 item",
"message": "Kindly approve",
"due-date": "2023-04-09T00:00:00.000Z",
"status": "pending",
"request-type": "file"
}
}]
}
Moving Files from One Folder to Another
When moving files from one folder to another, all that needs to be done is changing the parent ID of the files. To do so, use the below API:
PATCH /api/v2/{customer_id}/projects/{project_id}/dms/nodes?parent_id={uuid}
In your request URL, the UUID of the current parent folder is a filter that selects the files to be moved. In this case, all files/folders with that parent ID will be moved to the destination folder.
Your request data should include the original parent UUID as well as the destination folder UUID and , of course, your PlanRadar API key.
However, to move files to the “all files“ section instead of a certain destination folder, do not include a UUID for a destination folder.
In the response, all files that are moved to a new folder will be listed and the new parent UUID will be shown.
Here’s a sample response:
{
"data": [{
"id": "jzex",
"type": "node",
"attributes": {
"id": "jzex",
"name": "construction diary.pdf",
"uuid": "c016adaa-ce97-4bba-87bf-59ebbde16e8b",
"path": "2828.864",
"node-type": "file",
"parent-uuid": "a8c1dc1f-0e46-4d02-904f-d5fc8d268a2f"
}
},
{
"id": "olox",
"type": "node",
"attributes": {
"id": "olox",
"name": "construction diary2.pdf",
"uuid": "d0662db1-ae86-4946-a3f0-4835dd87340d",
"path": "2828.836",
"node-type": "file",
"parent-uuid": "a8c1dc1f-0e46-4d02-904f-d5fc8d268a2f"
}
}
]
}
Moving Files to Trash
To move more than one file in your document to the trash, use the API, including your customer ID, the project ID, and the folders'/files' UUIDs:
PUT api/v2/{customer_id}/projects/{project_id}/dms/nodes/trash?filter[uuid][][predicate]=in&filter[uuid][][value][]={file_uuid}&show_virtual=false
In the response, the trashed files will be listed, as the sample below:
{
"data": [{
"id": "dgax",
"type": "node",
"attributes": {
"id": "dgax",
"name": "Architect_Planner_Template-Site diary.pdf",
"uuid": "string",
"path": "2826",
"original-path": "2826",
"deleted-at": null,
"created-at": "2023-03-06T13:24:10.087Z",
"updated-at": "2023-03-07T10:51:16.000Z",
"node-type": "file",
"trashed-at": "2023-03-07T10:51:15.995Z"
},
"relationships": {}
}]
}
Restoring Files from Trash
To restore all files and/or folders from trash to their original place, use the API:
PUT /api/v2/{customer_id}/projects/{project_id}/dms/nodes/restore
This API restores all files and/or folders in the trash.
To restore a files in a certain folder in the trash add the UUID of the folder to your API:
PUT /api/v2/{customer_id}/projects/{project_id}/dms/nodes/restore?parent_id={folder_uuid}
In the response, all restored files and/or folders will be listed.
Deleting Files
For deleting files/folders permanently, these files/folders need to be already trashed.
You can delete all files/folders in your trash file by using the API:
DELETE /api/v2/{customer_id}/projects/{project_id}/dms/nodes/delete
However, if you want to delete only the files that are included in a folder by including the folder’s UUID into your API request:
DELETE /api/v2/{customer_id}/projects/{project_id}/dms/nodes/delet?parent_id={folder_id}
You can also delete only certain files/folders permanently by adding the files' UUIDs:
DELETE /api/v2/{customer_id}/projects/{project_id}/dms/nodes/delete?filter[uuid][][predicate]=in&filter[uuid][][value][]={file_id}
The response will show all deleted files:
{
"data": [{
"id": "dgax",
"type": "node",
"attributes": {
"id": "dgax",
"name": "Architect_Planner_Template-Site diary.pdf",
"uuid": "8d33c29d-bcc8-45ba-80bc-46082e49c940",
"path": "2826",
"original-path": "2826",
"deleted-at": "2023-03-07T11:59:35.864Z",
"created-at": "2023-03-06T13:24:10.087Z",
"updated-at": "2023-03-07T11:59:35.870Z",
"node-type": "file",
"trashed-at": "2023-03-07T10:51:15.995Z",
"is-deletable": true
}
}]
}
Retrieve All Ticket Attachments
To retrieve all files attached to tickets across an entire project in a single API call, use the API:
GET /api/v2/{customer_id}/projects/{project_id}/dms/nodes/ticket_attachments
Instead of querying attachments per ticket individually, this endpoint returns all ticket attachments across the entire project at once. The response includes both the attachment nodes and related data such as ticket information, file metadata, and download links.
You can refine the results using the following query parameters:
Pagination: Use pagesize to control how many results are returned per page (default: 100, max: 500). Use page to specify which page to retrieve (default: 1). If the number of returned results equals your pagesize, fetch the next page by incrementing page. If it is less, you have reached the last page.
Sorting: Use sort=created-at for ascending order (oldest first) or sort=-created-at for descending order (newest first).
Search: Use search={filename} to filter results by file name. Partial matches are supported. If no match is found, the response returns an empty data array and a count of 0.
Sync filter: Use last_sync_date={unix_timestamp} to retrieve only attachments updated after a specific point in time. The value must be a Unix timestamp in seconds.
A sample curl request:
curl -X 'GET' \
'https://www.planradar.com/api/v2/{customer_id}/projects/{project_id}/dms/nodes/ticket_attachments?last_sync_date={unix_timestamp}&pagesize=500&sort=-created-at' \
-H 'accept: application/json' \
-H 'X-PlanRadar-API-Key: {string}'Pagination: Use pagesize to control how many results are returned per page (default: 100, max: 500). Use page to specify which page to retrieve (default: 1). If the number of returned results equals your pagesize, fetch the next page by incrementing page. If it is less, you have reached the last page.
Each entry in data represents a single ticket attachment and includes a relationships object linking to the associated ticket, file, and author in the included array.
A sample response body:
{
"data": [
{
"id": "naynjej",
"type": "node",
"attributes": {
"id": "naynjej",
"uuid": "eb0c70ff-8ad5-4415-adc2-c5d458102fc4",
"name": "Bill of quantities.pdf",
"node-type": "virtual_document",
"created-at": "2023-06-16T11:18:42.900Z",
"updated-at": "2023-06-16T11:18:42.900Z",
"deleted-at": null,
"trashed-at": null,
"parent-uuid": "string",
"is-deletable": true,
"project-id": "string",
"customer-id": "string"
},
"relationships": {
"file": {
"data": { "id": "string", "type": "dms-attachment" }
},
"ticket": {
"data": { "id": "string", "type": "tickets" }
},
"author": {
"data": { "id": "string", "type": "users" }
}
}
}
],
"included": [
{
"id": "string",
"type": "dms-attachment",
"attributes": {
"uuid": "string",
"title": "Bill of quantities.pdf",
"ticket-uuid": "string",
"file-data": {
"metadata": {
"size": 29098,
"filename": "Bill of quantities.pdf",
"mime_type": "application/pdf"
}
},
"url": "https://prd-planradar.s3-eu-central-1.amazonaws.com/...",
"download-link": "https://www.planradar.com/api/v2/{customer_id}/projects/{project_id}/dms/nodes/{node_id}/versions/{version_id}/download",
"revision": 1
}
}
],
"meta": {
"count": 207,
"page": "1",
"pagesize": "500"
}
}
To match an attachment to its ticket, use data[].relationships.ticket.data.id to find the corresponding entry in included where type is tickets. That entry contains the ticket's sequential-id, subject, status-id, and approval-status. Similarly, use data[].relationships.file.data.id to find the corresponding dms-attachment entry in included, which contains the file URL, download link, and metadata.
Note that the pre-signed S3 URLs returned in the url field expire after 2 hours. For long-running workflows, use the download-link instead, as it is authenticated via your API key and does not expire.
Create a Ticket Attachment
To upload a new attachment to a specific ticket, use the API:
POST /api/v2/{customer_id}/projects/{project_id}/dms/nodes/ticket_attachments/{ticket_uuid}
In your request, include the file data and, optionally, a caption. The file can be uploaded either as a base64-encoded Data URI or as multipart form data.
Upload via Data URI (Base64)
To upload a file as a base64-encoded Data URI, set the Content-Type header to application/json and include the MIME type directly in the data string using the format data:{mime_type};base64,{base64_string}. The attachment-name field is required when using this method.
A sample curl request:
curl -X 'POST' \
'https://www.planradar.com/api/v2/{customer_id}/projects/{project_id}/dms/nodes/ticket_attachments/{ticket_uuid}' \
-H 'accept: application/json' \
-H 'X-PlanRadar-API-Key: {string}' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"attributes": {
"attachment": "data:image/png;base64,{base64_string}",
"attachment-name": "site_photo_001.png",
"caption": "Foundation inspection - North wall"
}
}
}'
The attachment will be created and linked to the specified ticket. The response body will include the attachment metadata.
Upload via Multipart Form Data
To upload a file using multipart form data, set the Content-Type header to multipart/form-data and pass the file directly using the @ prefix. This method is more efficient for larger files.
A sample curl request:
curl -X 'POST' \
'https://www.planradar.com/api/v2/{customer_id}/projects/{project_id}/dms/nodes/ticket_attachments/{ticket_uuid}' \
-H 'accept: application/json' \
-H 'X-PlanRadar-API-Key: {string}' \
-H 'Content-Type: multipart/form-data' \
-F 'data[attributes][attachment]=@site_photo_001.png' \
-F 'data[attributes][caption]=Foundation inspection - North wall'
The file will be uploaded and linked to the specified ticket. A sample response body:
{
"data": {
"id": "string",
"type": "attachments",
"attributes": {
"uuid": "string",
"name": "site_photo_001.png",
"content-type": "image/png",
"size": 284500,
"caption": "Foundation inspection - North wall",
"ticket-uuid": "string",
"field-id": null,
"created-at": "2026-04-23T10:30:00.000Z"
}
}
}
Comments
0 comments
Please sign in to leave a comment.