Use the file operations to bring a PDF, image or other document into Brigg. You receive a stable file_id; storage paths and provider credentials stay inside Brigg.
Give the API key access to documents.CreateUpload, documents.GetUploadPart, documents.CompleteUpload and the operations that will use the file. Choose an idempotency key for each command and reuse it when retrying that command.
Upload from your server
Download the Node.js server helper into your integration as brigg.mjs. For TypeScript, also save the type declarations beside it as brigg.d.mts. It has no third-party dependencies. Use Node.js 22 or later and keep the API key on your server.
import { createBriggClient } from "./brigg.mjs";
const brigg = createBriggClient({
apiKey: process.env.BRIGG_API_KEY!,
baseUrl: "https://api.brigg.app",
});
const file = await brigg.uploadFile("./Report.pdf", {
contentType: "application/pdf",
visibility: "workspace",
requestId: "report-2026-11-upload",
});
console.log(file.file_id);This streams the local file, handles transfers of any supported size, waits for scanning and returns the verified file metadata. You do not split files, store part receipts or handle temporary URLs. A Blob is also accepted; supply its filename in the options.
Keep requestId with your integration's job and reuse it with the same unchanged file and options after an interruption. The helper checks for an already completed transfer before sending bytes. A different file or an expired upload needs a new request ID. The returned file_id remains the same for that upload.
Pass an AbortSignal as signal to cancel a transfer or scan wait. Cancellation stops the client; use documents.CancelUpload if you also want to cancel the upload. Blocked scans, quota failures and other errors are returned to your code. BriggApiError includes code, status, details, the server's requestId and retryAfter when available.
The helper uses the operations below and the same permissions and limits. Bytes go directly to the transfer service, so a large document does not need to fit inside a JSON API request.
Upload with HTTP
The following steps are useful when using another language or implementing resumable and parallel transfers yourself.
Start an upload
Send the original filename, media type and exact byte length. A workspace-only call must choose workspace visibility. With member delegation, use personal to keep the resulting document in that member’s private files. Personal MCP connections may also choose personal.
curl 'https://api.brigg.app/v1/operations/documents.CreateUpload' \
-H "Authorization: Bearer $BRIGG_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agreement-upload-2026-11-10' \
--data '{"filename":"Agreement.pdf","content_type":"application/pdf","size_bytes":48231,"visibility":"workspace"}'This example's fictional PDF is 48,231 bytes. Use the size of your actual file. The response describes how to transfer it:
{
"upload_id": "upl_example",
"state": "uploading",
"expires_at": 1794387600000,
"mode": "single",
"part_size": 48231,
"part_count": 1
}An upload lasts 24 hours. Only the credential that created it may read its status, issue transfer URLs, complete it or cancel it. After completion, normal file and record permissions govern the file.
Transfer the bytes
Request a transfer URL for part 1:
curl 'https://api.brigg.app/v1/operations/documents.GetUploadPart' \
-H "Authorization: Bearer $BRIGG_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"upload_id":"upl_example","part_number":1}'The response includes url, method: "PUT", content_type, size_bytes and expires_at. Send the exact bytes to that URL with the returned media type. Do not send your Brigg API key to the transfer URL.
curl "$UPLOAD_URL" \
-X PUT \
-H 'Content-Type: application/pdf' \
--data-binary '@Agreement.pdf'A transfer URL is a temporary credential. Keep it out of logs and public pages. If it expires before the transfer finishes, request another URL for the same part and retry.
For mode: "multipart", split the file into part_size byte chunks. The final chunk contains the remainder. Request and upload parts 1 through part_count. Brigg collects their transfer receipts when you complete the upload. Parts may be transferred in parallel and an interrupted part can be retried independently.
Complete and scan
After all bytes have transferred, complete the upload with its ID. The same request works for single and multipart uploads:
curl 'https://api.brigg.app/v1/operations/documents.CompleteUpload' \
-H "Authorization: Bearer $BRIGG_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agreement-complete-2026-11-10' \
--data '{"upload_id":"upl_example"}'Existing clients may still send the optional parts array with part numbers and ETags. New integrations can omit it. An upload_incomplete error means one or more parts have not arrived with their expected byte lengths.
Brigg checks the byte length, file type and malware scan before creating the file. A refusal whose details.error is file_scan_pending means scanning is still running: wait the retry_after seconds in the error details, then retry completion with the same input and idempotency key. A blocked file cannot be completed.
Successful completion returns:
{
"file_id": "f_example",
"filename": "Agreement.pdf",
"content_type": "application/pdf",
"size_bytes": 48231,
"visibility": "workspace",
"scan_status": "clean"
}Repeating completion returns the same file. Store file_id in your integration. You can read the upload's state through documents.GetUpload or cancel an unfinished upload through documents.CancelUpload.
Attach or download the file
Attach document accepts the completed file_id and the target record's entry_id. The key needs the attachment operation and access to the target resource type. Attaching a document follows Brigg's existing visibility rules; it does not grant arbitrary access to a private file.
Use Get file for current metadata and Get file download for a temporary download URL. Each request checks current access. The download points to the verified version of the file, so an old upload URL cannot replace the bytes behind that download.
The server helper can obtain and use a fresh download URL in one call:
import { createWriteStream } from "node:fs";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
const response = await brigg.downloadFile(file.file_id);
await pipeline(Readable.fromWeb(response.body), createWriteStream("./Downloaded.pdf", { flags: "wx" }));This streams the download without buffering the whole file in memory. It checks current access each time and never sends the Brigg API key to the file-transfer service.
Next, send a PDF for signature or import contacts.
See the files and uploads reference for all file operations.
One document identity
Every document has a stable file_id, whether it is an uploaded file, collaborative text or a generated document. Find it through List documents or Search documents. Get document returns its kind and supported reads and exports for the acting member.
| Document kind | Bring it into Brigg | Get its content out |
|---|---|---|
file |
Upload original bytes using the flow above. | Download the original bytes with GetFileDownload, or ExportDocument with format: "original". |
text_document |
CreateTextDocument accepts Markdown. ConvertDocumentToTextDocument can copy a plain-text upload or a generated document's prose into a new text document. | ReadTextDocument returns current Markdown. ExportDocument renders Markdown, PDF or Word. |
generated_document |
Created in Brigg from a template or a document workflow. | ExportDocument renders PDF or Word from the selected saved state. |
Uploading a PDF or Word file preserves that file; it does not make the content collaboratively editable. Conversion to a text document supports plain text and generated prose, not PDF or Word import. Conversion creates a separate document and leaves the original unchanged; styling and layout are not copied into Markdown.
GetDocument and ExportDocument require member delegation. Use the same approved member when inspecting, exporting and downloading private content. Workspace credentials can still upload and download shared original files without delegation.
Export and download
Inspect capabilities.export.formats before choosing a format. The public API uses the same file_id throughout; internal bundle and storage identifiers are unnecessary.
const brigg = createBriggClient({
apiKey: process.env.BRIGG_API_KEY!,
baseUrl: "https://api.brigg.app",
actAs: process.env.BRIGG_ACT_AS!,
});
const exported = await brigg.call("documents.ExportDocument", {
file_id: "f_project_notes",
format: "pdf",
}, { requestId: "project-notes-export-2026-11-10" });
const response = await brigg.downloadFile(exported.file_id);
await pipeline(Readable.fromWeb(response.body), createWriteStream("./Project-notes.pdf", { flags: "wx" }));Use the stream imports shown in the download example above. exported.source.file_id identifies the unchanged source; exported.file_id identifies the downloadable result. A rendered export is a new private file owned by the acting member and is not automatically filed into Documents. Exporting original returns the original file ID and makes no copy.
The default revision is saved. For generated documents this excludes private drafts; if no saved state exists, you receive no_saved_version. Set revision: "current" explicitly to include the acting member's own draft. For collaborative text, both selections capture live content and fail if that content cannot be flushed. To export an exact historical state, pass an accessible version_id as revision; Brigg never silently substitutes another version.
The result's source.revision is an opaque content token; source.version_id, when present, is a historical version you can select later. They serve different purposes. Retrying with the same idempotency key returns the same export; a new snapshot needs a new key.
Edit text without losing changes
Read text document returns current Markdown and a revision. Send that token as expected_revision with Edit text document or Replace text document. The comparison and edit happen atomically against live content. If someone edits first, you receive 409 revision_conflict and your change is not applied.
Re-read, reconcile your edit, then submit a new command with a new idempotency key. Omitting expected_revision deliberately edits the latest content; a full replacement can then overwrite intervening changes. A revision token is not a history version ID.
Private files
To upload into a member's private files, use a credential with member delegation, set the helper's actAs to their account ID, and choose visibility: "personal". Keep that identity for all calls in the upload. A different member or credential cannot take over an open upload. Workspace uploads continue to use visibility: "workspace".