The quick answer
For a custom RESTlet, load the existing PDF with N/file and return its base64 content in a JSON object. The client parses JSON, decodes the base64 and writes binary bytes. Saving the complete JSON response with a .pdf extension does not create a PDF.
Current Oracle documentation also describes GET /services/rest/document/v1/file/{id}/content for raw file content where available and authorized. Check that standard service before choosing a custom endpoint. This guide teaches the RESTlet JSON contract; it does not verify the standard document endpoint in an account. Oracle: REST document service
The walkthrough executes a local RESTlet with N/file mocked. It retrieves a real teaching PDF that already exists locally. The client creates actual files, parses the decoded PDF, renders it and checks downloaded bytes. No NetSuite request, deployment, credential setup or account file change occurs. The PDF layout is a local example, not a NetSuite print template.
Before you start
Keep the two identities separate:
- Invoice 78001, number INV78001, belongs to Harbor Equipment Ltd. and totals 420.00 USD.
- File 920, named invoice_INV78001.pdf, is the existing PDF in Invoices, folder 80. It contains 2,205 bytes and one page.
The fixture supplies their relationship manually. A production integration needs a verified stored association or lookup. An invoice ID does not automatically identify a File Cabinet file.
In an account, verify an active deployment, the account-specific HTTPS URL, authorization, deployment audience and the role’s access to the requested file and folder. Keep credentials in the integration backend. The shown numeric-ID check is not business authorization: a production endpoint must also restrict files to the caller’s allowed transaction or business context before loading them. This local example does not implement that access policy. Each independent customer account needs its own installation, account-specific deployment URL and authorization. Do not expose invoice documents publicly to avoid that setup.
01. Identify the existing invoice
Start from the local invoice INV78001. Confirm the customer, Open status and 420.00 USD total. The PDF already exists; this workflow does not call render.transaction or generate a new printed transaction.
02. Identify the existing PDF file
Search the local File Cabinet for invoice_INV78001.pdf. The result has internal ID 920, type PDF File and displayed size 2.15 KB. That display rounds the actual 2,205-byte file size. Its folder is Invoices; folder ID 80 is also separate from file ID 920.
03. Expose the JSON-versus-PDF mistake
The checked-in invoice_pdf.js RESTlet validates a positive numeric file ID, loads the file and requires file.Type.PDF. It rejects files at or above 6 MiB, an example endpoint policy below the documented 10 MB getContents memory limit. Base64 adds roughly one third to content size; response and deployment limits can require a smaller policy. Oracle: File.getContents
For this JSON endpoint, return a JavaScript object with fileId, name, fileType, encoding and content. Binary file content uses base64. Here, fileType is a custom JSON property, not an HTTP application/pdf header. The endpoint response is JSON. Let the RESTlet serialize the object; do not double-stringify it. Oracle: file.File, Oracle: RESTlet get
return {
fileId: String(pdf.id),
name: pdf.name,
fileType: 'PDF',
encoding: 'base64',
content: pdf.getContents()
};
The local harness executes that source against its N/file mock and saves a real JSON response. The deliberate mistake saves the complete response as wrong.pdf. Its first bytes are JSON, so the initial %PDF- signature check fails. The extension did not change the content.
04. Decode and verify the actual PDF
The corrected local client parses the JSON and validates the expected file ID, PDF type, base64 encoding and known filename. It checks base64 syntax before decoding. Strict canonical base64 is this client’s contract, rather than a universal NetSuite requirement. The accepted filename is fixed for this fixture; do not write an arbitrary returned filename as an unchecked filesystem path.
After those checks, the essential conversion is:
const envelope = JSON.parse(body);
const bytes = Buffer.from(
envelope.content, 'base64'
);
fs.writeFileSync(targetPath, bytes);
The decoded file contains 2,205 bytes. Its SHA-256 matches the known original fixture. A PDF parser reads one page and verifies INV78001 and 420.00 in its text. The viewer rendering is generated from this decoded file.
A %PDF- prefix alone is insufficient. Parser success, expected document content and a visible rendering provide stronger checks. A matching hash proves equality only when the expected hash comes from a trusted reference; the fixture supplies that reference here.
05. Inspect and download the document
Open the actual decoded PDF rendering. Confirm INV78001, Harbor Equipment Ltd., three Inspection Services at 140.00 and a 420.00 USD total. The full page includes an explicit LOCAL TEACHING FIXTURE footer.
Download the PDF. The capture driver compares those downloaded bytes exactly with the original local file. Invoice identity, displayed content and binary equality agree; no account document was downloaded.
Common questions
Can I use the invoice internal ID as fileId?
They identify different objects. This example uses invoice 78001 and file 920. Establish the relationship explicitly rather than assuming the numbers match.
Why does a response saved as .pdf fail to open?
For this contract, the response body is JSON containing base64. Parse JSON and decode content before writing bytes. Do not save the entire serialized file object or envelope as a PDF.
Should I JSON.stringify the returned RESTlet object?
For this JSON endpoint, return the object. SuiteScript 2.1 return-type behavior can otherwise leave a client with an extra serialization layer. Match the client to the endpoint's actual content contract. Oracle: RESTlet return types
Does this generate a PDF from an invoice?
It retrieves an existing file. Generating a transaction PDF is a separate workflow with its own form, template and authorization behavior. The illustrated PDF was created locally as a teaching fixture.
Can the standard REST service return raw file bytes?
Current Oracle documentation describes the document-service file-content route linked above. Availability and access must be verified in the target account. It is a different transport contract from this custom JSON/base64 RESTlet; do not base64-decode a raw binary response merely because this guide does so.
Will one deployment URL work for independent customer accounts?
Each account needs its own installed script/deployment, URL and authorization setup. The original author's follow-up distinguishes separate client accounts from multiple users in one account.
Does an attachment prove it is the invoice’s generated PDF?
A File Cabinet attachment may be a separate document. An account feature, script or custom field can establish an explicit reference, but the relationship and document purpose must be verified. The fixture supplies both identities manually.
Does the local test prove account access?
It proves the local contract and fixture bytes. It does not verify a deployed RESTlet, authentication, folder access or the standard REST route in any account. Production authorization for the requested file needs its own implementation and verification.
Can this approach transfer any PDF size?
This example rejects files at or above 6 MiB. getContents has a documented 10 MB memory limit, and base64 plus JSON increases response size. Verify all applicable platform and client limits; use a suitable transfer design for larger files.