Skip to main content
Sends file data to a previously created file upload. This endpoint accepts multipart/form-data instead of JSON.

Method

client.fileUploads.send({
  file_upload_id: "file_upload_id",
  file: {
    data: fileBuffer,
    filename: "document.pdf"
  }
})

Parameters

file_upload_id
string
required
Identifier for a Notion file upload object, obtained from the id of the Create File Upload response.
file
object
required
The file data to upload.
file.data
string | Blob
required
Raw file contents as a string, Buffer, Blob, or File object.
file.filename
string
Optional filename. Overrides the filename specified in create.
part_number
string
Required for multi-part uploads. Stringified integer (e.g., "1", "2", "3") indicating which part is being sent. Must be sequential starting from "1" and match the number_of_parts specified in create.

Response

Returns an updated FileUpload object.
status
string
Updated status: "pending" (for multi-part, more parts needed) or "uploaded" (for single-part, or after all parts sent and complete called).
number_of_parts
object
For multi-part uploads, tracks progress:
  • total: Total number of parts expected
  • sent: Number of parts successfully uploaded
file_import_result
object
After successful upload, contains import results:
  • type: "success" or "error"
  • imported_time: ISO 8601 timestamp
  • error: Error details if type is "error"

Examples

Single-part upload

For files under 20MB, send all data in one request:
import fs from "fs"

// First, create the file upload
const fileUpload = await client.fileUploads.create({
  mode: "single_part",
  filename: "report.pdf",
  content_type: "application/pdf"
})

// Then send the file data
const fileBuffer = fs.readFileSync("./report.pdf")

const result = await client.fileUploads.send({
  file_upload_id: fileUpload.id,
  file: {
    data: fileBuffer,
    filename: "report.pdf"
  }
})

console.log(result.status) // "uploaded"

Multi-part upload

For files larger than 20MB, split into parts and send sequentially:
import fs from "fs"

const CHUNK_SIZE = 20 * 1024 * 1024 // 20MB per part
const fileBuffer = fs.readFileSync("./large-video.mp4")
const totalParts = Math.ceil(fileBuffer.length / CHUNK_SIZE)

// Create the upload
const fileUpload = await client.fileUploads.create({
  mode: "multi_part",
  filename: "large-video.mp4",
  content_type: "video/mp4",
  number_of_parts: totalParts
})

// Send each part sequentially
for (let i = 0; i < totalParts; i++) {
  const start = i * CHUNK_SIZE
  const end = Math.min(start + CHUNK_SIZE, fileBuffer.length)
  const chunk = fileBuffer.slice(start, end)
  
  await client.fileUploads.send({
    file_upload_id: fileUpload.id,
    file: {
      data: chunk,
      filename: "large-video.mp4"
    },
    part_number: String(i + 1) // Must be a string: "1", "2", "3", etc.
  })
  
  console.log(`Sent part ${i + 1} of ${totalParts}`)
}

// Finally, complete the upload
await client.fileUploads.complete({
  file_upload_id: fileUpload.id
})

Browser file upload

Using the File API in browsers:
// In a browser environment
const handleFileUpload = async (file: File) => {
  // Create the upload
  const fileUpload = await client.fileUploads.create({
    mode: "single_part",
    filename: file.name,
    content_type: file.type
  })
  
  // Send the file (File object is a type of Blob)
  const result = await client.fileUploads.send({
    file_upload_id: fileUpload.id,
    file: {
      data: file,
      filename: file.name
    }
  })
  
  return result
}

Notes

  • This endpoint sends HTTP multipart/form-data instead of JSON
  • For multi-part uploads, parts must be sent sequentially starting from "1"
  • The part_number parameter must be a string, not a number
  • After sending all parts, call complete to finalize the upload
  • File data is passed under the file.data property, not as the raw file

Build docs developers (and LLMs) love