diff --git a/content/concepts/files/_meta.js b/content/concepts/files/_meta.js
new file mode 100644
index 0000000..15f8200
--- /dev/null
+++ b/content/concepts/files/_meta.js
@@ -0,0 +1,4 @@
+export default {
+ 'files-in-sharetribe': {},
+ 'file-lifecycle': {}
+};
diff --git a/content/concepts/files/file-lifecycle/index.mdx b/content/concepts/files/file-lifecycle/index.mdx
new file mode 100644
index 0000000..bbc338b
--- /dev/null
+++ b/content/concepts/files/file-lifecycle/index.mdx
@@ -0,0 +1,208 @@
+---
+title: File lifecycle in Sharetribe
+sidebarTitle: File lifecycle
+description:
+ This article describes the lifecycle of files on a Sharetribe
+ marketplace
+---
+
+import { Callout, Steps } from 'nextra/components';
+
+# File lifecycle in Sharetribe
+
+Sharetribe has endpoints and mechanisms for uploading and downloading
+digital files. Uploading and downloading files are both multi-step
+operations, and there are a number of different entities and states
+involved.
+
+## Uploading a file
+
+
+
+Files can be uploaded in two sequences. The sequence described here is
+to first create the **ownFile** resource, and then create the
+**fileUpload** resource and upload details with the file id of the
+**ownFile**.
+
+It is also possible to first create the **fileUpload** resource and
+upload the file, and then create the **ownFile** resource with the id of
+the **fileUpload** resource.
+
+
+
+
+
+### Read file metadata
+
+Before we make any API calls, we need to get the metadata of the file in
+the local file system.
+
+The SDK has a helper function for parsing the attributes of the file
+metadata that are needed for uploading the file.
+
+```js
+import * as sharetribeSdk from 'sharetribe-flex-sdk';
+
+const { file: sdkFile } = sharetribeSdk;
+
+const metadata = sdkFile.metadata(file);
+```
+
+If you are using the APIs directly, you'll need to parse the necessary
+metadata information from the File object:
+
+```js
+const metadata = {
+ name: 'File to be uploaded.pdf',
+ mimeType: 'application/pdf',
+ size: 10697,
+};
+```
+
+### Create an ownFile resource
+
+With the metadata information, you can create an `ownFile` resource in
+the Sharetribe backend with `ownFiles.create`.
+
+```js
+const ownFileResource = await sdk.ownFiles.create({ ...metadata }).data;
+```
+
+This step also validates that the file mime type is supported.
+
+### Create a file upload URL
+
+The id of the `ownFile` resource is then used to create a signed file
+upload URL.
+
+```js
+const fileId = ownFileResource.data.id;
+const fileUploadDetails = await sdk.fileUploads.create({
+ fileId,
+});
+```
+
+```json
+{
+ "fileUploadDetails": {
+ "status": 200,
+ "statusText": "",
+ "data": {
+ "data": {
+ "id": {
+ "uuid": "63abdec4-85e7-4157-8e99-2e2c7465d8be"
+ },
+ "type": "fileUpload",
+ "attributes": {
+ "fileId": {
+ "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811"
+ },
+ "url": "https://unique-signed-sharetribe-upload-url.com",
+ "headers": {
+ "Content-Type": "application/pdf"
+ },
+ "method": "PUT",
+ "expiresAt": "2026-05-08T08:16:30.988Z"
+ }
+ }
+ }
+ }
+}
+```
+
+### Upload the file to storage with the file upload URL
+
+The actual file is uploaded directly to storage with the URL and other
+details from the response, and the upload does not use Sharetribe APIs.
+The SDK has another helper function to upload the file.
+
+When using the SDK, you can also pass in an upload progress tracking
+callback to display the progress to the user.
+
+```js
+const {
+ method = 'PUT',
+ url,
+ headers = {},
+} = fileUploadDetails?.data?.data?.attributes;
+
+const onUploadProgress = (progressEvent) => {
+ const loaded = progressEvent?.loaded || 0;
+ const total = progressEvent?.total || file.size;
+ const progress = total
+ ? Math.min(100, Math.round((loaded / total) * 100))
+ : null;
+ console.log(`progress ${progress} %`);
+};
+
+sdkFile.upload({
+ method,
+ url,
+ headers,
+ file,
+ onUploadProgress,
+});
+```
+
+### Attach the file to a resource
+
+The file id of the `ownFile` resource is then used to attach the file to
+another resource, e.g. to a message:
+
+```js
+sdk.messages.send({
+ transactionId: '6985dfd3-34bc-4bc8-806f-03a0f29e056d',
+ content: 'This is a message with a file',
+ publicFileAttachments: [fileId],
+});
+```
+
+The file resource (`file` or `ownFile`) can be in one of several states:
+
+- pendingUpload
+- pendingVerification
+- available
+- verificationFailed
+
+Depending on your use case, you may only want to attach files that are
+available, or then allow attaching files that have completed upload.
+
+
+
+After the file is attached to a resource, it is visible according to the
+scope of the `fileAttachment`.
+
+## Downloading a file
+
+To download a file, the user makes a POST request for a short-lived
+download URL for the file from Sharetribe API, and uses that generated
+URL to fetch the file from storage.
+
+Download links are only created for files in the `available` state.
+
+There are two different download endpoints that use different ids:
+
+- sdk.ownFileDownloads.create for ownFile resources, where the parameter
+ is the `file` id
+- sdk.fileDownloads.create for file resources, where the parameter is
+ the `fileAttachment` id
+
+```js
+const downloadDetails = isOwnFile
+ ? sdk.ownFileDownloads.create({ fileId }).data?.data
+ : sdk.fileDownloads.create({ fileAttachmentId }).data?.data;
+
+const { url } = downloadDetails?.attributes;
+```
+
+## Deleting a file
+
+An operator can delete a file in Console. When attaching files to a
+message, a single file is only attached to a single message at a time,
+so when an operator deletes a file associated with a message, both the
+`fileAttachment` and the `file` are deleted.
+
+There is no file deletion endpoint in either Integration API or
+Marketplace API. If you've uploaded a file but you have not yet attached
+it to a message, leaving the file unattached will result in it being
+eventually deleted.
diff --git a/content/concepts/files/files-in-sharetribe/files-architecture.png b/content/concepts/files/files-in-sharetribe/files-architecture.png
new file mode 100644
index 0000000..6be72e8
Binary files /dev/null and b/content/concepts/files/files-in-sharetribe/files-architecture.png differ
diff --git a/content/concepts/files/files-in-sharetribe/index.mdx b/content/concepts/files/files-in-sharetribe/index.mdx
new file mode 100644
index 0000000..5e3d6b7
--- /dev/null
+++ b/content/concepts/files/files-in-sharetribe/index.mdx
@@ -0,0 +1,339 @@
+---
+title: Files in Sharetribe
+sidebarTitle: Files in Sharetribe
+description:
+ This article describes how files and file attachments work on a
+ Sharetribe marketplace.
+---
+
+# Files in Sharetribe
+
+Sharetribe supports uploading and downloading files and attaching them
+to different marketplace resources.
+
+The file information and relationships to other resources is stored in
+the Sharetribe backend and available through the Sharetribe APIs. The
+file entities themselves are stored in a third-party storage, and you
+need to call the Sharetribe APIs to request unique signed upload and
+download URLs to access the actual file entities.
+
+- Call Sharetribe APIs to
+ - create a file resource
+ - retrieve file information
+ - create upload and download URLs for a file resource
+ - manage resources with file attachments
+- Call Sharetribe storage to
+ - upload file using a short-lived upload URL
+ - download file using a short-lived download URL
+
+
+
+## Permissions
+
+Uploading and downloading files is enabled by default. Operators can
+disable uploading and downloading files across the marketplace in Access
+control. When uploads and downloads are disabled in Console > General >
+Access control,
+[file upload and download endpoints](/concepts/users-and-authentication/user-access-control-in-sharetribe/#disabling-file-uploads-and-downloads)
+will throw a 403 Forbidden error.
+
+In addition, you can use for instance listing types to enable or disable
+specific use cases for files in your marketplace, if files are enabled.
+
+For example, to view and use file attachment capabilities in transaction
+messages when using the default Sharetribe Web Template, you'll need to
+double check that files are enabled both on the marketplace level and in
+the listing type of the transaction's listing.
+
+## File and FileAttachment
+
+In the Sharetribe system, `file` and `fileAttachment` represent two
+different things:
+
+- A `file` represents a single uploaded digital file resource
+- A `fileAttachment` is a file's connection to another resource, such as
+ a message.
+
+A `file` is a related resource to a `fileAttachment`, and a
+`fileAttachment` is a related resource of another resource, for example
+a message. In this structure, it is possible for a `file` to have
+`fileAttachment` links to multiple different resources.
+
+When querying the resource, you'll need to include both the file
+attachments relationship and the nested file relationship if you want to
+retrieve both.
+
+```js
+const messages = await sdk.messages.query({
+ transaction_id: txId,
+ include: ['publicFileAttachments', 'publicFileAttachments.file'],
+});
+```
+
+```js
+// messages.query API response with included
+// publicFileAttachments and publicFileAttachments.file
+{
+ "data": [
+ {
+ "id": {
+ "uuid": "69fd886a-96b2-4ba1-b458-cda5fdc0616e"
+ },
+ "type": "message",
+ "attributes": {
+ "content": "message content",
+ "createdAt": "2026-05-08T06:53:30.043Z",
+ "deleted": false
+ },
+ // publicFileAttachments is a relationship to message
+ "relationships": {
+ "publicFileAttachments": {
+ "data": [
+ {
+ "id": {
+ "uuid": "69fd886a-1924-41c5-bfe1-a43deb7e1c3b"
+ },
+ "type": "fileAttachment"
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "included": [
+ {
+ // this is the file resource that has a relationship to fileAttachment
+ "id": {
+ "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811"
+ },
+ "type": "file",
+ "attributes": {
+ "name": "File to be uploaded.pdf",
+ "state": "available",
+ "mimeType": "application/pdf",
+ "size": 10697,
+ "deleted": false
+ }
+ },
+ // This message has one fileAttachment in its publicFileAttachments array
+ {
+ "id": {
+ "uuid": "69fd886a-1924-41c5-bfe1-a43deb7e1c3b"
+ },
+ "type": "fileAttachment",
+ "attributes": {
+ "scope": "public",
+ "deleted": false
+ },
+ // file is a relationship to fileAttachment
+ "relationships": {
+ "file": {
+ "data": {
+ "id": {
+ "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811"
+ },
+ "type": "file"
+ }
+ }
+ }
+ }
+ ],
+ "meta": {
+ "totalItems": 1,
+ "totalPages": 1,
+ "page": 1,
+ "perPage": 100
+ }
+}
+```
+
+An operator can delete a file attachment to disconnect a resource from a
+file, or they can delete the file itself so that all resources linked to
+the file lose access. When attaching files to messages, a single file is
+only attached to a single message at a time, so when an operator deletes
+a file associated with a message, both the `fileAttachment` and the
+`file` are deleted.
+
+## file and ownFile
+
+The digital file resource can be represented by two resources: `file`
+and `ownFile`.
+
+To fetch a public `file` resource, you'll need to use the id of the
+associated `fileAttachment`, as the `fileAttachment` resource contains
+the scope information that informs whether the API caller should have
+access to the resource.
+
+```js
+const file = await sdk.files.show({ fileAttachmentId }).data.data;
+```
+
+```json
+"file": {
+ "id": {
+ "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811"
+ },
+ "type": "file",
+ "attributes": {
+ "name": "File to be uploaded.pdf",
+ "state": "available",
+ "mimeType": "application/pdf",
+ "size": 10697,
+ "deleted": false
+ }
+}
+```
+
+There's also an `ownFile` resource, which is the author's expanded
+version of the file. In addition to the public attributes, the
+`ownFile`resource also shows when the resource was created and when the
+state was updated.
+
+To fetch an `ownFile`resource, you'll need to use the id of the `file`
+resource, not the file attachment resource.
+
+```js
+const ownFile = await sdk.ownFiles.show({ id: fileId }).data.data;
+```
+
+```json
+"ownFile": {
+ "id": {
+ "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811"
+ },
+ "type": "ownFile",
+ "attributes": {
+ "name": "File to be uploaded.pdf",
+ "mimeType": "application/pdf",
+ "size": 10697,
+ "state": "available",
+ "createdAt": "2026-05-08T06:53:13.091Z",
+ "stateUpdatedAt": "2026-05-08T06:53:25.880Z"
+ }
+}
+```
+
+A `file` can be in one of several states:
+
+- pendingUpload
+- pendingVerification
+- available
+- verificationFailed.
+
+Both `file` and `ownFile` resources are immutable, so updating an
+existing file resource is not possible.
+
+## Visibility
+
+A file is first uploaded by creating an `ownFile` resource, which is
+visible to the creator of the resource. An `ownFile` can then be
+attached to another resource.
+
+Once a file has been attached to another resource, it becomes also
+visible as a `file` resource according to the scope of the corresponding
+fileAttachments attribute:
+
+- `publicFileAttachments` are visible to all users who have access to
+ the resource in question
+- `protectedFileAttachments` are visible to the user who created the
+ resource, and additionally the file can be revealed in a transaction
+ to the other transaction participant
+- `privateFileAttachments` are visible only to the user who created the
+ resource.
+
+For example, files attached to messages are visible to both participants
+of the transaction in question, because they are attached via the
+`publicFileAttachments` relationship of the message.
+
+You can review the
+[API reference](https://www.sharetribe.com/api-reference/) to see which
+resources have which scopes of file attachments available.
+
+In addition, operators have visibility to files and file attachments in
+Console. For example, files attached to messages are visible in the
+Console transaction details.
+
+## File storage
+
+Files are both uploaded to and downloaded from storage directly using
+pre-signed URLs – not using Sharetribe API endpoints.
+
+For uploads, this means that a user first creates the file resource in
+Sharetribe, and then fetches a time-limited signed URL connected to that
+specific file resource that allows uploading the file to storage.
+
+```js
+const uploadDetails = sdk.fileUploads.create({ fileId }).data.data;
+```
+
+```json
+"uploadDetails": {
+ "id": {
+ "uuid": "63abdec4-85e7-4157-8e99-2e2c7465d8be"
+ },
+ "type": "fileUpload",
+ "attributes": {
+ "fileId": {
+ "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811"
+ },
+ "url": "https://unique-signed-sharetribe-upload-url.com",
+ "headers": {
+ "Content-Type": "application/pdf"
+ },
+ "method": "PUT",
+ "expiresAt": "2026-05-08T08:16:30.988Z"
+ }
+}
+```
+
+For downloads, the user makes a POST request for a short-lived download
+URL for the file from Sharetribe API, and uses that generated URL to
+fetch the file from storage. There are two different download endpoints
+that use different ids, similarly to how files/show and own_files/show:
+
+- own_file_downloads/create (sdk.ownFileDownloads.create) for ownFile
+ resources, where the parameter is the `file` id
+- file_downloads/create (sdk.fileDownloads.create) for file resources,
+ where the parameter is the `fileAttachment` id
+
+```js
+const downloadDetails = isOwnFile
+ ? sdk.ownFileDownloads.create({ fileId }).data.data
+ : sdk.fileDownloads.create({ fileAttachmentId }).data.data;
+```
+
+```json
+"downloadDetails": {
+ "id": {
+ "uuid": "fd76299c-1be6-4250-8de6-10b1d8f9cb9e"
+ },
+ "type": "fileDownload",
+ "attributes": {
+ "fileId": {
+ "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811"
+ },
+ "url": "https://unique-signed-sharetribe-download-url.com",
+ "expiresAt": "2026-05-08T08:29:28.089Z"
+ }
+}
+```
+
+Since both upload URLs and download URLs are short-lived, it's not
+possible to save them in a resource's extended data for reuse. Both
+resources have an `expiresAt` attribute that indicates how long they are
+valid.
+
+## Security
+
+When a file is uploaded, the Sharetribe backend runs a security
+verification to ensure that the file is safe to handle and distribute,
+for example that it does not have malware. In addition, the backend
+verifies that the file mime type is acceptable and the size is less than
+1GB.
+
+Files can be attached to resources once they've been uploaded, even if
+they have not yet been verified. The Sharetribe backend processes the
+verification asynchronously, and the file becomes available if the
+verification succeeds. If you want to show the file as available
+immediately when verification succeeds, your client will need to poll
+the file to determine when it becomes available.
diff --git a/content/concepts/index.mdx b/content/concepts/index.mdx
index 377820f..64a873a 100644
--- a/content/concepts/index.mdx
+++ b/content/concepts/index.mdx
@@ -219,6 +219,19 @@ design decisions behind the platform.
/>
+## Files
+
+
+
+
+
+
## Integrations
diff --git a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png
index b4fd56e..18ccd73 100644
Binary files a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png and b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png differ
diff --git a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx
index 16e0f0c..2e7c565 100644
--- a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx
+++ b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx
@@ -44,6 +44,12 @@ See which endpoints are affected by this setting
Read more about this feature in the
[Help Center](https://www.sharetribe.com/help/en/articles/9503164-make-marketplace-private).
+## Disable file uploads and downloads
+
+By default, Sharetribe marketplaces allow uploading and downloading
+files and using them as file attachments on other resources. You can
+also disable uploading and downloading files on a marketplace level.
+
## Approve users who want to join
User approval means that when a user signs up, they need to be approved
@@ -162,6 +168,19 @@ When this setting is toggled, these endpoints will return a 403
Forbidden response, indicating that access is denied due to the
marketplace's private status.
+### Disabling file uploads and downloads
+
+Toggling this setting on prevents users from uploading and downloading
+files to and from Sharetribe storage. This selection will restrict the
+following endpoints:
+
+- `POST /file_uploads/create`
+- `POST /file_downloads/create`
+
+In other words, endpoints showing file or file attachment information
+are not restricted, only endpoints that create signed URLs to Sharetribe
+file storage to upload or download a file entity.
+
### Approve users who want to join
Toggling this setting in Console will require users to be approved by an
diff --git a/content/references/digital-files/index.mdx b/content/references/digital-files/index.mdx
new file mode 100644
index 0000000..10ed04e
--- /dev/null
+++ b/content/references/digital-files/index.mdx
@@ -0,0 +1,66 @@
+---
+title: Digital files management
+sidebarTitle: Digital files management
+description: Reference documentation for digital files management.
+---
+
+# Digital files management
+
+The digital files management features of Sharetribe allow listing
+authors to upload and download digital files and attach them to other
+resources. There are three key concepts related to digital files
+management:
+
+- **file** is the Sharetribe representation of the uploaded digital file
+ item.
+- **ownFile** is the Sharetribe representation of the uploaded digital
+ file item visible to the author of the upload.
+- A **file attachment** is the association of a **file** to another
+ marketplace resource, such as a message.
+
+The actual digital file entity is uploaded to and downloaded from
+Sharetribe storage with a direct URL, not through the Sharetribe APIs.
+The URLs are managed through three other resource types:
+
+- **fileUpload** contains the URL and other details for uploading a file
+ directly to storage.
+- **fileDownload** contains the URL for downloading a file directly from
+ storage.
+- **ownFileDownload** contains the URL for downloading a user's own
+ uploaded file directly from storage.
+
+## File states
+
+A file can be in one of several possible states:
+
+- `pendingUpload`
+- `pendingVerification`
+- `available`
+- `verificationFailed`
+
+Files change state as a result of the file verification process that
+happens once a user uploads the digital file entity using the signed
+upload URL. Download URLs are only generated for files in `available`
+state.
+
+## File attachment scopes
+
+A **file** resource can be associated with another resource by adding it
+to the other resource's file attachments. The scope of the file
+attachment determines the visibility of the file:
+
+- **publicFileAttachments** are visible to all users who have access to
+ the resource in question
+- **protectedFileAttachments** are visible to the user who created the
+ resource, and additionally the file can be revealed in a transaction
+ to the other transaction participant
+- **privateFileAttachments** are visible only to the user who created
+ the resource.
+
+The [API reference](https://www.sharetribe.com/api-reference/) details
+which resources have which scopes of file attachments available.
+
+## Further reading
+
+- Marketplace API reference
+- Integration API reference
diff --git a/content/references/index.mdx b/content/references/index.mdx
index c652470..a851742 100644
--- a/content/references/index.mdx
+++ b/content/references/index.mdx
@@ -24,6 +24,10 @@ customizing your marketplace with code.
title="Availability management"
href="/references/availability/"
/>
+