Files API
Operations on a single file. Files and folders share the resource model, the listing endpoint, version history, and locking described in Common conventions; this page covers what applies to files only.
Base URL and scope
/api/external/v1/files
Read operations need api:read. Upload, delete, rename, move, and copy additionally need api:write.
Download a file for the Intellect viewer
This endpoint loads the document body when Intellect previews or opens a file, so it checks the VIEW permission, not the DOWNLOAD permission. For an ordinary download, issue a download token and hand it to the browser as described in Download through the browser.
GET /api/external/v1/files/{resourceSeq}/download
| Parameter | Type | Required | Description |
|---|---|---|---|
resourceSeq | long | Yes | Path parameter - the file to load |
ss | integer | No | Sharing user seq |
curl -X GET "https://drive.example.com/api/external/v1/files/{resourceSeq}/download?ss=21" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY"
On success the response is the file itself, not the JSON envelope:
HTTP 200 OK
Content-Type: {contentType}
Content-Disposition: attachment; filename={fileName}
Content-Length: {byte}
Download through the browser
When the download should be performed by the user's browser rather than your server, issue a short-lived token first and hand the returned path to the browser. The token carries the authorization, so the second request needs no API Key.
GET /api/external/v1/files/{resourceSeq}/download-token
| Parameter | Type | Required | Description |
|---|---|---|---|
resourceSeq | long | Yes | Path parameter - the file to download |
ss | integer | No | Sharing user seq |
{
"result": true,
"code": 200,
"message": "OK",
"data": {
"linkUrl": "/files/download?dt={token}"
}
}
linkUrl is a relative path. The browser requests it against the domain the user is signed in to, so the second
step is not an /api/external/v1 call and carries no Authorization header:
GET https://drive.example.com/files/download?dt={dt}
The response is the file stream with the same Content-Type, Content-Disposition, and Content-Length headers as
above. Calling /api/external/v1/files/download directly instead requires an API Key, which does not fit this browser
flow.
To download several resources in one archive, use the multi-resource download described in Common conventions.
Preview a file
GET /api/external/v1/files/{resourceSeq}/view
| Parameter | Type | Required | Description |
|---|---|---|---|
resourceSeq | long | Yes | Path parameter - the file to preview |
ss | integer | No | Sharing user seq |
Returns the original file as a stream, not a rendered preview:
HTTP 200 OK
Content-Type: {contentType}
Content-Disposition: attachment; filename={fileName}
Content-Length: {byte}
Upload a file
api:writePOST /api/external/v1/files
Send the file as multipart/form-data.
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to upload |
pfs | long | No | Parent folder seq; used only when st is duplicate, and the file goes to the root when it is omitted. Required when a shared user uploads |
ss | integer | No | Sharing user seq; null when uploading to your own drive |
st | enum | No | Save type, case-insensitive - duplicate (default) adds a new file with a copy number, version adds a new version of rs, overwrite replaces rs |
rs | long | No | Target resource seq; required when st is version or overwrite, otherwise the request fails with RESOURCE_014 |
curl -X POST "https://drive.example.com/api/external/v1/files" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY" \
-H "Content-Type: multipart/form-data" \
-F "file=@report.docx" \
-F "pfs=1"
{
"result": true,
"code": 201,
"message": "Created",
"data": {
"resourceSeq": 283,
"resourceName": "report.docx"
}
}
version and overwrite act on the rs file, so pfs is not used with them. Uploading beyond the account quota
fails with QUOTA_003.
Rename a file
api:writePATCH /api/external/v1/files/{resourceSeq}/rename
| Parameter | Type | Required | Description |
|---|---|---|---|
resourceSeq | long | Yes | Path parameter - the file to rename |
rn | string | Yes | New name |
ncp | enum | No | Name-collision policy, case-insensitive - NONE (default) rejects a duplicate name with RESOURCE_006, AUTO appends a number |
pfs | long | No | Parent folder seq; required with ncp=NONE for a file that is not in the root. The duplicate-name check runs against this folder, and against the root when it is omitted. Not used when ncp is AUTO |
ss | integer | No | Sharing user seq |
curl -X PATCH "https://drive.example.com/api/external/v1/files/{resourceSeq}/rename" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY" \
-H "Content-Type: application/json" \
-d '{"ss": 21, "rn": "quarterly-report", "ncp": "AUTO"}'
The response returns the name that was actually applied in data.name, without the extension - an rn of
test11.docx returns a name of test11. It differs from rn when AUTO resolved a collision.
Move a file
api:writePOST /api/external/v1/files/{resourceSeq}/move
| Parameter | Type | Required | Description |
|---|---|---|---|
resourceSeq | long | Yes | Path parameter - the file to move |
ts | integer | No | Target user seq; required when the caller did not create the share |
pfs | long | No | Destination folder seq; required when the caller did not create the share |
ss | integer | No | Sharing user seq |
curl -X POST "https://drive.example.com/api/external/v1/files/{resourceSeq}/move" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY" \
-H "Content-Type: application/json" \
-d '{"ts": 1, "pfs": 367}'
Copy a file
api:writePOST /api/external/v1/files/{resourceSeq}/copy
| Parameter | Type | Required | Description |
|---|---|---|---|
resourceSeq | long | Yes | Path parameter - the file to copy |
ts | integer | No | User seq to copy to; null when the caller provides the share |
pfs | long | No | Destination folder seq; required when it differs from the sharing user |
ss | integer | No | Sharing user seq; null when the caller provides the share |
The optional Accept-Language header sets the language of the copy's name:
| Language | Copy name |
|---|---|
ko | {name}의 사본.{ext} |
en | Copy of {name}.{ext} |
es | Copia de {name}.{ext} |
Returns 201 Created with the new resourceSeq. Copying beyond the account quota fails with QUOTA_003.
Delete a file
api:writePATCH /api/external/v1/files/{resourceSeq}/delete
| Parameter | Type | Required | Description |
|---|---|---|---|
resourceSeq | long | Yes | Path parameter - the file to delete |
Only a file you own can be deleted, which is why there is no ss parameter. A file shared with you fails with
RESOURCE_005, and a locked file fails with RESOURCE_LOCK_ASPECT_002.
The file moves to the trash rather than being removed. See Users for listing, restoring, and permanently deleting trashed items.
Create a new Office document
api:writeCreates an empty Word, Spreadsheet, Presentation, or note document from a built-in template, without uploading a file.
POST /api/external/v1/documents
| Parameter | Type | Required | Description |
|---|---|---|---|
tt | enum | Yes | Template type - word, excel, ppt, or note |
pfs | long | No | Parent folder seq. In your own drive, the document goes to the root when it is omitted. Required when creating in a shared folder (ss is not you); omitting it then fails with PERMISSION_002 |
ss | integer | No | Sharing user seq; null when creating in your own drive |
The Accept-Language header selects both the template and the file name. Korean (ko), English (en), and Spanish
(es) templates are available; any other language fails with FILE_002 because no template exists for it.
| Language | File names |
|---|---|
ko | 새 문서.docx, 새 프레젠테이션.pptx, 새 스프레드시트.xlsx, 새 노트.tfi |
en | New Document.docx and the matching English names for the other types |
es | Nuevo documento.docx and the matching Spanish names for the other types |
If the same name already exists in the target folder, a numbered name is used, such as 새 문서 (1).docx.
curl -X POST "https://drive.example.com/api/external/v1/documents" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY" \
-H "Content-Type: application/json" \
-d '{"ss": 21, "pfs": 5, "tt": "word"}'
{
"result": true,
"code": 201,
"message": "Created",
"data": {
"resourceSeq": 4699,
"userSeq": 1
}
}
data.userSeq is the owner of the drive the document was created in. When you create in a shared folder, it is the
sharing user, not the caller.
The target folder is checked for the UPLOAD permission. Failures use the common failure envelope:
| Status | errorCode | Cause |
|---|---|---|
| 400 | REQUEST_001 | Request validation failed |
| 403 | PERMISSION_001 | The target folder is not shared with the caller |
| 403 | PERMISSION_002 | The caller lacks the UPLOAD permission, or pfs is missing for a shared folder |
| 404 | RESOURCE_005 | The target folder does not exist |
| 404 | FILE_002 | No template for the requested language |
| 400 | QUOTA_003 | Not enough storage capacity |
{
"result": false,
"code": 404,
"errorCode": "FILE_002",
"message": "{message}"
}
Editor session endpoints
The web-office endpoints back a browser editing session: the editor reads document metadata, fetches the file, locks
it while editing, writes it back, and unlocks it. Use them when you host the Thinkfree editor yourself. For embedding
an editor through a supported SDK instead, see Editor SDK.
GET /api/external/v1/web-office/{loginUserSeq}/info
GET /api/external/v1/web-office/{loginUserSeq}/get
POST /api/external/v1/web-office/{loginUserSeq}/lock
PUT /api/external/v1/web-office/{loginUserSeq}/put
POST /api/external/v1/web-office/{loginUserSeq}/unlock
loginUserSeq is supplied by the caller, not derived from the API Key. The server checks only that the named user
belongs to the same tenant as the key - it does not check that the user matches the key's own account. Any key holder
can therefore act as any user in that tenant through these endpoints. Treat a key that can reach them as a
tenant-wide credential, and do not pass a browser-supplied loginUserSeq through without deciding server-side who the
caller is allowed to act as.
All five are exempt from the scope check because they serve the editor session flow. A key holding only api:read can
call lock, put, and unlock, which change stored content.
Common parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
loginUserSeq | long | Yes | Path parameter - the user the session acts as |
docId | string | Yes | Document identifier, formed as the resource owner seq and the resource seq joined by a hyphen ({ownerUserSeq}-{resourceSeq}, such as 21-512) |
app | enum | Yes | Editor surface, such as WORD_EDITOR, CELL_VIEWER, or SHOW_WATCHER; required by info, get, and put |
user_id | string | Yes | Signed-in account name; required by info, get, and put |
lang | string | No | Session language, such as ko_KR. Sets the language (ko, en, es) of the permission-denied message and of an anonymous user's display name |
The app value names both the application and the mode, giving 15 values. Applications are WORD, CELL, SHOW, HWP,
and PDF; modes are EDITOR, VIEWER, and WATCHER.
An *_EDITOR value is checked for the EDIT permission; *_VIEWER and *_WATCHER are checked for the VIEW permission.
This applies to info, get, and put, and the check is skipped for the document owner. An anonymous user who
arrives through a public link can open only documents shared with PUBLIC_LINK.
Read document metadata
GET /api/external/v1/web-office/{loginUserSeq}/info
| Parameter | Type | Required | Description |
|---|---|---|---|
type | enum | No | Request type, case-insensitive - open, download (original file), pdf_download (PDF conversion), or print. download and pdf_download are recorded as downloads in the audit log |
Returns an XML document with the root element <property>, not JSON. Permissions are checked when the request is
made, and a caller without them gets HTTP 403. The read, write, and locked fields do not reflect the actual
permission or lock state.
| Field | Description |
|---|---|
type | Always file |
name | File name, in CDATA |
modified | Last modified time, epoch milliseconds |
read, write | Always true |
locked | Always false |
size | File size in bytes |
fullName | Display name of the user |
userCursorName | The user_id sent in the request; for an anonymous user, an anonymous animal name in lang |
profileImage | Profile image of the user |
cursorColor | Cursor color, random on every request |
revision | Same value as modified |
currentVersionNumber | Current version number |
curl -X GET "https://drive.example.com/api/external/v1/web-office/{loginUserSeq}/info?app=WORD_EDITOR&user_id={user_id}&docId=21-512&lang=ko_KR&type=open" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY"
<property>
<type>file</type>
<name><![CDATA[report.docx]]></name>
<modified>1758240000000</modified>
<read>true</read>
<write>true</write>
<locked>false</locked>
<size>24576</size>
<fullName>{fullName}</fullName>
<userCursorName>{user_id}</userCursorName>
<profileImage>{profileImage}</profileImage>
<cursorColor>{cursorColor}</cursorColor>
<revision>1758240000000</revision>
<currentVersionNumber>3</currentVersionNumber>
</property>
A missing resource returns HTTP 200 with the plain-text body NotExist, so check the body as well as the status code.
Fetch the document
GET /api/external/v1/web-office/{loginUserSeq}/get
Streams the file as application/octet-stream. A *_VIEWER request is recorded in the audit log as a preview; any
other app is recorded as opening the document.
curl -X GET "https://drive.example.com/api/external/v1/web-office/{loginUserSeq}/get?app=WORD_EDITOR&user_id={user_id}&docId=21-512" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY" \
-o report.docx
Store the document
PUT /api/external/v1/web-office/{loginUserSeq}/put
Send the document body as application/octet-stream. The response is the docId as plain text.
| Parameter | Type | Required | Description |
|---|---|---|---|
app, user_id, docId | - | Yes | See Common parameters |
size | long | Yes | Size of the uploaded document in bytes |
type | enum | No | Save reason, case-insensitive. save, autoPut, close, a missing value, or any other value saves the body as a new version. saveAs and autoSave save nothing and only return the docId |
openTimestamp | long | No | Accepted but unused |
Unlike the other web-office endpoints, a missing or malformed required parameter fails with 400 REQUEST_001 as
JSON. Exceeding the storage quota returns 500 Internal Server Error, not QUOTA_003.
curl -X PUT "https://drive.example.com/api/external/v1/web-office/{loginUserSeq}/put?app=WORD_EDITOR&user_id={user_id}&docId=21-512&size=24576&type=save" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary "@report.docx"
Lock and unlock the document
POST /api/external/v1/web-office/{loginUserSeq}/lock
POST /api/external/v1/web-office/{loginUserSeq}/unlock
Both take only docId; app, user_id, and lang are not needed. They return 200 OK with no body. If no resource
matches docId, they fail with HTTP 400 and a plain-text message.
curl -X POST "https://drive.example.com/api/external/v1/web-office/{loginUserSeq}/lock?docId=21-512" \
-H "Authorization: Bearer replace-with-your-USER-API-KEY"
Editor session errors
Several of these failures carry no errorCode, or no body at all, so they cannot be matched against
Errors. Check both the status code and the body.
| Case | Response |
|---|---|
Missing required parameter or invalid app - info, get, lock, unlock | HTTP 500, no body |
Missing or malformed required parameter - put | 400 REQUEST_001, JSON |
No user matches loginUserSeq | 404 USER_005, JSON |
| The user belongs to a different tenant from the API Key | 403 AccessDenied, plain text |
Malformed docId | 500 Internal Server Error, JSON without errorCode |
| No permission for the document | 403, plain-text permission-denied message in lang |
Missing resource - info | 200 NotExist, plain text |
Missing resource, file read or save failure, or quota exceeded - get, put | 500 Internal Server Error, JSON without errorCode |
Missing resource - lock, unlock | 400, plain-text lock or unlock failure message |
Errors
Failures on the files and documents endpoints use the common failure envelope:
{
"result": false,
"code": 400,
"errorCode": "QUOTA_003",
"message": "{message}"
}
| Status | errorCode | Cause |
|---|---|---|
| 400 | RESOURCE_001 | Delete failed |
| 404 | RESOURCE_005 | Resource not found |
| 409 | RESOURCE_006 | Duplicate name |
| 400 | RESOURCE_014 | rs is missing |
| 423 | RESOURCE_LOCK_ASPECT_002 | The resource is locked |
| 400 | QUOTA_003 | Not enough storage capacity |
| 500 | RESPONSE_001 | Server error |
See Errors for the full list of codes. The web-office endpoints are covered in
Editor session errors.