Document

Process Uploaded Files from an Endpoint

Build a synchronous HTTP workflow that accepts one text file, stages it in run-local storage, validates and normalizes its content, persists the result, and returns a JSON response.

This guide uses a dedicated Guide - Process Uploaded Files pipeline with the following path:

Endpoint Trigger -> Read File -> Code -> Save File -> Endpoint Response

A file-processing workflow from an Endpoint upload to a persisted result and synchronous responseA file-processing workflow from an Endpoint upload to a persisted result and synchronous response

The example deliberately accepts UTF-8 text only. Use a separate, format-aware processing path for JSON, XML, images, PDFs, archives, or other binary files.

Before you begin

Define the endpoint contract before adding blocks. The example uses:

Contract itemExample value
HTTP methodPOST
AccessProtected endpoint
Request bodymultipart/form-data
File fieldfile
File countOne
Transport limit5 MB
Intake storageContext Drive
ProcessingUTF-8 text normalization
Result storageStatic Drive
ResponseSynchronous JSON

Confirm that the project owner has access to the required drive scope and enough storage, compute, and concurrent-run capacity. Use non-sensitive test files until validation, retention, and cleanup behavior have been verified.

1. Configure the upload endpoint

Add Endpoint Trigger and label it Receive Text Upload. Configure POST, a custom path such as guides/process-uploaded-files, and a synchronous JSON response.

Open Request, enable Accept file, and configure the upload:

Endpoint Trigger request settings for one multipart text upload stored in Context DriveEndpoint Trigger request settings for one multipart text upload stored in Context Drive

SettingValue
File modeSingle
Field namefile
Maximum size5 MB
StorageContext
Destination file pathguide-uploads/{{run.id}}/source.txt

Use a controlled destination filename and a unique run directory. Do not build a writable path directly from the caller-supplied filename. A filename is untrusted metadata and can contain collisions, unexpected separators, or traversal-like sequences.

Protect the endpoint and distribute its token only to authorized server-side callers. A long-lived endpoint token does not belong in public browser code. Apply an appropriate rate limit before publishing because rejected, failed, and malicious requests can still consume resources.

The trigger's maximum size is an intake limit, not complete file validation. It does not prove that the bytes are UTF-8 text or that the declared media type is correct.

2. Send a multipart request

The multipart field name must exactly match file:

curl --request POST "$BUILDERS_UPLOAD_URL" \
  --header "Authorization: Bearer $BUILDERS_ENDPOINT_TOKEN" \
  --form "file=@./notes.txt;type=text/plain"

In a trusted JavaScript client, construct the same request with FormData:

const form = new FormData();
form.append("file", selectedFile);

