Image drop boxes
A drop box turns photos and PDFs into image URLs hosted by SuiteOp under your organization, which any imageUrl field in the API accepts. A person drags files into an upload link, or your code uploads a file directly, and SuiteOp processes each one. It is the way in for images you have as files rather than as SuiteOp URLs, including images inside a PDF. A PDF is also kept as a private document whose text you can read, and which you can attach to an element as a manual, warranty or receipt. This guide covers the four drop box operations. Every field is listed on the operation’s page in the API Reference.
| Operation | Request | Permission |
|---|---|---|
createImageDropBox | POST /image-drop-boxes | modify_properties |
createDropBoxUpload | POST /image-drop-boxes/{dropBoxId}/uploads | modify_properties |
listDropBoxImages | GET /image-drop-boxes/{dropBoxId}/images | view_properties |
getDropBoxDocumentText | GET /image-drop-box-files/{fileId}/text | view_properties |
Paths are relative to your region’s base URL, for example https://api-us.suiteop.com/api/v1. See Authentication for permissions.
Opening a drop box
Section titled “Opening a drop box”POST /image-drop-boxes takes an empty body and answers 201:
curl -X POST https://api-us.suiteop.com/api/v1/image-drop-boxes \ -H "Authorization: Bearer sk_live_your_key_here" \ -H "Content-Type: application/json" \ -d '{}'{ "data": { "dropBoxId": "3f8a1c6e-2b9d-4e7a-8c5f-0d1e4b7a9c32", "uploadPageUrl": "https://…/public/drop/eyJ…", "expiresAt": "2026-10-12T14:30:00.000Z", "maxFiles": 100, "maxImageBytes": 26214400, "maxPdfBytes": 52428800 }, "meta": { "requestId": "…" }}uploadPageUrlis a page anyone holding the link can upload to for 24 hours, without signing in. Send it only to the person who has the files, and treat it like a password.- A box holds up to 100 uploaded files. Images pulled out of a PDF don’t count toward that, and an upload that never arrived gives its place back.
- The box has no other state. There is nothing to close or delete; the link simply expires.
Uploading a file yourself
Section titled “Uploading a file yourself”When your code has the file, ask for a signed URL, one file per call:
curl -X POST "https://api-us.suiteop.com/api/v1/image-drop-boxes/3f8a1c6e-2b9d-4e7a-8c5f-0d1e4b7a9c32/uploads" \ -H "Authorization: Bearer sk_live_your_key_here" \ -H "Content-Type: application/json" \ -d '{"fileName": "lockbox.jpg", "contentType": "image/jpeg", "sizeBytes": 482113}'{ "data": { "fileId": "7b2e9d4a-1c6f-4a8e-9d3b-5f0c2e8a1d64", "uploadUrl": "https://…", "method": "PUT", "headers": { "content-type": "image/jpeg" }, "expiresInSeconds": 900 }, "meta": { "requestId": "…" }}Then send the bytes with every header the response lists:
curl -X PUT "$UPLOAD_URL" \ -H "content-type: image/jpeg" \ --data-binary @lockbox.jpgcontentTypeisimage/jpeg,image/png,image/webp,image/giforapplication/pdf. HEIC is not accepted: convert iPhone photos to JPEG first.sizeBytesmust be the file’s exact size. The signed URL is pinned to it, so a body of any other size is refused by storage. The cap is 25 MB for an image and 50 MB for a PDF; a larger size returns400 validation_error.fileNameis up to 255 characters and may not contain/,\or...- Send the
PUTwithin 15 minutes. There is no “done” call: processing starts on its own once the file lands. - A box has no stored record, so any
dropBoxIdis accepted, including one whose upload link has expired or one you never opened. Always use the IDcreateImageDropBoxreturned, and read the results with that same ID. A full box returns400 validation_errorwith the messagevalidation.image_drop_box.full.
Reading the results
Section titled “Reading the results”GET /image-drop-boxes/{dropBoxId}/images returns every file in the box as a plain array, not a paginated list:
{ "data": [ { "id": "7b2e9d4a-1c6f-4a8e-9d3b-5f0c2e8a1d64", "parentFileId": null, "kind": "upload", "status": "ready", "fileName": "lockbox.jpg", "contentType": "image/jpeg", "pageNumber": null, "imageUrl": "https://…/drop-box/3f8a1c6e-…/….jpg", "previewUrl": "https://…/drop-box/3f8a1c6e-…/…-preview.jpg", "width": 2560, "height": 1920, "failureCode": null, "createdAt": "2026-10-11T14:31:09.000Z", "documentUrl": null, "hasText": null } ], "meta": { "requestId": "…" }}- Order: oldest upload first. Each PDF (
kind: "upload") is followed by the images taken from it (kind: "extracted",parentFileIdset to the PDF,pageNumberfrom 1), in page order. statusispendingwhile processing, thenreadyorfailed. Poll until no file ispending.imageUrlis set on every ready image. Pass it to anyimageUrlfield. A ready PDF has noimageUrlof its own; use its extracted images.documentUrlis set only on a ready uploaded PDF: a signed link to the original PDF, valid for one hour. It is private, so never put it in animageUrlfield or in anything a guest sees. Read the list again for a fresh link. It can benullon a ready PDF if signing failed; the text andidstill work.hasTextistrueon a processed PDF when at least one page has a text layer,falsefor a scan, andnullon images and on a PDF whose text was never read.failureCodeon a failed file isnever_uploaded(noPUTarrived in time),unsupported_type,too_large,unreadableorprocessing_failed.no_images_in_pdfappears only on files processed before PDFs were kept as documents: a PDF with no pictures now endsreadywith its text.- A box ID from another organization, or one never used, returns an empty array rather than
404. - A box and its files are deleted 30 days after its newest upload. Images you already placed somewhere keep working; copy anything else you need to keep, such as a PDF, with
createElementAttachment.
Every image is decoded and re-encoded as JPEG, which also strips metadata such as GPS location. The stored image is at most 2560 pixels on its long edge, and previewUrl is a small 384-pixel version. The type you declared only picks how the file is processed: SuiteOp checks the bytes themselves.
From a PDF, SuiteOp reads the first 60 pages. It keeps up to 80 distinct images of at least 200 pixels on their longer edge, and up to 20,000 characters of text per page and 200,000 per document.
Reading a PDF’s text
Section titled “Reading a PDF’s text”GET /image-drop-box-files/{fileId}/text returns the text of one uploaded PDF, page by page. fileId is the id of the PDF’s own row in listDropBoxImages (kind: "upload", contentType: "application/pdf"):
curl "https://api-us.suiteop.com/api/v1/image-drop-box-files/9c4d2e7f-3a1b-4c8d-9e6f-1a2b3c4d5e6f/text" \ -H "Authorization: Bearer sk_live_your_key_here"{ "data": { "fileId": "9c4d2e7f-3a1b-4c8d-9e6f-1a2b3c4d5e6f", "fileName": "house-manual.pdf", "status": "ready", "pageCount": 14, "truncated": false, "pages": [ { "pageNumber": 1, "text": "Welcome to Seaside Cottage…" }, { "pageNumber": 2, "text": "Wi-Fi: SeasideGuest…" } ], "nextPage": 3 }, "meta": { "requestId": "…" }}- Paging: one call returns whole pages up to 40,000 characters in total, and always at least one page unless
fromPageis past the last page, which returns an emptypageslist. WhennextPageis notnull, call again with?fromPage=set to it (1 to 1000). pageCountis the number of pages SuiteOp kept text for, not the PDF’s own page count. It isnullwhile the PDF is stillpending, after itfailed, and for a PDF processed before text was read. In those casespagesis empty; checkstatus.truncatedistruewhen the PDF had more than 60 pages or the text limits cut a page short.- A scanned PDF comes back
readywith every page’stextempty, andhasText: falsein the list. SuiteOp doesn’t run OCR on it. - An image row, an extracted image, or a file of another organization returns
404 not_found_error.
To keep a PDF beyond the box’s 30 days, for example a warranty or a receipt, attach it to an element with createElementAttachment and its dropBoxFileId.