Create a document
Upload the file content to a temporary upload on the server first, then create the document in one call that names the upload, the properties and the permissions, and checks the new document in.
Calls
- UploadTempFile: send the content in
blocks. The first call has
upload_id0,offset0 and the file'stotal_size_in_bytes, and returns theupload_idof a new upload; later calls pass that ID and theoffsetof their block. The samples send blocks of 4 MiB. - UploadTempFileCommit with
upload_idandlogical_size, the number of bytes uploaded. An upload that is not committed cannot be used. - AddObjectWithFiles with:
| Field | Value |
|---|---|
object_type_id |
0 (Document) |
properties |
0 Name or title (text), 100 Class (lookup, value list 1), 22 Single file (boolean true), and the properties the class requires |
acl |
custom_component with has_named_acl true and named_acl_id = the named ACL to use; the samples use -1 |
files |
type OBJ_FILE_SOURCE_TYPE_UPLOADED_FILE, data.uploaded_file with upload_id, title, extension (no dot) and size |
check_in |
true; false leaves the new document checked out to you |
AddObjectWithFiles consumes the upload. The response's created_object.object_version has the
new object's object_info.obj_id and the version information with the file.
Permissions
Always send acl. An object created without one gets an empty access control list: nobody
can see it, not even the user who created it, and the call still succeeds. The samples link the
new document to a named ACL with has_named_acl: true and named_acl_id: -1. In a default vault,
-1 is "Full control for all internal users". Pick the named ACL that your application needs; the
IDs of the others differ from vault to vault. See Permissions.
Code
Python
The upload:
# Upload the content to a temporary file on the server, in blocks. The first call
# (upload_id 0) starts the upload; the next ones continue it.
upload_id = 0
for offset in range(0, max(len(content), 1), BLOCK_SIZE):
upload_id = objects.UploadTempFile(ops.UploadTempFileRequest(
upload_id=upload_id,
total_size_in_bytes=len(content),
offset=offset,
data=content[offset:offset + BLOCK_SIZE])).upload_id
objects.UploadTempFileCommit(ops.UploadTempFileCommitRequest(
upload_id=upload_id, logical_size=len(content)))
Source: python/recipes/create_document.py
The document:
response = objects.AddObjectWithFiles(ops.AddObjectWithFilesRequest(
object_type_id=0, # Document
properties=[
property_value(0, text(title)), # Name or title
property_value(100, lookup(1, 1)), # Class: value list 1, item 1 "Other Document"
property_value(22, boolean(True)), # Single file
],
# Always send an ACL: without one, the new document has no permissions and not even
# its creator can see it. Named ACL -1 is "Full control for all internal users".
acl=named_acl(-1),
files=structs.ObjFileSource(
type=enums.OBJ_FILE_SOURCE_TYPE_UPLOADED_FILE,
data=structs.ObjFileSourceUnion(
uploaded_file=structs.ObjFileSourceUnion.UploadedFileData(
upload_id=upload_id,
title=title, # a single-file document's file takes the document's title
extension="txt",
size=len(content)))),
check_in=True))
created = response.created_object.object_version
print(f"Created {describe(created)}")
Source: python/recipes/create_document.py
C#
The upload:
// Upload the content to a temporary file on the server, in blocks. The first call
// (upload_id 0) starts the upload; the next ones continue it.
int uploadId = 0;
for (int offset = 0; offset == 0 || offset < content.Length; offset += BlockSize)
{
var block = await objects.UploadTempFileAsync(new UploadTempFileRequest
{
UploadId = uploadId,
TotalSizeInBytes = content.Length,
Offset = offset,
Data = ByteString.CopyFrom(content, offset, Math.Min(BlockSize, content.Length - offset)),
});
uploadId = block.UploadId;
}
await objects.UploadTempFileCommitAsync(new UploadTempFileCommitRequest
{
UploadId = uploadId,
LogicalSize = content.Length,
});
Source: csharp/Recipes/CreateDocument.cs
The document:
var response = await objects.AddObjectWithFilesAsync(new AddObjectWithFilesRequest
{
ObjectTypeId = 0, // Document
Properties =
{
Property(0, Text(title)), // Name or title
Property(100, Lookup(1, 1)), // Class: value list 1, item 1 "Other Document"
Property(22, Boolean(true)), // Single file
},
// Always send an ACL: without one, the new document has no permissions and not even
// its creator can see it. Named ACL -1 is "Full control for all internal users".
Acl = new AccessControlList
{
CustomComponent = new AccessControlListComponent { HasNamedAcl = true, NamedAclId = -1 },
},
Files = new ObjFileSource
{
Type = ObjFileSourceType.UploadedFile,
Data = new ObjFileSourceUnion
{
UploadedFile = new ObjFileSourceUnion.Types.UploadedFileData
{
UploadId = uploadId,
Title = title, // a single-file document's file takes the document's title
Extension = "txt",
Size = content.Length,
},
},
},
CheckIn = true,
});
var created = response.CreatedObject.ObjectVersion;
Console.WriteLine($"Created {RecipeHelpers.Describe(created)}");
Source: csharp/Recipes/CreateDocument.cs
Go
The upload:
// upload uploads content to a temporary file on the server, in blocks, and returns its upload
// ID. The first call (upload_id 0) starts the upload; the next ones continue it.
func upload(ctx context.Context, objects mfiles.IRPCObjectOperationsClient,
content []byte) (int32, error) {
var uploadID int32
for offset := 0; offset == 0 || offset < len(content); offset += blockSize {
block, err := objects.UploadTempFile(ctx, &mfiles.UploadTempFileRequest{
UploadId: uploadID,
TotalSizeInBytes: int64(len(content)),
Offset: int64(offset),
Data: content[offset:min(offset+blockSize, len(content))],
})
if err != nil {
return 0, err
}
uploadID = block.GetUploadId()
}
_, err := objects.UploadTempFileCommit(ctx, &mfiles.UploadTempFileCommitRequest{
UploadId: uploadID,
LogicalSize: int64(len(content)),
})
return uploadID, err
}
Source: go/recipes/create_document/main.go
The document:
response, err := objects.AddObjectWithFiles(ctx, &mfiles.AddObjectWithFilesRequest{
ObjectTypeId: 0, // Document
Properties: []*mfiles.PropertyValue{
mfilesclient.PropertyValue(0, mfilesclient.Text(title)), // Name or title
mfilesclient.PropertyValue(100, mfilesclient.Lookup(1, 1)), // Class: "Other Document"
mfilesclient.PropertyValue(22, mfilesclient.Boolean(true)), // Single file
},
// Always send an ACL: without one, the new document has no permissions and not even its
// creator can see it. Named ACL -1 is "Full control for all internal users".
Acl: mfilesclient.NamedACL(mfilesclient.NamedACLFullControlForAllInternalUsers),
Files: &mfiles.ObjFileSource{
Type: mfiles.ObjFileSourceType_OBJ_FILE_SOURCE_TYPE_UPLOADED_FILE,
Data: &mfiles.ObjFileSourceUnion{Data: &mfiles.ObjFileSourceUnion_UploadedFile{
UploadedFile: &mfiles.ObjFileSourceUnion_UploadedFileData{
UploadId: uploadID,
Title: title, // a single-file document's file takes the document's title
Extension: "txt",
Size: int64(len(content)),
},
}},
},
CheckIn: true,
})
if err != nil {
return err
}
created := response.GetCreatedObject().GetObjectVersion()
fmt.Println("Created", recipe.Describe(created))
Source: go/recipes/create_document/main.go
Output (the recipe deletes the document at the end unless you pass --keep):
Created [docs-test] Created by the Python sample 2026-10-31 14:55:56 (ID 611, version 1, modified 2026-10-31 14:55:56 UTC)
File: [docs-test] Created by the Python sample 2026-10-31 14:55:56.txt, 96 bytes
Deleted object 0-611
Pitfalls
- Required properties. The built-in class Document (0) requires Name or title (0) and Single
file (22); many classes require more. A class lists them in
associated_property_defswithis_required: see Read the vault structure. - Single file is true for a document with exactly one file and false otherwise. The other
recipes create documents without files, with Single file false and no
files. - The file takes the document's title in a single-file document. The samples pass the
document's title as the file's
title. - Commit exactly once, with the right size. A
logical_sizethat differs from the bytes uploaded, or a second commit, fails with "File size mismatch." (0x8004003D) and removes the upload. - Uploads belong to the login session that made them, on the server instance that received
the first block; keep the
mfilesmsmcookie on M-Files Cloud (the helpers do). An unused upload is removed after a while; see UploadTempFile. - Cleaning up. The recipes delete what they create with
RemoveObject. Destroying with
DestroyObject needs a vault-level right that
normal users do not have; they get
0x800400A9.
See also
- Files: uploads and downloads in detail.
- Objects and versions
- Properties and values
- Update properties