const response = await fetch(uploadUrl, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${endpointToken}`,
  },
  body: form,
});

Do not manually set a bare Content-Type: multipart/form-data header when the client creates FormData. The client must add the boundary that separates multipart fields. See Using FormData Objects on MDN and the multipart/form-data specification.

3. Stage the upload in Context Drive

The trigger writes the accepted file to:

guide-uploads/{{run.id}}/source.txt

Context Drive is isolated to the current run and is appropriate for temporary intake. It prevents an unvalidated upload from becoming a durable shared file merely because the request was accepted.

Keep the canonical file descriptor intact when a downstream block supports it. The descriptor carries the storage location and file metadata; it is not the same as the decoded file content. Persist only the original file when the business process and retention policy require it.

4. Read the file explicitly as text

Add Read File, label it Read Uploaded Text, and connect the Endpoint Trigger's Out port to it. Configure:

SettingValue
StorageContext Drive
File pathguide-uploads/{{run.id}}/source.txt
Read asText

Selecting Text makes the expected decoding explicit instead of relying on the extension or an automatically detected mode. It does not make arbitrary bytes valid text. Invalid encodings, unsupported content, and parsing failures must stop the success path.

Run one controlled upload and inspect the Read File output before writing downstream templates. Its Out object contains a canonical file; select the decoded content property from autocomplete rather than assuming that provider-specific metadata always has the same shape.

5. Validate and normalize the content

Add Code, label it Validate and Normalize, and connect Read File to it. Use Code for deterministic checks and transformations that are not covered by a dedicated parser block.

The following TypeScript is an illustrative normalization pattern. After a successful Read File run, replace input.file.content with the exact decoded-content path exposed by autocomplete:

export async function run(input: any, ctx: BuildersRuntimeContext) {
  const source = String(input.file?.content ?? "");

  if (!source.trim()) {
    throw new Error("The uploaded text file is empty.");
  }

  if (source.length > 100_000) {
    throw new Error("The decoded text exceeds the processing limit.");
  }

  const processedText = source
    .replace(/\r\n?/g, "\n")
    .split("\n")
    .map((line) => line.replace(/[ \t]+$/g, ""))
    .join("\n");

  ctx.log("Text upload processed", {
    characters: processedText.length,
  });

  return {
    processedText,
    summary: {
      characters: processedText.length,
      lines: processedText.split("\n").length,
    },
  };
}

Do not log the source text. Logs are operational records and may be visible to people who should not receive the uploaded content.

Apply validation in layers:

LayerWhat to verify
TransportMethod, authentication, file count, and maximum request size
MetadataAllowed extension and declared media type
BytesMagic bytes, encoding, and actual format
StructureParse with a format-aware parser and enforce its schema or limits
Content policyRequired fields, allowed values, and business rules
SafetyMalware scanning or sandboxing where untrusted binary content requires it
Resource useExpanded size, page or row count, recursion, and decompression limits

Extensions and declared MIME values are useful signals, but neither authorizes the content. Use registered names from the IANA media type registry and validate the actual format. For PDFs, images, archives, and office files, use an appropriate parser or scanning service rather than treating bytes as text in Code.

The Code block documents the runtime entry point and links to the separate Builders Runtime API references.

6. Persist the processed result

Add Save File, label it Persist Processed File, and connect the Code block's Out port. Configure text mode:

SettingValue
StorageStatic Drive
Destination pathguide-results/{{run.id}}/normalized.txt
Text{{input.processedText}}

This creates a durable result only after validation succeeds. The run ID prevents concurrent requests from overwriting one another. If callers need stable business identifiers, validate authorization and uniqueness before using those identifiers in a path.

Static Drive belongs to the compatible personal or team owner scope. If the approved system of record is Google Drive, OneDrive, AWS S3, or Custom S3, select the matching integration in Save File and verify its write permissions, retention controls, encryption, and duplicate behavior.

Define cleanup for both successful results and abandoned data. Do not keep every processed file indefinitely simply because the workflow can persist it.

7. Return a stable synchronous response

Connect Save File's Out port back to the Endpoint Trigger's Response input. The example returns the Save File result as JSON so you can inspect the descriptor during development.

For a production API, add a Transform or small Code step before the response and expose a deliberate contract instead of raw provider metadata. For example:

{
  "status": "processed",
  "runId": "{{run.id}}",
  "result": {
    "path": "guide-results/{{run.id}}/normalized.txt"
  }
}

Do not return credentials, internal provider identifiers, private storage URLs, or unvalidated request metadata.

When the caller should receive the processed bytes instead of JSON, choose the Endpoint Trigger's File response type and make Response path resolve to the canonical file descriptor. Set a validated filename, correct MIME type, and either inline or attachment disposition.

Keep synchronous processing short enough for the caller and every proxy in between. Move long-running parsing, AI processing, scanning, or conversion to an asynchronous pattern that returns an accepted status and a correlation identifier.

Response caching applies to stable synchronous GET file responses. This upload workflow uses POST; its response represents a new processing action and must not be cached as reusable content.

8. Publish and test the complete path

Save the version, publish it, and upload a controlled file only after all four blocks show valid configuration. Inspect the published run in execution order:

  1. Endpoint Trigger accepted exactly one file part and wrote the expected Context path.
  2. Read File decoded the expected number of bytes as text.
  3. Code returned processedText and summary data without logging the source.
  4. Save File wrote a unique Static Drive path.
  5. Endpoint Response returned the documented status and response body.

Cover negative cases as well:

Test caseExpected result
Missing file fieldRequest is rejected before processing
Wrong multipart field nameNo upload is bound to the configured field
File above 5 MBTrigger rejects intake
Empty textCode stops the success path
Binary file labeled text/plainFormat or decoding validation rejects it
Invalid or missing tokenRequest is unauthorized
Repeated requestA unique result is created or documented idempotency logic reuses the original result
Save File failureError path records the failure and no success response is emitted

For retried business operations, add an idempotency key supplied by the caller or derived from an authorized business identifier. Store its completed outcome before performing another durable write. Do not rely on the original filename as an idempotency key.

Troubleshoot the workflow

SymptomCheck
Endpoint receives no fileHTTP method, multipart encoding, exact file field, and client-generated boundary
Upload is rejected immediatelyAuthentication, configured size limit, file count, and rate limit
Read File cannot find the sourceMatching Context path and the same {{run.id}}
Code sees undefined contentSuccessful Read File output and the property selected from autocomplete
Text is corruptedSource encoding and explicit Read File mode
Unsafe file passes extension checksActual-byte parser, signature checks, and scanning policy
Result overwrites another fileUnique destination path and retry or idempotency behavior
File disappears after the runContext lifetime; persist the approved result to Static or external storage
Caller times outSynchronous workload duration; redesign long processing as asynchronous
Caller receives internal metadataMap a stable response contract before Endpoint Response

Next steps

Boilerplate Wiki - Process Uploaded Files from an Endpoint