# Create a document

Location: [gRPC API](https://developer.m-files.com/APIs/gRPC-API/index.md) > [Guides](https://developer.m-files.com/APIs/gRPC-API/index.md#guides)

HTML version: https://developer.m-files.com/APIs/gRPC-API/guides/create-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

1. [UploadTempFile](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/UploadTempFile/index.md): send the content in
   blocks. The first call has `upload_id` 0, `offset` 0 and the file's `total_size_in_bytes`, and
   returns the `upload_id` of a new upload; later calls pass that ID and the `offset` of their
   block. The samples send blocks of 4 MiB.
2. [UploadTempFileCommit](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/UploadTempFileCommit/index.md) with
   `upload_id` and `logical_size`, the number of bytes uploaded. An upload that is not committed
   cannot be used.
3. [AddObjectWithFiles](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/AddObjectWithFiles/index.md) 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](https://developer.m-files.com/APIs/gRPC-API/concepts/permissions/index.md).

## Code

### Python

The upload:

```python
# 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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/python/recipes/create_document.py)*

The document:

```python
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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/python/recipes/create_document.py)*

### C#

The upload:

```csharp
// 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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/csharp/Recipes/CreateDocument.cs)*

The document:

```csharp
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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/csharp/Recipes/CreateDocument.cs)*

### Go

The upload:

```go

// 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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/go/recipes/create_document/main.go)*

The document:

```go
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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/go/recipes/create_document/main.go)*

Output (the recipe deletes the document at the end unless you pass `--keep`):

```text
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_defs` with
  `is_required`: see [Read the vault structure](https://developer.m-files.com/APIs/gRPC-API/guides/vault-structure/index.md).
* **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_size` that 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 `mfilesmsm` cookie on M-Files Cloud (the helpers do). An unused upload
  is removed after a while; see
  [UploadTempFile](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/UploadTempFile/index.md).
* **Cleaning up.** The recipes delete what they create with
  [RemoveObject](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/RemoveObject/index.md). Destroying with
  [DestroyObject](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/DestroyObject/index.md) needs a vault-level right that
  normal users do not have; they get `0x800400A9`.

## See also

* [Files](https://developer.m-files.com/APIs/gRPC-API/runtime/files/index.md): uploads and downloads in detail.
* [Objects and versions](https://developer.m-files.com/APIs/gRPC-API/concepts/objects-and-versions/index.md)
* [Properties and values](https://developer.m-files.com/APIs/gRPC-API/concepts/properties-and-values/index.md)
* [Update properties](https://developer.m-files.com/APIs/gRPC-API/guides/update-properties/index.md)
